添加缩进查询

Helix 使用 tree-sitter 来正确缩进新行。这需要一个 tree-sitter 语法和一个 indent.scm 查询文件,该文件放在 runtime/queries/{language}/indents.scm 下。

行的缩进是通过从新行开头的最低节点遍历语法树来计算的。当被查询捕获时,这些节点中的每一个都构成了总缩进(以何种方式取决于捕获的名称)。

请注意,这些添加的缩进从哪里开始很重要。例如,从同一行开始增加多个缩进级别只会使总缩进级别增加 1。

作用域

添加的缩进并不总是应用于整个节点。例如,在大多数情况下,当一个节点应该缩进时,我们实际上只希望缩进除第一行以外的所有内容。

为此,存在几个作用域(如果需要,将来可能会添加更多作用域):

  • all:适用于捕获的整个节点。只有当捕获的节点是这行的第一个节点时,这与 tail 不同。
  • tail:适用于捕获节点的第一行以外的所有内容。

每种捕获类型都有一个默认作用域,该作用域在大多数情况下都应该是正确的。当需要不同的作用域时,可以在模式中的任何位置使用 #set! 声明来更改:

(assignment_expression
  right: (_) @indent
  (#set! "scope" "all"))

捕获类型

  • @indent:默认作用域为 tail;缩进级别加 1;同一行多次出现时不叠加。如果同一行上至少捕获一个 @indent 和一个 @outdent,则缩进级别根本不会改变。
  • @outdent:默认作用域为 all;缩进级别减 1,规则与 @indent 相同。
  • @extend:将该节点的范围扩展到行尾,以及缩进超过该节点所在行的行。这对于像 Python 这样的语言很有用,在这种语言中,出于缩进的目的,一些节点(如函数或类)还应该包含紧跟在它们后面的缩进行。
  • @extend.pre-once:阻止该节点的祖级节点的第一次扩展。例如,在 Python 中,返回表达式总是结束它所在的块。注意,这只会停止下一次 @extend 捕获的扩展。如果捕获了多个祖级,则仅阻止最里面的祖级的扩展。所有其他祖级都不受影响(无论最里面的祖先是否实际上已经被扩展)。

谓词

在某些情况下,S-表达式不能准确表达应该匹配什么模式。为此,tree-sitter 程序允许谓词 (predicates) 出现在模式中的任何位置,类似于 #set! 声明的工作方式:

(some_kind
  (child_kind) @indent
  (#predicate? arg1 arg2 ...)
)

参数的数量取决于使用的谓词。每个参数要么是一个捕获(如 @name),要么是一个字符串(如 "某个字符串")。tree-sitter 支持以下谓词:

  • #eq?/#not-eq?:第一个参数(捕获)必须/不能等于第二个参数(捕获或字符串)。
  • #match?/#not-match?:第一个参数(捕获)必须/不能与第二个参数(字符串)中给出的正则表达式匹配。

此外,我们还支持一些用于缩进查询的自定义的谓词:

  • #not-kind-eq?:第一个参数(捕获)的类型不能等于第二个参数(字符串)。
  • #same-line?/#not-same-line?:两个参数的捕获必须/不能从同一行开始。