Emacs:如何粘贴最后一次复制的文本,而不管后续是否有删除操作?

34

我经常发现自己在执行一些操作后不断地重复粘贴某个内容,这个过程如下:

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

每次我杀死(kill)一些文本时,它会把第一个 kill 的内容推回kill ring中,因此需要循环浏览所有的kill才能返回到想要粘贴的文本。我的目标是在粘贴之间杀死文本并重复粘贴相同的文本。是否有可能实现这个目标?


我经常遇到同样的问题。我认为你需要做一些关于 kill 而不是 yank 的事情,例如,一个小模式,在这个模式下被删除的内容不会添加到 kill ring 中。 - sawa
我认为有一种方法可以旋转 kill-ring。你不需要一遍又一遍地执行 C-y,但至少它不会每次都是一个增长的命令列表。我急切地等待真正的答案。 - drysdam
这引出了一个问题,如果 C-y 总是粘贴相同的文本,那么你如何告诉 Emacs C-y 应该粘贴什么呢?也就是说,如果删除文本不起作用... - Trey Jackson
3
相关链接:https://dev59.com/FG865IYBdhLWcg3wfusk - Juancho
8个回答

21

不要使用kill ring,而是将文本放入寄存器中。使用C-x r s a将区域内的文本存储到(比如说)寄存器"a"中;然后使用C-x r i a在其他地方插入它。


如果你想的话,可以将C-x r i a制作成一个键盘宏。 - Nicholas Riley

20

这是一个奇怪的技巧,但可能会有所帮助。

第一次使用 M-y 通常会出现错误(没有先前的yank)。因此,这个想法是在第一次使用时获取最后一个yank而不是最后一个kill。

为了存储最后一个yank,我在这个例子中使用 'Y' 寄存器。

这两个函数将包装yank和yank-pop。您可以期望出现错误,我期望得到建议。

(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))

1
我最近了解到,set-registerget-register也将接受非字符键(但比较是使用eq进行的,因此字符串不是一个选项)。对于这种用法,可能最好不要破坏字符寄存器(这也可能在交互上下文中使用),而是使用符号。 - phils

5
你可以使用M-x delete-region代替删除文本,如果你经常使用它,可以将其绑定到一个按键上。

2
我建议将 delete-region 绑定到 C-c k :-) - semente
4
在最新版本的Emacs中,退格键执行“delete-region”命令。选定一个区域,按下退格键可以将其擦除而不将其推送到kill环中。这种行为可以使用“delete-active-region”选项进行自定义。 - Jisang Yoo

4
  1. 如果你想重复拷贝相同的文本,使用次选区而不是区域或删除的文本。

    在基本的Emacs中没有绑定按键来拷贝次选区。我使用C-M-y(参见库second-sel.el)进行拷贝。

  2. 使用M-y结合浏览Kill Ring或者Icicles可以直接访问kill ring里的任何拷贝内容。 在这两种情况下,从顶层开始,M-y可以让你访问kill ring里的所有条目。

    如果你同时使用库second-sel.el,除了kill ring之外,你还可以访问过去次选区的环形内容。

    如果你同时使用库second-sel.elIcicles,那么M-y可以从你最后拷贝的环形中(kill ring或次选区环形)获取内容。

    如果你使用库browse-kill-ring+.el,则kill-ring浏览器还可以让你访问另一个环形(默认情况下,如果你使用库second-sel.el,则是次选区环形)。


2

我会尝试更多地使用delete-region,但我更熟悉kill命令。一个不需要编程或提前规划的技巧是在一连串烦人的M-y之后使用M-w。这会将最终的yank更方便地复制到kill ring中。


2

更好的Yank-Pop实现

这定义了一个更好的Yank-Pop实现,旨在解决不断增加的Yank-Pop问题。

它只覆盖了函数“current-kill”。由于Emacs中yank、yank-pop和kill ring变量和函数的模块化设计,覆盖“current-kill”就足以获得我们想要的行为。

所需的行为是:(1) 删除某些内容仍将其放在kill ring的前面,但现在 (2) 粘贴或yank-pop某些内容也将其放在kill ring的前面,(3) 我们保留yank-pop的能力,通过增加全局变量并使用它将上一个yank-pop的项替换回原来的位置,从而给出移动kill ring的外观。 这也意味着 (4) 转换性地yanked的项(即由yank或yank-pop命令放置的项,下一个命令是yank-pop)最终可以保留在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.

代码:

;; ----------------------------------------------------------------
;; 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)

顺便说一下,这就像是“iflipb”,它使用类似的缓冲区切换算法:http://www.emacswiki.org/emacs/iflipb - Metamorphic
1
非常有用。我一直想知道为什么这不是emacs的默认行为。 - asmeurer

1

我正在尝试通过使用一个小模式来进行黑客攻击。让我们称之为delete-mode。一旦进入删除模式,杀死命令(kill-linekill-paragraphkill-word等)将改变它们的行为,使得它们的命令中的kill-region部分将被替换为delete-region,并且新的材料不会被添加到kill ring中。在这种模式下,kill ring将保持不变。当您退出此模式时,行为将恢复正常。

以下是一个不完整的代码,试图实现上述内容。它在切换到删除模式时可以正常工作,但在切换回来时存在问题(关闭小模式)。任何帮助修复此问题的建议都将不胜感激。

(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)
)

1
我喜欢这种方法。只是我采取了另一种方式:从相关命令中删除 kill-region,这样我仍然可以在 CUA 模式下使用 C-x 来剪切内容。以下代码似乎对我有效:https://gist.github.com/vfaronov/6156902 但这是我第一次接触 Elisp,所以可能都是错误和可怕的。 - Vasiliy Faronov

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接