Subject: bug#21199: yank-pop problem

From: Frederik Eaton <[email-masked]>
Date: Thu, 6 Aug 2015 16:49:43 -0700

I just coded up a solution and posted it under "A Better Yank-Pop
Implementation" in the stackoverflow thread:

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

On Thu, Aug 06, 2015 at 12:38:31AM -0700, Frederik Eaton wrote:
> 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.
>