B.2 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 fdescribe-function)再按 RET 查看文档,默认即为当前函数。)

函数最后一部分说明执行成功时的处理逻辑。