let* 表达式 ¶forward-paragraph 函数的下一行开始一个 let* 表达式(see let* introduced),Emacs 在其中总共绑定了七个变量:opoint、fill-prefix-regexp、parstart、parsep、sp-parstart、start 和 found-start。let* 表达式的前半部分如下:
(let* ((opoint (point))
(fill-prefix-regexp
(and fill-prefix (not (equal fill-prefix ""))
(not paragraph-ignore-fill-prefix)
(regexp-quote fill-prefix)))
;; Remove ^ from paragraph-start and paragraph-sep if they are there.
;; These regexps shouldn't be anchored, because we look for them
;; starting at the left-margin. This allows paragraph commands to
;; work normally with indented text.
;; This hack will not find problem cases like "whatever\\|^something".
(parstart (if (and (not (equal "" paragraph-start))
(equal ?^ (aref paragraph-start 0)))
(substring paragraph-start 1)
paragraph-start))
(parsep (if (and (not (equal "" paragraph-separate))
(equal ?^ (aref paragraph-separate 0)))
(substring paragraph-separate 1)
paragraph-separate))
(parsep
(if fill-prefix-regexp
(concat parsep "\\|"
fill-prefix-regexp "[ \t]*$")
parsep))
;; This is used for searching.
(sp-parstart (concat "^[ \t]*\\(?:" parstart "\\|" parsep "\\)"))
start found-start)
...)
变量 parsep 出现了两次:第一次用于移除 ‘^’ 实例,第二次用于处理填充前缀。
变量 opoint 就是光标 point 的值。你可以猜到,它和在 forward-sentence 中一样,用于 constrain-to-field 表达式。
变量 fill-prefix-regexp 被设为对以下列表求值后返回的值:
(and fill-prefix
(not (equal fill-prefix ""))
(not paragraph-ignore-fill-prefix)
(regexp-quote fill-prefix))
这是一个以 and 特殊形式为第一个元素的表达式。
如前所述(see The kill-new function),and 特殊形式会依次对每个参数求值,直到某个参数返回 nil,此时整个 and 表达式返回 nil;如果所有参数均未返回 nil,则返回最后一个参数的求值结果。(由于该结果非 nil,在 Lisp 中被视为真。)换句话说,and 表达式仅在所有参数均为真时返回真值。
在本例中,仅当以下四个表达式求值均为真(非 nil)时,变量 fill-prefix-regexp 才会绑定到非 nil 值;否则 fill-prefix-regexp 绑定为 nil。
fill-prefix对该变量求值时,会返回填充前缀的值(若存在)。若无填充前缀,该变量返回 nil。
(not (equal fill-prefix ""))该表达式检查已存在的填充前缀是否为空字符串(即不包含任何字符的字符串)。空字符串不是有效的填充前缀。
(not paragraph-ignore-fill-prefix)如果变量 paragraph-ignore-fill-prefix 被设为真值(如 t)而开启,该表达式返回 nil。
(regexp-quote fill-prefix)这是 and 特殊形式的最后一个参数。如果 and 的所有参数均为真,该表达式的求值结果会由 and 表达式返回,并绑定到变量 fill-prefix-regexp。
该 and 表达式成功求值的结果是:fill-prefix-regexp 会绑定到经 regexp-quote 函数处理后的 fill-prefix 值。regexp-quote 的作用是读取一个字符串,并返回一个能精确匹配该字符串且不匹配其他内容的正则表达式。这意味着,如果填充前缀存在,fill-prefix-regexp 会被设为能精确匹配该填充前缀的值;否则该变量设为 nil。
let* 表达式中的接下来两个局部变量用于从 parstart 和 parsep(分别表示段落起始和段落分隔的局部变量)中移除 ‘^’ 实例。随后的表达式再次设置 parsep,用于处理填充前缀。
这也是需要使用 let* 而非 let 定义的原因。if 的真假测试依赖于变量 fill-prefix-regexp 求值为 nil 还是其他值。
如果 fill-prefix-regexp 没有值,Emacs 会执行 if 表达式的 else 分支,并将 parsep 绑定到其局部值。(parsep 是一个匹配段落分隔内容的正则表达式。)
但如果 fill-prefix-regexp 有值,Emacs 会执行 if 表达式的 then 分支,并将 parsep 绑定到一个包含 fill-prefix-regexp 作为模式一部分的正则表达式。
具体来说,parsep 被设为原段落分隔正则表达式的值,与一个备选表达式拼接而成:该备选表达式由 fill-prefix-regexp 后跟行尾可选空白字符组成。空白字符由 "[ \t]*$" 定义。‘\\|’ 将这部分正则表达式定义为 parsep 的备选。
根据代码中的注释,下一个局部变量 sp-parstart 用于搜索;最后两个变量 start 和 found-start 被设为 nil。
现在我们进入 let* 的主体。主体第一部分处理函数接收负参数并因此反向移动的情况,我们略过这一节。