Emacs: How to yank the last yanked text regardless of subsequent kills?
Asked Answered
S

8

34

I often find myself repeatedly yanking something after doing some kills and it becomes a process like:

  1. C-y
  2. C-y M-y
  3. C-y M-y M-y
  4. C-y M-y M-y M-y

Each time I kill some text it pushes the first kill back in the kill ring so that I need to cycle through all the kills to return to text I want to yank. What I want to do is repeatedly yank the same text while killing text in-between yanks. Is this possible?

Sunshine answered 28/4, 2011 at 18:46 Comment(4)
I often face the same problem. I think you have to do something with kill rather than with yank, for example, a minor mode in which killed material is not added to the kill ring.Mossy
I think there's a way to rotate the kill-ring. You wouldn't do C-y over and over, but it might at least not be a growing list of commands each time. I await a real answer with bated breath.Shipping
It begs the question, if C-y always yanks the same text, how do you tell Emacs what C-y should yank? i.e. if killing the text doesn't do it...Bastien
Related: #3787395Ralina
R
20

This is a strange hack, but may help.

The first time you use M-y you normally get an error (no previous yank). So the idea is that this first time you get the last yank instead of the last kill.

For storing that last yank I use the 'Y' register in this example.

These 2 functions would wrap around yank and yank-pop. You expect bugs, I expect suggestions.

(defun jp/yank (&optional arg)
  "Yank and save text to register Y"
  (interactive)
  (set-register ?Y (current-kill 0 t))
  (yank arg))

(defun jp/yank-pop (&optional arg)
  "If yank-pop fails, then insert register Y"
  (interactive)
  (condition-case nil
      (yank-pop arg)
    (error (insert (get-register ?Y)))))

(global-set-key (kbd "M-y") (quote jp/yank-pop))
(global-set-key (kbd "C-y") (quote jp/yank))
Ralina answered 28/4, 2011 at 21:5 Comment(1)
I learned recently that set-register and get-register will also accept non-character keys (but the comparison is done with eq, so strings aren't an option). For this sort of usage it probably makes sense to not clobber a character register (which might also be used in an interactive context), and to use a symbol instead.Lucent
A
21

Don't use the kill ring; put the text into a register instead. C-x r s a to store the region's text into (say) register "a"; then C-x r i a to insert it elsewhere.

Aquiculture answered 28/4, 2011 at 20:4 Comment(1)
...and make a keyboard macro out of C-x r i a if you want.Vestal
R
20

This is a strange hack, but may help.

The first time you use M-y you normally get an error (no previous yank). So the idea is that this first time you get the last yank instead of the last kill.

For storing that last yank I use the 'Y' register in this example.

These 2 functions would wrap around yank and yank-pop. You expect bugs, I expect suggestions.

(defun jp/yank (&optional arg)
  "Yank and save text to register Y"
  (interactive)
  (set-register ?Y (current-kill 0 t))
  (yank arg))

(defun jp/yank-pop (&optional arg)
  "If yank-pop fails, then insert register Y"
  (interactive)
  (condition-case nil
      (yank-pop arg)
    (error (insert (get-register ?Y)))))

(global-set-key (kbd "M-y") (quote jp/yank-pop))
(global-set-key (kbd "C-y") (quote jp/yank))
Ralina answered 28/4, 2011 at 21:5 Comment(1)
I learned recently that set-register and get-register will also accept non-character keys (but the comparison is done with eq, so strings aren't an option). For this sort of usage it probably makes sense to not clobber a character register (which might also be used in an interactive context), and to use a symbol instead.Lucent
H
5

You could use M-x delete-region instead to kill the text, possibly binding it to a key if you want to use it a lot.

Herold answered 28/4, 2011 at 18:50 Comment(2)
I suggest binding delete-region to C-c k :-)Leggat
Also, Backspace in latest version of Emacs does delete-region. Select a region and press Backspace to erase it without pushing it to the kill ring. This behavior can be customized with the delete-active-region option.Folkestone
C
4
  1. If you want to repeatedly yank the same text, use the secondary selection instead of the region or killed text.

    What's missing from vanilla Emacs is a key binding to yank the secondary selection. I use C-M-y for that (see library second-sel.el).

  2. To get direct access to any kills in the kill ring, use M-y with Browse Kill Ring or with Icicles. In both cases, M-y at top level gives you access to all entries in the kill ring.

    And if you use library second-sel.el then, in addition to the kill ring, you have access to a ring of your past secondary selections.

    And if you use library second-sel.el and Icicles then M-y yanks an entry from the the ring you last yanked from (kill ring or secondary-selection ring).

    And if you use library browse-kill-ring+.el then the kill-ring browser gives you access to an alternative ring also (which, by default, is the ring of secondary selections if you use library second-sel.el).

Counterfactual answered 27/7, 2014 at 17:46 Comment(0)
N
2

I'll try to use delete-region more, but I know the kill commands better. A trick that requires no programming or advance planning is to use M-w after a particularly annoying string of M-y. This puts a more accessible copy of the final yank into the kill ring.

Nodular answered 27/11, 2013 at 18:14 Comment(0)
I
2

A Better Yank-Pop Implementation

This defines a better yank-pop implementation which tries to fix the increasing yank-pop problem.

It only overrides the function "current-kill". Due to the modular design of yank, yank-pop, and the kill ring variables and functions in Emacs, it turns out that overriding "current-kill" is all that is necessary to get the behavior we want.

The desired behavior is that (1) killing something still puts it at the front of the kill ring, but now (2) yanking or yank-popping something also puts it at the front of the kill ring (3) we preserve the ability of yank-pop to give the appearance of moving through the kill ring by incrementing a global variable and using this to replace the last yank-pop'ped item back where it was. This also means that (4) items which are transitionally yanked (i.e. items placed by yank or yank-pop commands, where the next command is a yank-pop) ultimately get to stay where they are in the kill ring.

;; Example:
;; (setq kill-ring '("a" "b" "c" "d" "e"))
;;
;; keystroke        kill ring contents              value of kill-ring-yank-index
;; C-y              ("a" "b" "c" "d" "e")           0
;; M-y              ("b" "a" "c" "d" "e")           1
;; M-y              ("c" "a" "b" "d" "e")           2
;; M-y              ("d" "a" "b" "c" "e")           3

;; C-y              ("d" "a" "b" "c" "e")           0
;; M-y              ("a" "d" "b" "c" "e")           1

;; M-d              ("x" "a" "d" "b" "c" "e")
;; etc.

the code:

;; ----------------------------------------------------------------
;; helper functions

(defun list-insert-before (l n x)
  (if (<= n 0) (cons x l)
    (cons (car l) (list-insert-before (cdr l) (- n 1) x))))

(defun list-prepend-nth (l n)
  (if (<= n 0) l
    (let* ((lx (list-prepend-nth (cdr l) (- n 1))))
      (cons (car lx) (cons (car l) (cdr lx))))))

(defun list-insert-car-at (l n)
  (list-insert-before (cdr l) n (car l)))


;; ----------------------------------------------------------------
;; overriding current-kill

(defvar kill-ring-yank-index 0
  "Index into kill-ring of last yank-pop. The item yank-popped
  will be at the head of the kill ring, but if the next command
  is also yank-pop, it will be returned here first before this
  variable is incremented.")

(defun current-kill (n)
  "Replaces standard 'current-kill' function. This version tries
to fix the increasing yank-pop problem.

TODO:
- respect second argument of original function
- deal with 'interprogram-{cut,paste}-function'
"
  (if (eq 0 n) ;; looks like we're doing a yank; reset
               ;; kill-ring-yank-index to 0 to indicate that the
               ;; current head of the list is useful to the user
      (progn (setq kill-ring-yank-index 0)
             (car kill-ring))

    ;; otherwise put the head of kill-ring back where we had
    ;; previously found it, and fetch the next element
    (setq kill-ring
        (list-insert-car-at kill-ring kill-ring-yank-index))
    (setq kill-ring-yank-index (+ kill-ring-yank-index n))
    (when (>= kill-ring-yank-index (- (length kill-ring) 1))
      (setq kill-ring-yank-index (- (length kill-ring) 1))
      (message "Reached end of kill-ring"))
    (when (< kill-ring-yank-index 0)
      (setq kill-ring-yank-index 0)
      (message "Reached beginning of kill-ring"))
    (setq kill-ring (list-prepend-nth kill-ring kill-ring-yank-index))
    (car kill-ring)))

;; ----------------------------------------------------------------
;; new key binding

;; Here's an auxiliary function and key binding that makes it easy to
;; go back and forth in the kill-ring while we're yank-popping
(defun yank-pop-back () "" (interactive "*")
       (yank-pop -1))

(global-set-key "\C-\M-y" 'yank-pop-back)
Involucrum answered 6/8, 2015 at 23:47 Comment(2)
By the way this is like "iflipb", which uses a similar algorithm for buffer-switching: emacswiki.org/emacs/iflipbInvolucrum
Incredibly useful. I've wondered why this isn't the default behavior in emacs.Doctrinal
M
1

I am trying to hack along the line of using a minor mode. Let's call this delete-mode. Once you get into delete mode, kill commands (kill-line, kill-paragraph, kill-word, ...) will change their behavior so that the kill-region part of their commands will be replaced by delete-region, and new material will not be added to the kill ring. While in this mode, the kill ring will stay constant. When you switch back out of this mode, the behaviour returns to normal.

The following is an incomplete code attempting to implement what I wrote above. It works correctly in switching to delete mode, but it has problem switching back (turning the minor mode off). Any help fixing this would be appreciated.

(defvar delete-mode nil)

(defun delete-mode ()
    "delete minor-mode"
    (interactive)
    (setq delete-mode (not delete-mode))
    (if delete-mode
        (defalias 'kill-region 'delete-region)
        (defalias 'kill-region 'original-kill-region)
    )
)

(if (not (assq 'delete-mode minor-mode-alist))
    (setq minor-mode-alist
        (cons '(delete-mode "Delete mode on") minor-mode-alist)
    )
    (defalias 'original-kill-region 'kill-region)
)
Mossy answered 29/4, 2011 at 1:55 Comment(1)
I like this approach. Only I went the other way round: replace kill-region from out of the relevant commands, so I can still do C-x (in CUA mode) to actually cut stuff. The following code seems to work for me: gist.github.com/vfaronov/6156902 but it’s my first contact with Elisp so it’s probably all wrong and horrible.Fortunio
L
0

I use

M-x browse-kill-ring

with keybinding 'M-y'. There is also helm support.

https://www.emacswiki.org/emacs/BrowseKillRing

offers more solutions.

Lemniscate answered 27/12, 2017 at 14:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.