Sift 通过为 Scheme、Emacs Lisp 和 Common Lisp 提供幂等格式化程序、基于规则的 linter 和语言服务器 (LSP),为 Lisp 开发提供了一种统一的方法。通过将 Lisp 源代码视为形式树而非文本行序列,该工具确保格式化保持为“固定点”,即重复应用不会改变输出。

此动画展示了 Sift 作为格式化和 Linting 统一工具链的核心功能。
特定方言逻辑
Lisp 工具链面临的一个主要挑战是,不同的方言使用相同的字符用于不同的目的。例如,方括号在一种方言中可能代表向量,而在另一种方言中可能代表子句。Sift 通过为每个方言使用特定的报告来避免在这些规则之间做出折中。
这种方法确保在特定项目(如 Guile 和 Guix 代码库或 Emacs 软件包开发)上工作的学生和开发人员获得的格式符合他们特定的语言惯例,而不是通用的标准。
“一个 Lisp 程序是一个具有读取语法的资料,而将它视为这样的格式化程序可以同时做到精确且廉价。”
— GitHub - jjba23/sift: Lisp, Scheme and Emacs Lisp formatter, linter and LSP (language server)
无损重写的机制
Sift 的操作前提是 Lisp 程序是一个具有读取语法的资料。通过将文件读取为携带源位置的形式树,该工具可以执行关于注释和特殊形式的“字节精确”重写。

此代码列表提供了 Sift 在不同方言中处理的 Lisp 语法的具体示例。
由于读取器和打印器是逆操作,Sift 可以在移动空白的同时确保底层结构保持不变。这使得该工具能够提供差异(diff),其块(hunks)是具体的更改,使其适用于人工审查和自动化流水线。
工具化与集成
该项目作为单个可执行文件和 Emacs 软件包发布,在不同编辑器中提供一致的体验。它支持各种输出格式,包括用于结构化数据的 JSON、用于代码扫描的 SARIF 2.1.0 以及用于拉取请求的 GitHub 注释。
语言服务器 (sift lsp) 为所有三种方言提供诊断、快速修复和格式化。它还提供“跳转到定义”、内联提示和文件重命名等功能,并在可用时使用该方言自身的编译器作为“第二意见”。
局限性与安全性
Sift 优先考虑正确性而非激进的自动化。“fix”命令仅应用被声明为等价的更改,并将丢弃任何可能引入解析错误的修复。它还避免重命名标识符或发明新的分隔符。
虽然该工具提供了 92 条具有明确依据的规则,但当前文档中未提供针对大规模仓库的具体性能基准。用户可以使用 “sift explain” 命令查询任何特定规则的推理。