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

后端 未结 8 988
無奈伤痛
無奈伤痛 2021-01-31 16:23

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
8条回答
  •  轻奢々
    轻奢々 (楼主)
    2021-01-31 17:20

    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)
    

提交回复
热议问题