Intro elisp 4. A Few Buffer-Related Functions

In this chapter we study in detail several of the functions used in GNU Emacs. This is called a “walk-through”. These functions are used as examples of Lisp code, but are not imaginary examples; with the exception of the first, simplified function definition, these functions show the actual code used in GNU Emacs. You can learn a great deal from these definitions. The functions described here are all related to buffers. Later, we will study other functions.

4.1 Finding More Information

In this walk-through, I will describe each new function as we come to it, sometimes in detail and sometimes briefly. If you are interested, you can get the full documentation of any Emacs Lisp function at any time by typing C-h f and then the name of the function (and then ). Similarly, you can get the full documentation for a variable by typing C-h vand then the name of the variable (and then ).

Also, describe-function will tell you the location of the function definition.

Put point into the name of the file that contains the function and press the key. In this case, means push-button rather than “return” or “enter”. Emacs will take you directly to the function definition.

More generally, if you want to see a function in its original source file, you can use the xref-find-definitions function to jump to it. xref-find-definitions works with a wide variety of languages, not just Lisp, and C, and it works with non-programming text as well. For example, xref-find-definitions will jump to the various nodes in the Texinfo source file of this document (provided that you’ve run the etags utility to record all the nodes in the manuals that come with Emacs; see Create Tags Table).

To use the xref-find-definitions command, type M-. (i.e., press the period key while holding down the key, or else type the key and then type the period key), and then, at the prompt, type in the name of the function whose source code you want to see, such as mark-whole-buffer, and then type . (If the command doesn’t prompt, invoke it with an argument: C-u M-.; see Interactive Options.) Emacs will switch buffers and display the source code for the function on your screen10. To switch back to your current buffer, type M-, or C-x b . (On some keyboards, the key is labeled .)

Incidentally, the files that contain Lisp code are conventionally called libraries. The metaphor is derived from that of a specialized library, such as a law library or an engineering library, rather than a general library. Each library, or file, contains functions that relate to a particular topic or activity, such as abbrev.el for handling abbreviations and other typing shortcuts, and help.el for help. (Sometimes several libraries provide code for a single activity, as the various rmail… files provide code for reading electronic mail.) In The GNU Emacs Manual, you will see sentences such as “The C-h p command lets you search the standard Emacs Lisp libraries by topic keywords.”

4.2 A Simplified beginning-of-buffer Definition

The beginning-of-buffer command is a good function to start with since you are likely to be familiar with it and it is easy to understand. Used as an interactive command, beginning-of-buffer moves the cursor to the beginning of the buffer, leaving the mark at the previous position. It is generally bound to M-<.

In this section, we will discuss a shortened version of the function that shows how it is most frequently used. This shortened function works as written, but it does not contain the code for a complex option. In another section, we will describe the entire function. (See Complete Definition of beginning-of-buffer.)

Before looking at the code, let’s consider what the function definition has to contain: it must include an expression that makes the function interactive so it can be called by typing M-x beginning-of-buffer or by typing a keychord such as M-<; it must include code to leave a mark at the original position in the buffer; and it must include code to move the cursor to the beginning of the buffer.

Here is the complete text of the shortened version of the function:

1
2
3
4
5
6
(defun simplified-beginning-of-buffer ()
"Move point to the beginning of the buffer;
leave mark at previous position."
(interactive)
(push-mark)
(goto-char (point-min)))

Like all function definitions, this definition has five parts following the macro defun:

  1. The name: in this example, simplified-beginning-of-buffer.
  2. A list of the arguments: in this example, an empty list, (),
  3. The documentation string.
  4. The interactive expression.
  5. The body.

In this function definition, the argument list is empty; this means that this function does not require any arguments. (When we look at the definition for the complete function, we will see that it may be passed an optional argument.)

The interactive expression tells Emacs that the function is intended to be used interactively. In this example, interactive does not have an argument because simplified-beginning-of-buffer does not require one.

The body of the function consists of the two lines:

1
2
(push-mark)
(goto-char (point-min))

The first of these lines is the expression, (push-mark). When this expression is evaluated by the Lisp interpreter, it sets a mark at the current position of the cursor, wherever that may be. The position of this mark is saved in the mark ring.

The next line is (goto-char (point-min)). This expression jumps the cursor to the minimum point in the buffer, that is, to the beginning of the buffer (or to the beginning of the accessible portion of the buffer if it is narrowed. See Narrowing and Widening.)

The push-mark command sets a mark at the place where the cursor was located before it was moved to the beginning of the buffer by the (goto-char (point-min)) expression. Consequently, you can, if you wish, go back to where you were originally by typing C-x C-x.

That is all there is to the function definition!

When you are reading code such as this and come upon an unfamiliar function, such as goto-char, you can find out what it does by using the describe-function command. To use this command, type C-h f and then type in the name of the function and press . The describe-function command will print the function’s documentation string in a Help window. For example, the documentation for goto-char is:

1
2
Set point to POSITION, a number or marker.
Beginning of buffer is position (point-min), end is (point-max).

The function’s one argument is the desired position.

(The prompt for describe-function will offer you the symbol under or preceding the cursor, so you can save typing by positioning the cursor right over or after the function and then typing C-h f .)

The end-of-buffer function definition is written in the same way as the beginning-of-buffer definition except that the body of the function contains the expression (goto-char (point-max)) in place of (goto-char (point-min)).

4.3 The Definition of mark-whole-buffer

The mark-whole-buffer function is no harder to understand than the simplified-beginning-of-buffer function. In this case, however, we will look at the complete function, not a shortened version.

The mark-whole-buffer function is not as commonly used as the beginning-of-buffer function, but is useful nonetheless: it marks a whole buffer as a region by putting point at the beginning and a mark at the end of the buffer. It is generally bound to C-x h.

An overview of mark-whole-buffer

In GNU Emacs 22, the code for the complete function looks like this:

1
2
3
4
5
6
7
8
9
(defun mark-whole-buffer ()
"Put point at beginning and mark at end of buffer.
You probably should not use this function in Lisp programs;
it is usually a mistake for a Lisp function to use any subroutine
that uses or sets the mark."
(interactive)
(push-mark (point))
(push-mark (point-max) nil t)
(goto-char (point-min)))

Like all other functions, the mark-whole-buffer function fits into the template for a function definition. The template looks like this:

1
2
3
4
(defun name-of-function (argument-list)
"documentation..."
(interactive-expression...)
body...)

Here is how the function works: the name of the function is mark-whole-buffer; it is followed by an empty argument list, ‘()’, which means that the function does not require arguments. The documentation comes next.

The next line is an (interactive) expression that tells Emacs that the function will be used interactively. These details are similar to the simplified-beginning-of-bufferfunction described in the previous section.

4.3.1 Body of mark-whole-buffer

The body of the mark-whole-buffer function consists of three lines of code:

1
2
3
(push-mark (point))
(push-mark (point-max) nil t)
(goto-char (point-min))

The first of these lines is the expression, (push-mark (point)).

This line does exactly the same job as the first line of the body of the simplified-beginning-of-buffer function, which is written (push-mark). In both cases, the Lisp interpreter sets a mark at the current position of the cursor.

I don’t know why the expression in mark-whole-buffer is written (push-mark (point)) and the expression in beginning-of-buffer is written (push-mark). Perhaps whoever wrote the code did not know that the arguments for push-mark are optional and that if push-mark is not passed an argument, the function automatically sets mark at the location of point by default. Or perhaps the expression was written so as to parallel the structure of the next line. In any case, the line causes Emacs to determine the position of point and set a mark there.

In earlier versions of GNU Emacs, the next line of mark-whole-buffer was (push-mark (point-max)). This expression sets a mark at the point in the buffer that has the highest number. This will be the end of the buffer (or, if the buffer is narrowed, the end of the accessible portion of the buffer. See Narrowing and Widening, for more about narrowing.) After this mark has been set, the previous mark, the one set at point, is no longer set, but Emacs remembers its position, just as all other recent marks are always remembered. This means that you can, if you wish, go back to that position by typing C-u C- twice.

In GNU Emacs 22, the (point-max) is slightly more complicated. The line reads

1
(push-mark (point-max) nil t)

The expression works nearly the same as before. It sets a mark at the highest numbered place in the buffer that it can. However, in this version, push-mark has two additional arguments. The second argument to push-mark is nil. This tells the function it should display a message that says “Mark set” when it pushes the mark. The third argument is t. This tells push-mark to activate the mark when Transient Mark mode is turned on. Transient Mark mode highlights the currently active region. It is often turned off.

Finally, the last line of the function is (goto-char (point-min))). This is written exactly the same way as it is written in beginning-of-buffer. The expression moves the cursor to the minimum point in the buffer, that is, to the beginning of the buffer (or to the beginning of the accessible portion of the buffer). As a result of this, point is placed at the beginning of the buffer and mark is set at the end of the buffer. The whole buffer is, therefore, the region.

4.4 The Definition of append-to-buffer

The append-to-buffer command is more complex than the mark-whole-buffer command. What it does is copy the region (that is, the part of the buffer between point and mark) from the current buffer to a specified buffer.

An Overview of append-to-buffer

The append-to-buffer command uses the insert-buffer-substring function to copy the region. insert-buffer-substring is described by its name: it takes a substring from a buffer, and inserts it into another buffer.

Most of append-to-buffer is concerned with setting up the conditions for insert-buffer-substring to work: the code must specify both the buffer to which the text will go, the window it comes from and goes to, and the region that will be copied.

Here is the complete text of the function:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
(defun append-to-buffer (buffer start end)
"Append to specified buffer the text of the region.
It is inserted into that buffer before its point.

When calling from a program, give three arguments:
BUFFER (or buffer name), START and END.
START and END specify the portion of the current buffer to be copied."
(interactive
(list (read-buffer "Append to buffer: " (other-buffer
(current-buffer) t))
(region-beginning) (region-end)))
(let ((oldbuf (current-buffer)))
(save-excursion
(let* ((append-to (get-buffer-create buffer))
(windows (get-buffer-window-list append-to t t))
point)
(set-buffer append-to)
(setq point (point))
(barf-if-buffer-read-only)
(insert-buffer-substring oldbuf start end)
(dolist (window windows)
(when (= (window-point window) point)
(set-window-point window (point))))))))

The function can be understood by looking at it as a series of filled-in templates.

The outermost template is for the function definition. In this function, it looks like this (with several slots filled in):

1
2
3
4
(defun append-to-buffer (buffer start end)
"documentation..."
(interactive ...)
body...)

The first line of the function includes its name and three arguments. The arguments are the buffer to which the text will be copied, and the start and end of the region in the current buffer that will be copied.

The next part of the function is the documentation, which is clear and complete. As is conventional, the three arguments are written in upper case so you will notice them easily. Even better, they are described in the same order as in the argument list.

Note that the documentation distinguishes between a buffer and its name. (The function can handle either.)

4.4.1 The append-to-buffer Interactive Expression

Since the append-to-buffer function will be used interactively, the function must have an interactive expression. (For a review of interactive, see Making a Function Interactive.) The expression reads as follows:

1
2
3
4
5
6
(interactive
(list (read-buffer
"Append to buffer: "
(other-buffer (current-buffer) t))
(region-beginning)
(region-end)))

This expression is not one with letters standing for parts, as described earlier. Instead, it starts a list with these parts:

The first part of the list is an expression to read the name of a buffer and return it as a string. That is read-buffer. The function requires a prompt as its first argument, ‘“Append to buffer: “’. Its second argument tells the command what value to provide if you don’t specify anything.

In this case that second argument is an expression containing the function other-buffer, an exception, and a ‘t’, standing for true.

The first argument to other-buffer, the exception, is yet another function, current-buffer. That is not going to be returned. The second argument is the symbol for true, t. that tells other-buffer that it may show visible buffers (except in this case, it will not show the current buffer, which makes sense).

The expression looks like this:

1
(other-buffer (current-buffer) t)

The second and third arguments to the list expression are (region-beginning) and (region-end). These two functions specify the beginning and end of the text to be appended.

Originally, the command used the letters ‘B’ and ‘r’. The whole interactive expression looked like this:

1
(interactive "BAppend to buffer: \nr")

But when that was done, the default value of the buffer switched to was invisible. That was not wanted.

(The prompt was separated from the second argument with a newline, ‘\n’. It was followed by an ‘r’ that told Emacs to bind the two arguments that follow the symbol buffer in the function’s argument list (that is, start and end) to the values of point and mark. That argument worked fine.)

4.4.2 The Body of append-to-buffer

The body of the append-to-buffer function begins with let.

As we have seen before (see let), the purpose of a let expression is to create and give initial values to one or more variables that will only be used within the body of the let. This means that such a variable will not be confused with any variable of the same name outside the let expression.

We can see how the let expression fits into the function as a whole by showing a template for append-to-buffer with the let expression in outline:

1
2
3
4
5
(defun append-to-buffer (buffer start end)
"documentation..."
(interactive ...)
(let ((variable value))
body...)

The let expression has three elements:

  1. The symbol let;
  2. A varlist containing, in this case, a single two-element list, (variable value);
  3. The body of the let expression.

In the append-to-buffer function, the varlist looks like this:

1
(oldbuf (current-buffer))

In this part of the let expression, the one variable, oldbuf, is bound to the value returned by the (current-buffer) expression. The variable, oldbuf, is used to keep track of the buffer in which you are working and from which you will copy.

The element or elements of a varlist are surrounded by a set of parentheses so the Lisp interpreter can distinguish the varlist from the body of the let. As a consequence, the two-element list within the varlist is surrounded by a circumscribing set of parentheses. The line looks like this:

1
2
(let ((oldbuf (current-buffer)))
... )

The two parentheses before oldbuf might surprise you if you did not realize that the first parenthesis before oldbuf marks the boundary of the varlist and the second parenthesis marks the beginning of the two-element list, (oldbuf (current-buffer)).

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:

1
2
3
4
5
6
(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 thesave-excursion itself is enclosed by the parentheses associated with the let:

1
2
3
4
5
6
(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:

1
2
3
4
5
(save-excursion
first-expression-in-body
second-expression-in-body
...
last-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 has a ‘*’ in its name. It enables Emacs to set each variable in its varlist in sequence, one after another.

Its critical feature is that variables later in the varlist can make use of the values to which Emacs set variables earlier in the varlist. See The let* expression.

We will skip functions like let* and focus on two: the set-buffer function and the insert-buffer-substring function.

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

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

but now it is

1
(set-buffer append-to)

append-to is bound to (get-buffer-create buffer) earlier on in the let* expression. That extra binding would not be necessary except for that append-to is used later in the varlist as an argument to get-buffer-window-list.

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:

1
(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 toinsert-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:

1
2
3
4
5
6
7
(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.

4.5 Review

Here is a brief summary of the various functions discussed in this chapter.

  • describe-function

  • describe-variable

    Print the documentation for a function or variable. Conventionally bound to C-h f and C-h v.

  • xref-find-definitions

    Find the file containing the source for a function or variable and switch buffers to it, positioning point at the beginning of the item. Conventionally bound to M-. (that’s a period following the key).

  • save-excursion

    Save the location of point and restore its value after the arguments to save-excursion have been evaluated. Also, remember the current buffer and return to it.

  • push-mark

    Set mark at a location and record the value of the previous mark on the mark ring. The mark is a location in the buffer that will keep its relative position even if text is added to or removed from the buffer.

  • goto-char

    Set point to the location specified by the value of the argument, which can be a number, a marker, or an expression that returns the number of a position, such as (point-min).

  • insert-buffer-substring

    Copy a region of text from a buffer that is passed to the function as an argument and insert the region into the current buffer.

  • mark-whole-buffer

    Mark the whole buffer as a region. Normally bound to C-x h.

  • set-buffer

    Switch the attention of Emacs to another buffer, but do not change the window being displayed. Used when the program rather than a human is to work on a different buffer.

  • get-buffer-create

  • get-buffer

    Find a named buffer or create one if a buffer of that name does not exist. The get-buffer function returns nil if the named buffer does not exist.

4.6 Exercises

  • Write your own simplified-end-of-buffer function definition; then test it to see whether it works.
  • Use if and get-buffer to write a function that prints a message telling you whether a buffer exists.
  • Using xref-find-definitions, find the source for the copy-to-buffer function.