Subject: yank-pop problem

From: Frederik Eaton <[email-masked]>
Date: Thu, 6 Aug 2015 00:38:31 -0700

Dear Help-Gnu-Emacs,

I was chatting on the #emacs freenode.net IRC channel and thought to
ask people about a long-standing annoyance I've had with Emacs. One
IRC user was horrified that I would find anything wrong with Emacs,
but another user suggested that I write up a document with the details
and send it as a bug report. So I wrote up a document which explains
the issue and how I think it should be fixed. I'm pasting it below and
would appreciate your comments. Mostly I'm looking to see if anyone
else has previously implemented this suggestion, and is willing to
share their code. I hope my thoughts are explained clearly enough.

Thanks!

Frederick

P.S. I Cc'ed [email-masked] because I figured problems go
there, but I haven't subscribed to that list and I'm not really
expecting the core of Emacs to be changed.

P.P.S. After writing this (typically of me) I just noticed that
somebody else posted this problem on stackoverflow.com in 2011!
However, none of the solutions posted there are really suitable in my
opinion. The basic problem, which is that Emacs puts stuff you don't
care about in the kill ring ahead of stuff that you do care about,
isn't ever fixed. Although the first ("jp/yank") solution is nearly
good enough, it still allows text we care about to get clobbered if
there is an intervening yank...

http://stackoverflow.com/questions/5823495/emacs-how-to-yank-the-last-yanked-text-regardless-of-subsequent-kills

----------------------------------------------------------------

Emacs Yank Pointer Problem Description

Problem:

In Emacs, I can use C-y (yank) and then M-y (yank-pop) to go through a
list of recent bits of text which have been killed.

Having done this, the next time I do C-y, it yanks the last bit of
text selected via M-y. This is desired since it means I don't have to
rotate through the list to find my favorite item again. This behavior
is implemented using the variable "kill-ring-yank-pointer" which
points to the last "yank-pop"ed item in the kill ring.

However, when I kill some text, the value of "kill-ring-yank-pointer"
is reset to point to the head of the "kill ring". This resetting means
that when I am interspersing a number of yanks (of the same item of
text) with an editing command such as M-d which also populates the
kill ring, then my "kill-ring-yank-pointer" is reset each time, and
each time I yank, I have to search farther and farther back with an
increasing number of M-y keystrokes, just to get the same item I've
been yanking.

In short, when (say) I am replacing a few strings manually with some
text from the kill ring, I end up doing C-y, then for the next
replacement C-y M-y, then next C-y M-y M-y, and next C-y M-y M-y M-y,
and so on.

I'll give a concrete tutorial. Try replacing every animal in the
following list with "dog", using only "M-d" and "C-y" and "M-y", and
"C-a" and "C-n".

dog
cat
ant
chicken

The kill ring will first look like ("dog"), then ("cat" "dog"), then
("ant" "cat" "dog"), then ("chicken" "ant" "cat" "dog"). After the
final yank-pop of each line, the kill-ring-yank-pointer will point to
"dog" at the end of the kill ring. However, when you use M-d to delete
the animal being replaced, then kill-ring-yank-pointer gets reset, and
so it is never used.

Proposed solution:

The kill ring, which is a list, should be treated as an array by
yank-pop, and rearranged more freely.

Killing text should still put stuff at the front of the list, as
before.

However, each yank-pop'ed item will also be moved to the front of the
kill ring.

Yank-pop should use a new variable to serve the purpose of
"kill-ring-yank-pointer". Rather than pointing to an existing item in
the kill ring, the new variable will be an index into the kill ring,
which just tells yank-pop how to fix up the kill ring in case the last
command was yank-pop. For example, suppose the kill ring looks like
this:

(I) A B C D E

C-y M-y M-y will end up with C being inserted at the cursor. After
that, we DON'T want the kill ring to look like "C D E A B" because we
care more about A and B than about D or E, as they are more recently
used items. What we do want it to look like is

(II) C A B D E

But suppose the next command is also M-y. Then the kill ring should
look like

(III) D A B C E

because we care more about A and B than about C (since C we saw only
on our way to D, and didn't end up using it). In order to obtain this
configuration, yank-pop will have to have inserted C back where it
came from, and moved D to the front. It would presumably do this with
an integer variable which is incremented at each yank-pop, and reset
to 0 in the 'yank' command, to guide the insertion and extraction of
elements from the kill ring.

To be specific, let's call the new variable
"kill-ring-yank-pop-index". In state (I) above,
"kill-ring-yank-pop-index" could have an arbitrary value, but it is
reset to zero by "yank". In state (II) above, it would have the value
2 to indicate that C came from position 2 in the (zero-indexed) kill
ring. In state (III), it would have value 3 - at that point, yank-pop
will have read the value 2 from "kill-ring-yank-pop-index", used that
to put C back into the list at position 2, moved D to the front, and
incremented the value to 3.

The END.