kill-append 函数 ¶kill-append 函数定义如下:
(defun kill-append (string before-p &optional yank-handler)
"将 STRING 追加到删除环最新删除内容的末尾。
若 BEFORE-P 非 nil,则将 STRING 添加到前方。
... "
(let* ((cur (car kill-ring)))
(kill-new (if before-p (concat string cur) (concat cur string))
(or (= (length cur) 0)
(equal yank-handler
(get-text-property 0 'yank-handler cur)))
yank-handler)))
kill-append 的逻辑相当直观。
它使用了 kill-new 函数,我们稍后会详细说明。
(此外,该函数还提供了可选参数 yank-handler;
调用时该参数用于告知函数如何处理
文本附加的属性,如粗体、斜体等。)
它使用 let* 将删除环第一个元素赋值给 cur。
(我不确定此处为何不用 let;
表达式中只设置了一个变量。
这或许是一个不引发问题的小错误?)
看一下作为 kill-new 参数之一的条件表达式:
它使用 concat 将新文本与删除环的 CAR 拼接。
是向前添加还是向后追加,取决于 if 表达式:
(if before-p ; if-part (concat string cur) ; then-part (concat cur string)) ; else-part
如果本次删除的选区位于上一次删除的选区之前,
就应该添加到之前保存的内容前面;
反之,如果本次删除的文本在后方,
就追加到之前文本的后面。
该 if 表达式依靠谓词 before-p
决定新保存的文本放在已有文本之前还是之后。
符号 before-p 是 kill-append
的其中一个参数名。当 kill-append 被求值时,
它会绑定到实际参数的求值结果。
在本例中,该表达式为 (< end beg)。
该表达式并不直接判断本次删除的文本
位于上一次删除文本的前方还是后方,
而是判断变量 end 的值是否小于 beg。
如果成立,通常意味着用户正向缓冲区开头移动。
同时,谓词表达式 (< end beg) 的结果为真,
文本会被添加到之前文本的前面。
反之,如果 end 大于 beg,
文本就会追加到已有文本之后。
当新保存的文本需要向前添加时, 新文本字符串会拼接在旧文本之前:
(concat string cur)
但如果是向后追加,则拼接在旧文本之后:
(concat cur string))
要理解这一机制,我们首先需要回顾
concat 函数。concat
会将两个文本字符串连接为一个字符串。例如:
(concat "abc" "def")
⇒ "abcdef"
(concat "new "
(car '("first element" "second element")))
⇒ "new first element"
(concat (car
'("first element" "second element")) " modified")
⇒ "first element modified"
现在我们可以理解 kill-append:
它修改删除环的内容。删除环是一个列表,
每个元素都是保存的文本。
kill-append 调用 kill-new,
而后者内部又使用了 setcar 函数。