yank-pop ¶理解 yank 与 current-kill 之后,你就知道该如何分析 yank-pop 函数了。为节省篇幅省略文档字符串,代码如下:
(defun yank-pop (&optional arg)
"..."
(interactive "*p")
(if (not (eq last-command 'yank))
(error "Previous command was not a yank"))
(setq this-command 'yank)
(unless arg (setq arg 1))
(let ((inhibit-read-only t)
(before (< (point) (mark t))))
(if before
(funcall (or yank-undo-function 'delete-region) (point) (mark t))
(funcall (or yank-undo-function 'delete-region) (mark t) (point)))
(setq yank-undo-function nil)
(set-marker (mark-marker) (point) (current-buffer))
(insert-for-yank (current-kill arg))
;; 尽可能将窗口起始位置恢复到粘贴命令执行时的状态
(set-window-start (selected-window) yank-window-start t)
(if before
;; 效果类似交换光标与标记,但不激活标记
;; 即便命令循环会因插入文本而取消标记激活,
;; 避免激活依然更简洁
(goto-char (prog1 (mark t)
(set-marker (mark-marker)
(point)
(current-buffer))))))
nil)
该函数为交互式函数,使用小写 ‘p’ 前缀参数,前缀参数会被处理并传入函数。该命令只能在之前执行过粘贴操作后使用,否则会抛出错误提示。该检查使用了 last-command 变量,其值由 yank 设置,相关内容会在其他地方讨论。(See copy-region-as-kill。)
let 子句会根据光标在标记之前还是之后,将 before 变量设为真或假,随后删除光标与标记之间的区域。该区域正是上一次粘贴插入的内容,即将被替换的文本。
funcall 将首个参数作为函数执行,并将剩余参数传入。首个参数是 or 表达式的返回值,剩余两个参数是上一次 yank 命令设置的光标与标记位置。
还有更多细节,但这已是最难理解的部分。