unofficial mirror of help-gnu-emacs@gnu.org
 help / color / mirror / Atom feed
* yank-pop problem
@ 2015-08-06  7:38 Frederik Eaton
  2015-08-06 23:49 ` Frederik Eaton
  0 siblings, 1 reply; 6+ messages in thread
From: Frederik Eaton @ 2015-08-06  7:38 UTC (permalink / raw)
  To: help-gnu-emacs; +Cc: bug-gnu-emacs

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 bug-gnu-emacs@gnu.org 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.



^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: yank-pop problem
  2015-08-06  7:38 yank-pop problem Frederik Eaton
@ 2015-08-06 23:49 ` Frederik Eaton
  2015-08-07  3:15   ` Ian Zimmerman
  0 siblings, 1 reply; 6+ messages in thread
From: Frederik Eaton @ 2015-08-06 23:49 UTC (permalink / raw)
  To: help-gnu-emacs; +Cc: bug-gnu-emacs

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 bug-gnu-emacs@gnu.org 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.
> 



^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: yank-pop problem
  2015-08-06 23:49 ` Frederik Eaton
@ 2015-08-07  3:15   ` Ian Zimmerman
  2015-08-07 18:49     ` code: deleterious mode [Was: yank-pop problem] Ian Zimmerman
  0 siblings, 1 reply; 6+ messages in thread
From: Ian Zimmerman @ 2015-08-07  3:15 UTC (permalink / raw)
  To: help-gnu-emacs

On 2015-08-06 16:49 -0700, Frederik Eaton wrote:

Frederik> The basic problem, which is that Emacs puts stuff you don't
Frederik> care about in the kill ring ahead of stuff that you do care
Frederik> about, isn't ever fixed.

If you really don't care about it, should you not _delete_ it, instead
of killing it?  Indeed, isn't that why delete-region exists?

-- 
Please *no* private copies of mailing list or newsgroup messages.
Rule 420: All persons more than eight miles high to leave the court.




^ permalink raw reply	[flat|nested] 6+ messages in thread

* code: deleterious mode [Was: yank-pop problem]
  2015-08-07  3:15   ` Ian Zimmerman
@ 2015-08-07 18:49     ` Ian Zimmerman
  2015-08-07 22:06       ` code: deleterious mode Ian Zimmerman
  0 siblings, 1 reply; 6+ messages in thread
From: Ian Zimmerman @ 2015-08-07 18:49 UTC (permalink / raw)
  To: help-gnu-emacs

This thread gave me an idea :-)

Constructive criticism most welcome!

;;; deleterious.el --- minor move to delete things instead of killing them

;; Copyright (C) Ian Zimmerman 2015
;; Terms: GNU General Public License, Version 2

(require 'thingatpt)

(defmacro deleterious-def (thing)
  (let ((sthing (symbol-name thing)))
  `(defun ,(intern (concat "deleterious-delete-" sthing)) (n)
     ,(concat
       "Delete a " sthing
       " without adding to the kill ring.
Numeric prefix arg means delete that many " sthing "s.
Negative arg means delete backward.")
     (interactive "p")
     (let ((bound
            (save-excursion
              (forward-thing (quote ,thing) n)
              (point))))
       (if (< (point) bound) (delete-region (point) bound)
         (delete-region bound (point)))))))

(deleterious-def line)
(deleterious-def sentence)
(deleterious-def sexp)
(deleterious-def word)

(defconst deleterious-mode-map
  (let ((k (make-sparse-keymap)))
    (define-key k [remap kill-region] 'delete-region)
    (define-key k [remap kill-line] 'deleterious-delete-line)
    (define-key k [remap kill-sentence] 'deleterious-delete-sentence)
    (define-key k [remap kill-sexp] 'deleterious-delete-sexp)
    (define-key k [remap kill-word] 'deleterious-delete-word)
    k)
  "Keymap to use in Deleterious minor mode.")

;;;###autoload
(define-minor-mode deleterious-mode
  "Set or toggle Deleterious minor mode.  It deletes instead of killing.
This global minor mode rebinds a few keys to commands which remove bits
of text but *do not* put them on the kill ring.  It is useful for
repetitive tasks involving multiple yanks of the same string; the string
you yank will not get pushed back in the kill ring by new additions."
  :global t :init-value nil :lighter " Del" :keymap deleterious-mode-map)

(provide 'deleterious)

\f
;; Local Variables:
;; End:

;;; deleterious.el ends here

-- 
Please *no* private copies of mailing list or newsgroup messages.
Rule 420: All persons more than eight miles high to leave the court.




^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: code: deleterious mode
  2015-08-07 18:49     ` code: deleterious mode [Was: yank-pop problem] Ian Zimmerman
@ 2015-08-07 22:06       ` Ian Zimmerman
  2015-08-10  5:31         ` Frederik Eaton
  0 siblings, 1 reply; 6+ messages in thread
From: Ian Zimmerman @ 2015-08-07 22:06 UTC (permalink / raw)
  To: help-gnu-emacs

Now even denser, and with backward deletions too.  Probably final.

;;; deleterious.el --- minor move to delete things instead of killing them

;; Copyright (C) Ian Zimmerman 2015
;; Terms: GNU General Public License, Version 2

(require 'thingatpt)

(defconst deleterious-mode-map
  (let ((k (make-sparse-keymap)))
    (define-key k [remap kill-region] 'delete-region)
    k)
  "Keymap to use in Deleterious minor mode.")

(defsubst deleterious-del (thing n)
  (let ((bound (save-excursion (forward-thing thing n) (point))))
    (if (< (point) bound) (delete-region (point) bound)
      (delete-region bound (point)))))

(defmacro deleterious-def (thing)
  (let* ((sth (symbol-name thing))
         (ndoc (concat "Numeric prefix arg means delete that many " sth "s.\n"))
         (delthing (intern (concat "deleterious-delete-" sth)))
         (bdelthing (intern (concat "deleterious-backward-delete-" sth)))
         (kthing (intern (concat "kill-" sth)))
         (bkthing (intern (concat "backward-kill-" sth))))
    `(let (fdelthing fbdelthing)
       (setq fdelthing
        (fset (quote ,delthing)
         (lambda (n) (interactive "p") (deleterious-del (quote ,thing) n))))
       (put (quote ,delthing) 'function-documentation
            (concat "Delete a " ,sth " without adding to the kill ring.\n"
                    ,ndoc "Negative arg means delete backward."))
       (define-key deleterious-mode-map [remap ,kthing] fdelthing)
       (setq fbdelthing
        (fset (quote ,bdelthing)
         (lambda (n) (interactive "p") (deleterious-del (quote ,thing) (- n)))))
       (put (quote ,bdelthing) 'function-documentation
            (concat
             "Delete a " ,sth " backward without adding to the kill ring.\n"
             ,ndoc "Negative arg means delete forward."))            
       (define-key deleterious-mode-map [remap ,bkthing] fbdelthing))))

(deleterious-def line)
(deleterious-def sentence)
(deleterious-def sexp)
(deleterious-def word)

;;;###autoload
(define-minor-mode deleterious-mode
  "Set or toggle Deleterious minor mode.  It deletes instead of killing.
This global minor mode rebinds a few keys to commands which remove bits
of text but *do not* put them on the kill ring.  It is useful for
repetitive tasks involving multiple yanks of the same string; the string
you yank will not get pushed back in the kill ring by new additions."
  :global t :init-value nil :lighter " Del" :keymap deleterious-mode-map)

(provide 'deleterious)

\f
;; Local Variables:
;; End:

;;; deleterious.el ends here

-- 
Please *no* private copies of mailing list or newsgroup messages.
Rule 420: All persons more than eight miles high to leave the court.




^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: code: deleterious mode
  2015-08-07 22:06       ` code: deleterious mode Ian Zimmerman
@ 2015-08-10  5:31         ` Frederik Eaton
  0 siblings, 0 replies; 6+ messages in thread
From: Frederik Eaton @ 2015-08-10  5:31 UTC (permalink / raw)
  To: help-gnu-emacs

Interesting take! Thanks for the contribution.

Frederick



^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2015-08-10  5:31 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-08-06  7:38 yank-pop problem Frederik Eaton
2015-08-06 23:49 ` Frederik Eaton
2015-08-07  3:15   ` Ian Zimmerman
2015-08-07 18:49     ` code: deleterious mode [Was: yank-pop problem] Ian Zimmerman
2015-08-07 22:06       ` code: deleterious mode Ian Zimmerman
2015-08-10  5:31         ` Frederik Eaton

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).