yank ¶学习完 current-kill 之后,yank 函数的代码就几乎很容易理解了。
yank 函数并不直接使用 kill-ring-yank-pointer 变量,它调用 insert-for-yank,而后者再调用 current-kill,由 current-kill 设置 kill-ring-yank-pointer 变量。
代码如下:
(defun yank (&optional arg) "重新插入(\"粘贴(paste)\")最近一次剪切的文本片段。 更准确地说,重新插入最近一次剪切或粘贴的文本片段。 将光标置于末尾,标记置于开头。 若仅使用 \\[universal-argument] 作为参数,效果相同但光标置于开头(标记置于末尾)。 参数为 N 时,重新插入第 N 近的剪切文本片段。 当该命令将剪切文本插入缓冲区时,会遵循 `yank-excluded-properties' 与 `yank-handler',详见 `insert-for-yank-1' 的文档字符串。 另请参见命令 `yank-pop'(\\[yank-pop])。"
(interactive "*P") (setq yank-window-start (window-start)) ;; 若未完整执行完毕,让 last-command 向后续命令指明这一点 (setq this-command t) (push-mark (point))
(insert-for-yank (current-kill (cond
((listp arg) 0)
((eq arg '-) -2)
(t (1- arg)))))
(if (consp arg)
;; 效果类似交换光标与标记,但不激活标记
;; 即便命令循环会因插入文本而取消标记激活,
;; 避免激活依然更简洁
(goto-char (prog1 (mark t)
(set-marker (mark-marker) (point) (current-buffer)))))
;; 若完整执行完毕,让 this-command 指明这一点
(if (eq this-command t)
(setq this-command 'yank))
nil)
核心表达式是 insert-for-yank,它插入 current-kill 返回的字符串,并移除其中部分文本属性。
在执行该表达式之前,函数会先将 yank-window-start 设为 (window-start) 表达式返回的位置,即当前显示起始位置。yank 函数还会设置 this-command 并推入标记。
粘贴对应元素后,若可选参数为 CONS 类型而非数字或空,则将光标置于粘贴文本开头,标记置于末尾。
(prog1 函数与 progn 类似,但返回首个参数的值而非最后一个。它的首个参数会强制以整数形式返回缓冲区标记。你可以将光标置于本缓冲区的这些函数上,然后按 C-h f(describe-function)再按 RET 查看文档,默认即为当前函数。)
函数最后一部分说明执行成功时的处理逻辑。