返回资讯中心
外部精选
软件工程
#开发者实时流

Sift 发布:为 Scheme、Emacs Lisp 和 Common Lisp 提供 LSP、lint 与格式化

来源标题:Sift: LSP, Lint, Format Lisp, Scheme and Emacs Lisp

Sift 是一个面向 Scheme、Emacs Lisp 和 Common Lisp 的幂等格式化器、规则式 linter 和语言服务器。它按各方言自身规则读取代码,格式化后的文件再次格式化保持不变,注释和读取期语法保持原位置。项目用 Guile 3.0 实现,可通过 guix install sift 或 nix run 安装,并提供 sift lsp 语言服务器模式。

Hacker News 最新jjba232026/11/11 · 今天30 分钟阅读

以下正文由 AI 翻译自 Hacker News 最新(原文版权归原站所有),可能存在偏差,重要内容请以原文为准。

一个幂等的格式化器、一个基于规则的 linter,以及一个面向 Scheme、Emacs Lisp 和 Common Lisp 的语言服务器。

sift 读取 Lisp 源文件,报告其中不合规范之处,并重写其空白。这种重写是文本上的全函数,也是一个不动点:对已格式化的文件再次格式化会原样返回,而注释、空行、#; 数据注释或 #. 读取期形式在输出中出现的位置与输入中一致。这两个性质在套件中的每个 fixture 上都有断言,因此它们不是会悄悄失效的承诺。

三种方言各自按其自身规范读取,而不是在它们之间取折中:Scheme、Emacs Lisp 和 Common Lisp。方括号在其中一种里是向量,在另一种里是子句,sift 不会把两者平均掉。

用 guix install sift 或 nix run 安装,或者克隆仓库并运行 ./bin/sift version。实现基于 Guile 3.0 和标准库,没有 C 依赖,也没有构建步骤,因为 Scheme 程序本就不需要;sift doctor 会报告它运行所在的解释器、加载的库以及找到的规则。

同一个程序也是语言服务器(sift lsp)。发现的问题、修复和格式化只计算一次,然后要么作为标准输出上的文本,要么作为 LSP 响应交付,因此通过 Eglot 使用 Emacs、Neovim 以及该协议的任何其他客户端所收到的内容,与流水线收到的完全一致。

它以单个可执行文件和一个 Emacs 包的形式发布,采用 MIT 许可证。前提很普通:Lisp 程序是带有读取语法的数据,把它当作数据对待的格式化器可以同时做到精确和廉价。

检查、格式化、lint。sift check 打印它将做的补丁并以 1 退出;sift format 打印格式化后的文件;sift lint 指出触发的规则、其严重级别和位置。编辑器和流水线收到同样的答案。那段录制中的每一行都是真实输出,所涉及的文件小到可以在图中读完。

Lisp 学习者。每种方言按自身规范读取,因此学习者面前的工具执行的是所学语言的约定,而不是格式化器发明的第四种约定。

Guile 和 Guix 代码库,其 cond 子句用方括号书写,其构建文件是 gexp,它们想要一个与隔壁的树一致的格式化器,而不是重新决定它。

Emacs 包作者需要在 Flymake 和 CI 中得到 docstring、lexical-binding、interactive 和 provide 规则,且来自一个程序而不是三个。

Common Lisp 项目中,包前缀、LOOP 关键字风格和悬空括号是约定,应被保留而不是规范化掉。

多语言 Lisp 仓库,得到一个可执行文件、一个配置文件和一步 CI,而不是各三份。

编辑器和工具作者,把发现的问题当作数据:--format=json、--format=sarif、GitHub 注解,或 sift lsp。

无损重写是整个论证的核心,其余由此推出:diff 的 hunk 就是改动本身,修复只替换一个范围,规则可以在不编辑它所反对的代码的情况下被静默。

字节级精确保留。注释、空行、#; 数据注释、#. 读取期形式和 #<...> 对象完全按读取时的样子返回。格式化是不动点:第二次运行不改变任何东西。套件在每个 fixture 上断言保留性和幂等性。

方言是被判定的,不是被假设的。三种方言,一个程序:字符字面量、读取宏、#| ... |# 块注释、#; 数据注释、包前缀、LOOP 和 docstring 约定遵循方言自身的规范,而不是折中。方括号也是如此——它在 Emacs Lisp 中是向量,在 Scheme 中是子句;#{ ... }# 也是如此——它是符号,且只有 Guile 读取。Guile 是 sift 所写所用的方言,也是其读取器最熟悉的方言;另外两种按其规范读取。

三项工作,三条命令。格式化、lint 和修复在设计上是分开的:format 打印,--write 写入,lint 报告,lint --fix 修复,ci 在一次遍历中同时提出格式化问题和 lint 问题,而不触碰目录树。

九十二条规则,每条都有其理由。每条规则都有一个名称、一个稳定的代码(SIFT012)、一句话、适用的方言、一个示例以及自己的测试文件。sift explain RULE 从程序中读出推理,doc/rules.org 由规则记录生成,因此文档不会偏离它所描述的代码。

拒绝猜测的修复。一条规则的修复恰好替换一个范围,如果一次运行的所有修复合在一起会引入解析错误,则全部丢弃。不发明分隔符,不重命名标识符。

抑制是一条注释,且留在代码所在之处。;; sift:disable-next-line keyword-arguments 为一行关闭一条规则,disable 关闭它所在形式的其余部分,enable 将其重新打开。抑制按设计是面向行且限定于形式的:配置文件中的例外列表是一份没有审查会触及的列表,而范围注释会比它所针对的代码活得更久。

一个语言服务器,所有编辑器。sift lsp 通过标准输入输出讲 LSP:诊断、快速修复和格式化,三种方言的转到定义和引用——包括标准库及其已安装包定义的名称、实现为其编译进来的名称所携带的文档,以及大多数 Emacs 安装附带的压缩源码——大纲、补全、命名调用参数的 inlay hints、描述符号或其下规则的悬停、导入整理、保持模块同步的文件重命名,以及缓冲区保存时来自方言自身编译器的第二意见。

面向人、编辑器和流水线的输出。:make 和 compilation-mode 解析的编译风格行,面向需要结构的编辑器的 JSON,

(原文较长,此处译出前 6000 字,完整内容请点「打开原文」。)

正文由 探How 从来源站点 RSS 同步,内容未经改写;遇到排版缺失或需要图片、视频时请以原文为准。

打开原文
去社区讨论 →