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

Handling the Kill Ring

The kill ring is a list that is transformed into a ring by the workings of the rotate-yank-pointer function. The yank and yank-pop commands use the rotate-yank-pointer function. This appendix describes the rotate-yank-pointer function as well as both the yank and the yank-pop commands.

The rotate-yank-pointer Function

The rotate-yank-pointer function changes the element in the kill ring to which kill-ring-yank-pointer points. For example, it can change kill-ring-yank-pointer from pointing to the second element to point to the third element.

Here is the code for rotate-yank-pointer:

(defun rotate-yank-pointer (arg)
  "Rotate the yanking point in the kill ring."
  (interactive "p")
  (let ((length (length kill-ring)))

    (if (zerop length)

        ;; then-part
        (error "Kill ring is empty")

      ;; else-part
      (setq kill-ring-yank-pointer
            (nthcdr (% (+ arg
                          (- length
                             (length
                              kill-ring-yank-pointer)))
                       length)
                    kill-ring)))))

The function looks complex, but as usual, it can be understood by taking it apart piece by piece. First look at it in skeletal form:

(defun rotate-yank-pointer (arg)
  "Rotate the yanking point in the kill ring."
  (interactive "p")
  (let varlist
    body...)

This function takes one argument, called arg. It has a brief documentation string; and it is interactive with a small `p', which means that the argument must be a processed prefix passed to the function as a number.

The body of the function definition is a let expression, which itself has a body as well as a varlist.

The let expression declares a variable that will be only usable within the bounds of this function. This variable is called length and is bound to a value that is equal to the number of items in the kill ring. This is done by using the function called length. (Note that this function has the same name as the variable called length; but one use of the word is to name the function and the other is to name the variable. The two are quite distinct. Similarly, an English speaker will distinguish between the meanings of the word `ship' when he says: "I must ship this package immediately." and "I must get aboard the ship immediately.")

The function length tells the number of items there are in a list, so (length kill-ring) returns the number of items there are in the kill ring.

The Body of rotate-yank-pointer

The body of rotate-yank-pointer is a let expression and the body of the let expression is an if expression.

The purpose of the if expression is to find out whether there is anything in the kill ring. If the kill ring is empty, the error function stops evaluation of the function and prints a message in the echo area. On the other hand, if the kill ring has something in it, the work of the function is done.

Here is the if-part and then-part of the if expression:

(if (zerop length)                      ; if-part
    (error "Kill ring is empty")        ; then-part
  ...

If there is not anything in the kill ring, its length must be zero and an error message sent to the user: `Kill ring is empty'. The if expression uses the function zerop which returns true if the value it is testing is zero. When zerop tests true, the then-part of the if is evaluated. The then-part is a list starting with the function error, which is a function that is similar to the message function (see section The message Function), in that it prints a one-line message in the echo area. However, in addition to printing a message, error also stops evaluation of the function within which it is embedded. In this case, this means that the rest of the function will not be evaluated if the length of the kill ring is zero.

(In my opinion, it is slightly misleading, at least to humans, to use the term `error' as the name of this function. A better term would be `cancel'. Strictly speaking, of course, you cannot point to, much less rotate a pointer to a list that has no length, so from the point of view of the computer, the word `error' is correct. But a human expects to attempt this sort of thing, if only to find out whether the kill ring is full or empty. This is an act of exploration.

(From the human point of view, the act of exploration and discovery is not necessarily an error, and therefore should not be labeled as one, even in the bowels of a computer. As it is, the code in Emacs implies that a human who is acting virtuously, by exploring his or her environment, is making an error. This is bad. Even though the computer takes the same steps as it does when there is an `error', a term such as `cancel' would have a clearer connotation.)

The else-part of the if expression

The else-part of the if expression is dedicated to setting the value of kill-ring-yank-pointer when the kill ring has something in it. The code looks like this:

(setq kill-ring-yank-pointer
      (nthcdr (% (+ arg
                    (- length
                       (length kill-ring-yank-pointer)))
                 length)
              kill-ring)))))

This needs some examination. Clearly, kill-ring-yank-pointer is being set to be equal to some CDR of the kill ring, using the nthcdr function that is described in an earlier section. (See section copy-region-as-kill.) But exactly how does it do this?

Before looking at the details of the code let's first consider the purpose of the rotate-yank-pointer function.

The rotate-yank-pointer function changes what kill-ring-yank-pointer points to. If kill-ring-yank-pointer starts by pointing to the first element of a list, a call to rotate-yank-pointer causes it to point to the second element; and if kill-ring-yank-pointer points to the second element, a call to rotate-yank-pointer causes it to point to the third element. (And if rotate-yank-pointer is given an argument greater than 1, it jumps the pointer that many elements.)

The rotate-yank-pointer function uses setq to reset what the kill-ring-yank-pointer points to. If kill-ring-yank-pointer points to the first element of the kill ring, then, in the simplest case, the rotate-yank-pointer function must cause it to point to the second element. Put another way, kill-ring-yank-pointer must be reset to have a value equal to the CDR of the kill ring.

That is, under these circumstances,

(setq kill-ring-yank-pointer
   ("some text" "a different piece of text" "yet more text"))

(setq kill-ring
   ("some text" "a different piece of text" "yet more text"))

the code should do this:

(setq kill-ring-yank-pointer (cdr kill-ring))

As a result, the kill-ring-yank-pointer will look like this:

kill-ring-yank-pointer
     => ("a different piece of text" "yet more text"))

The actual setq expression uses the nthcdr function to do the job.

As we have seen before (see section nthcdr), the nthcdr function works by repeatedly taking the CDR of a list--it takes the CDR of the CDR of the CDR ...

The two following expressions produce the same result:

(setq kill-ring-yank-pointer (cdr kill-ring))

(setq kill-ring-yank-pointer (nthcdr 1 kill-ring))

In the rotate-yank-pointer function, however, the first argument to nthcdr is a rather complex looking expression with lots of arithmetic inside of it:

(% (+ arg
      (- length
         (length kill-ring-yank-pointer)))
   length)

As usual, we need to look at the most deeply embedded expression first and then work our way towards the light.

The most deeply embedded expression is (length kill-ring-yank-pointer). This finds the length of the current value of the kill-ring-yank-pointer. (Remember that the kill-ring-yank-pointer is the name of a variable whose value is a list.)

The measurement of the length is inside the expression:

(- length (length kill-ring-yank-pointer))

In this expression, the first length is the variable that was assigned the length of the kill ring in the let statement at the beginning of the function. (One might think this function would be clearer if the variable length were named length-of-kill-ring instead; but if you look at the text of the whole function, you will see that it is so short that naming this variable length is not a bother, unless you are pulling the function apart into very tiny pieces as we are doing here.)

So the line (- length (length kill-ring-yank-pointer)) tells the difference between the length of the kill ring and the length of the list whose name is kill-ring-yank-pointer.

To see how all this fits into the rotate-yank-pointer function, let's begin by analyzing the case where kill-ring-yank-pointer points to the first element of the kill ring, just as kill-ring does, and see what happens when rotate-yank-pointer is called with an argument of 1.

In this case, the variable length and the value of the expression (length kill-ring-yank-pointer will be the same since the variable length is the length of the kill ring and the kill-ring-yank-pointer is pointing to the whole kill ring. Consequently, the value of

(- length (length kill-ring-yank-pointer))

will be zero. Since the value of arg will be 1, this will mean that the value of the whole expression

(+ arg (- length (length kill-ring-yank-pointer)))

will be 1.

Consequently, the argument to nthcdr will be found as the result of the expression

(% 1 length)

The % remainder function

To understand (% 1 length), we need to understand %. According to its documentation (which I just found by typing C-h f % RET), the % function returns the remainder of its first argument divided by its second argument. For example, the remainder of 5 divided by 2 is 1. (2 goes into 5 twice with a remainder of 1.)

What surprises people who don't often do arithmetic is that a smaller number can be divided by a larger number and have a remainder. In the example we just used, 5 was divided by 2. We can reverse that and ask, what is the result of dividing 2 by 5? If you can use fractions, the answer is obviously 2/5 or .4; but if, as here, you can only use whole numbers, the result has to be something different. Clearly, 5 can go into 2 zero times, but what of the remainder? To see what the answer is, consider a case that has to be familiar from childhood:

By considering the cases as parallel, we can see that

and so on.

So, in this code, if the value of length is 5, then the result of evaluating

(% 1 5)

is 1. (I just checked this by placing the cursor after the expression and typing C-x C-e. Indeed, 1 is printed in the echo area.)

Using % in rotate-yank-pointer

When the kill-ring-yank-pointer points to the beginning of the kill ring, and the argument passed to rotate-yank-pointer is 1, the % expression returns 1:

(- length (length kill-ring-yank-pointer))
     => 0

therefore,

(+ arg (- length (length kill-ring-yank-pointer)))
     => 1

and consequently:

(% (+ arg (- length (length kill-ring-yank-pointer)))
   length)
     => 1

regardless of the value of length.

As a result of this, the setq kill-ring-yank-pointer expression simplifies to:

(setq kill-ring-yank-pointer (nthcdr 1 kill-ring))

What it does is now easy to understand. Instead of pointing as it did to the first element of the kill ring, the kill-ring-yank-pointer is set to point to the second element.

Clearly, if the argument passed to rotate-yank-pointer is two, then the kill-ring-yank-pointer is set to (nthcdr 2 kill-ring); and so on for different values of the argument.

Similarly, if the kill-ring-yank-pointer starts out pointing to the second element of the kill ring, it length is shorter than the length of the kill ring by 1, so the computation of the remainder is based on the expression (% (+ arg 1) length). This means that the kill-ring-yank-pointer is moved from the second element of the kill ring to the third element if the argument passed to rotate-yank-pointer is 1.

Pointing to the last element

The final question is, what happens if the kill-ring-yank-pointer is set to the last element of the kill ring? Will a call to rotate-yank-pointer mean that nothing more can be taken from the kill ring? The answer is no. What happens is different and useful. The kill-ring-yank-pointer is set to point to the beginning of the kill ring instead.

Let's see how this works by looking at the code, assuming the length of the kill ring is 5 and the argument passed to rotate-yank-pointer is 1. When the kill-ring-yank-pointer points to the last element of the kill ring, its length is 1. The code looks like this:

(% (+ arg (- length (length kill-ring-yank-pointer))) length)

When the variables are replaced by their numeric values, the expression looks like this:

(% (+ 1 (- 5 1)) 5)

This expression can be evaluated by looking at the most embedded inner expression first and working outwards: The value of (- 5 1) is 4; the sum of (+ 1 4) is 5; and the remainder of dividing 5 by 5 is zero. So what rotate-yank-pointer will do is

(setq kill-ring-yank-pointer (nthcdr 0 kill-ring))

which will set the kill-ring-yank-pointer to point to the beginning of the kill ring.

So what happens with successive calls to rotate-yank-pointer is that it moves the kill-ring-yank-pointer from element to element in the kill ring until it reaches the end; then it jumps back to the beginning. And this is why the kill ring is called a ring, since by jumping back to the beginning, it is as if the list has no end! (And what is a ring, but an entity with no end?)

yank

After learning about rotate-yank-pointer, the code for the yank function is almost easy. It has only one tricky part, which is the computation of the argument to be passed to rotate-yank-pointer.

The code looks like this:

(defun yank (&optional arg)
  "Reinsert the last stretch of killed text.
More precisely, reinsert the stretch of killed text most 
recently killed OR yanked.
With just C-U as argument, same but put point in front 
(and mark at end).  With argument n, reinsert the nth 
most recently killed stretch of killed text.
See also the command \\[yank-pop]."

  (interactive "*P")
  (rotate-yank-pointer (if (listp arg) 0
                         (if (eq arg '-) -1
                           (1- arg))))
  (push-mark (point))
  (insert (car kill-ring-yank-pointer))
  (if (consp arg)
      (exchange-point-and-mark)))

Glancing over this code, we can understand the last few lines readily enough. The mark is pushed, that is, remembered; then the first element (the CAR) of what the kill-ring-yank-pointer points to is inserted; and then, if the argument passed the function is a cons, point and mark are exchanged so the point is put in the front of the inserted text rather than at the end. This option is explained in the documentation. The function itself is interactive with "*P". This means it will not work on a read-only buffer, and that the unprocessed prefix argument is passed to the function.

Passing the argument

The hard part of yank is understanding the computation that determines the value of the argument passed to rotate-yank-pointer. Fortunately, it is not so difficult as it looks at first sight.

What happens is that the result of evaluating one or both of the if expressions will be a number and that number will be the argument passed to rotate-yank-pointer.

Laid out with comments, the code looks like this:

(if (listp arg)                         ; if-part
    0                                   ; then-part
  (if (eq arg '-)                       ; else-part, inner if
      -1                                ; inner if's then-part
    (1- arg))))                         ; inner if's else-part

This code consists of two if expression, one the else-part of the other.

The first or outer if expression tests whether the argument passed to yank is a list. Oddly enough, this will be true if yank is called without an argument--because then it will be passed the value of nil for the optional argument and an evaluation of (listp nil) returns true! So, if no argument is passed to yank, the argument passed to rotate-yank-pointer inside of yank is zero. This means the pointer is not moved and the first element to which kill-ring-yank-pointer points is inserted, as we expect. Similarly, if the argument for yank is C-u, this will be read as a list, so again, a zero will be passed to rotate-yank-pointer. (C-u produces an unprocessed prefix argument of (4), which is a list of one element.) At the same time, later in the function, this argument will be read as a cons so point will be put in the front and mark at the end of the insertion. (The P argument to interactive is designed to provide these values for the case when an optional argument is not provided or when it is C-u.)

The then-part of the outer if expression handles the case then there is no argument or when it is C-u. The else-part handles the other situations. The else-part is itself another if expression.

The inner if expression tests whether the argument is a minus sign. (This is done by pressing the META and - keys at the same time, or the ESC key and then the - key). In this case, the rotate-yank-pointer function is passed -1 as an argument. This moves the kill-ring-yank-pointer backwards, which is what is desired.

If the true-or-false-test of the inner if expression is false (that is, if the argument is not a minus sign), the else-part of the expression is evaluated. This is the expression (1- arg). Because of the two if expressions, it will only occur when the argument is a positive number or when it is a negative number (not just a minus sign on its own). What (1- arg) does is decrement the number and return it. (The 1- function subtracts one from its argument.) This means that if the argument to rotate-yank-pointer is 1, it is reduced to zero, which means the first element to which kill-ring-yank-pointer points is yanked back, as you would expect.

Passing a negative argument

Finally, the question arises, what happens if either the remainder function, %, or the nthcdr function is passed a negative argument, as they quite well may?

The answers can be found by a quick test. When (% -1 5) is evaluated, a negative number is returned; and if nthcdr is called with a negative number, it returns the same value as if it were called with a first argument of zero. This can be seen be evaluating the following code.

Here the `=>' points to the result of evaluating the code preceding it. This was done by positioning the cursor after the code and typing C-x C-e (eval-last-sexp) in the usual fashion. You can do this if you are reading this in Info inside of GNU Emacs.

(% -1 5)
     => -1

(setq animals '(cats dogs elephants))
     => (cats dogs elephants)

(nthcdr 1 animals)
     => (dogs elephants)

(nthcdr 0 animals)
     => (cats dogs elephants)

(nthcdr -1 animals)
     => (cats dogs elephants)

So, if a minus sign or a negative number is passed to yank, the kill-ring-yank-point is rotated backwards until it reaches the beginning of the list. Then it stays there. Unlike the other case, when it jumps from the end of the list to the beginning of the list, making a ring, it stops. This makes sense. You often want to get back to the most recently clipped out piece of text, but you don't usually want to insert text from as many as thirty kill commands ago. So you need to work through the ring to get to the end, but won't cycle around it inadvertently if you are trying to come back to the beginning.

Incidentally, any number passed to yank with a minus sign preceding it will be treated as -1. This is evidently a simplification for writing the program. You don't need to jump back towards the beginning of the kill ring more than one place at a time and doing this is easier than writing a function to determine the magnitude of the number that follows the minus sign.

yank-pop

After understanding yank, the yank-pop function is easy. Leaving out the documentation to save space, it looks like this:

(defun yank-pop (arg)
  (interactive "*p")
  (if (not (eq last-command 'yank))
      (error "Previous command was not a yank"))
  (setq this-command 'yank)
  (let ((before (< (point) (mark))))
    (delete-region (point) (mark))
    (rotate-yank-pointer arg)
    (set-mark (point))
    (insert (car kill-ring-yank-pointer))
    (if before (exchange-point-and-mark))))

The function is interactive with a small `p' so the prefix argument is processed and passed to the function. The command can only be used after a previous yank; otherwise an error message is sent. This check uses the variable last-command which is discussed elsewhere. (See section copy-region-as-kill.)

The let clause sets the variable before to true or false depending whether point is before or after mark and then the region between point and mark is deleted. This is the region that was just inserted by the previous yank and it is this text that will be replaced. Next the kill-ring-yank-pointer is rotated so that the previously inserted text is not reinserted yet again. Mark is set at the beginning of the place the new text will be inserted and then the first element to which kill-ring-yank-pointer points is inserted. This leaves point after the new text. If in the previous yank, point was left before the inserted text, point and mark are now exchanged so point is again left in front of the newly inserted text. That is all there is to it!


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