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.
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.
rotate-yank-pointer
.
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.)
if
expression.
%
, function.
%
in rotate-yank-pointer
.
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)
%
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.)
%
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.
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.
rotate-yank-pointer
.
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.
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!