基于提供的语法,SMIE 无需额外配置即可实现自动缩进。 但在实际使用中,这种默认缩进风格往往不够理想, 你通常需要在多种场景下对其进行微调。
SMIE 缩进的设计理念是:缩进规则应尽可能**局部化**。 为此,它引入了 虚拟缩进 的概念, 即某一程序位置若位于行首时应具有的缩进量。 当然,如果该位置确实在行首,其虚拟缩进就是当前缩进值; 否则 SMIE 会通过缩进算法计算该位置的虚拟缩进。
在实际使用中,某程序点的虚拟缩进不必与在其前插入换行后的缩进完全一致。
举个例子,C 语言中 { 之后的 SMIE 缩进规则并不关心该 { 是单独占一行,还是位于上一行末尾。
这些不同情况会由 { 之前的缩进规则统一处理。
另一个重要概念是 父节点(parent):
一个标记的 父节点,是包裹它的最近语法结构的起始标记。
例如,else 的父节点是其所属的 if,
而 if 的父节点则是外层语法结构的起始标记。
命令 backward-sexp 可从一个标记跳转到其父节点,但有几点注意事项:
对于 起始标记(openers) (开启一个结构的标记,如 if),
需要将光标置于标记之前;
对其他标记则需将光标置于标记之后。
若父节点是当前标记的 起始标记(opener),backward-sexp 会将光标停在父节点之前,
否则停在父节点之后。
SMIE 缩进规则通过一个函数指定,该函数接收两个参数 method 和 arg,其中 arg 的含义与函数返回值由 method 决定。
method 可取以下值:
:after:此时 arg 为一个标记,函数应返回在 arg 之后进行缩进所用的偏移量 offset。
:before:此时 arg 为一个标记,函数应返回缩进 arg 自身所用的偏移量 offset。
:elem:函数应返回缩进函数参数所用的偏移量(若 arg 为符号 arg),
或基础缩进步长(若 arg 为符号 basic)。
:list-intro:此时 arg 为一个标记,若该标记后跟随一组表达式列表(无分隔符)而非单个表达式,
函数应返回非 nil。
当 arg 为标记时,函数被调用时光标位于该标记之前。
返回值为 nil 始终表示回退到默认行为,
因此函数对不处理的参数应返回 nil。
offset 可取以下值:
nil:使用默认缩进规则。
(column . column):缩进至指定列 column。
:after 而言基准是当前标记,对 :before 而言基准是其父节点。