5.3.3 beginning-of-buffer 完整实现

以下是 beginning-of-buffer 函数的完整代码:


(defun beginning-of-buffer (&optional arg)
  "将光标移至缓冲区开头;在原位置保留标记。
带 \\[universal-argument] 前缀时,不在原位置设置标记。
带数字参数 N 时,将光标置于距开头 N/10 处。

若缓冲区被缩窄,该命令会使用缓冲区可访问部分的开头与大小。

不要在 Lisp 程序中使用该命令!
\(goto-char (point-min)) 速度更快,且不会覆盖标记。"
  (interactive "P")
  (or (consp arg)
      (and transient-mark-mode mark-active)
      (push-mark))
  (let ((size (- (point-max) (point-min))))
    (goto-char (if (and arg (not (consp arg)))
                   (+ (point-min)
                      (if (> size 10000)
                          ;; 避免大缓冲区数值溢出!
                          (* (prefix-numeric-value arg)
                             (/ size 10))
                        (/ (+ 10 (* size (prefix-numeric-value arg)))
                           10)))
                 (point-min))))
  (if (and arg (not (consp arg))) (forward-line 1)))

除两处细节外,前文已说明该函数的工作原理。第一处是文档字符串中的细节,第二处是函数最后一行的逻辑。

文档字符串中引用了如下表达式:

\\[universal-argument]

该表达式左方括号前使用了 ‘\\’。这个 ‘\\’ 告知 Lisp 解释器,将 ‘[…]’ 替换为当前绑定的按键。对于 universal-argument,该按键通常为 C-u,但也可能不同。(更多信息参见 See Tips for Documentation Strings in The GNU Emacs Lisp Reference Manual。)

最后,beginning-of-buffer 命令的最后一行表示:若命令带参数调用,则将光标移至下一行开头:

(if (and arg (not (consp arg))) (forward-line 1))

这会将光标置于缓冲区对应十分之几位置的下一行行首。这一细节处理确保光标 至少 位于缓冲区目标比例位置之后,属于锦上添花的设计——虽非必需,但缺少则很可能引发用户抱怨。((not (consp arg)) 部分用于避免:仅使用 C-u 而不带数字时,即原始前缀参数只是一个序对节点时,命令不会将光标跳至第二行开头。)