Flycheck 38:力量与魔法
摘要
Flycheck 38 是一个重大版本,增加了对语言服务器的内置 Eglot 支持、内联诊断注释以及应用快速修复代码操作的能力,复兴了 Emacs 的 linting 工具。
查看缓存全文
缓存时间: 2026/07/29 19:58
Flycheck 38:力量与魔法
飘荡者并非皆迷失。 —— J.R.R. 托尔金
今天我很高兴地宣布 Flycheck 38(https://github.com/flycheck/flycheck/releases/tag/v38.0)正式发布——这无疑是我接管项目以来最大、最具雄心的 Flycheck 版本。在这个版本中,Flycheck 为语言服务器提供了完整的支持方案,引入了现代编辑器所应具备的内联诊断功能,并且学会了应用修复——所有这一切都没有放弃让它最初广受欢迎的检查器模型。
我得承认,这个版本号本身配不上这次发布——它只是 37 之后的整数,“38” 这个数字本身一点也不“特别“。但版本号就是这样:有些版本比另一些更特别,而仅从数字上你几乎猜不到。
去而复返
简单回顾一下历史,因为这正是让我如此兴奋的原因。Flycheck 的诞生是为了给 Emacs 带来真正现代的代码检查体验,那时 Emacs 内置的选项——我们客气点说——乏善可陈。多年来,它一直是 Emacs 中实现实时检查的首选方式。但后来开发速度放缓,Flymake 被彻底重写,并在许多领域悄悄赶了上来——它最近甚至增加了行末诊断功能(https://emacsredux.com/blog/2026/07/20/flymake-can-now-show-diagnostics-at-the-end-of-the-line/)——我开始看到人们公开质疑 Flycheck 是否还能带来什么价值。
我理解这个问题——一个停滞不前的项目自然会招致这种质疑。但我从不相信 Flycheck 已经完成;它只是在休整。这次发布就是我的回答:证明它依然能够创新,依然能带来惊喜。而且我真心感谢竞争。Flymake 的奋起直追,恰恰促使我思考更大胆的方案,而不是再来一轮小修小补。这个领域的竞争是件好事,毫无疑问——它让两个项目都变得更好,而 Emacs 用户是最终的赢家。如果你好奇两者现在如何比较,手册中有诚实的对比(https://www.flycheck.org/en/latest/user/flycheck-versus-flymake.html)。
所以,本着勇敢迈向许多编辑器已先行之地的精神,以下是新功能。
Eglot 支持,终于内置了
多年来,Flycheck 与 LSP 之间的配合一直很尴尬。Eglot 通过 Flymake 呈现诊断信息,本身没有提供 Flycheck 后端,所以如果你想让 LSP 错误出现在 Flycheck 的错误列表中,就得借助第三方包 flycheck-eglot。这确实能用,但“再装一个包来桥接两个已经随 Emacs 一起发布的东西“这种事情,我一直不太认可。
现在 Flycheck 自己解决了这个问题。启用 global-flycheck-eglot-mode 后,由 Eglot 管理的缓冲区会通过 Flycheck 报告服务器的诊断信息——错误列表、导航、边缘区域,所有功能——而 Eglot 继续负责 LSP 的重活。这使得 flycheck-eglot 包变得过时。¹(https://emacsredux.com/blog/2026/07/29/flycheck-38/#fn:collision)
而且,由于 Flycheck 读取的是原始的 LSP 诊断信息,而不是经过 Flymake 扁平化处理后的内容,这个桥接层能够呈现 Flymake-over-Eglot 所丢弃的信息:LSP 的 quickfix 代码操作会变成 Flycheck 的修复,你可以用 C-c ! f 应用;诊断信息的关联位置也可以导航。
美观的内联诊断
我真正想要的另一项功能是内联消息——错误直接显示在它们所指的代码旁边,就像 VS Code 的 Error Lens、Neovim、Helix 和 Zed 那样。Flycheck 38 开箱即提供此功能。
flycheck-annotate-mode 的内联诊断
打开 flycheck-annotate-mode 即可获得此效果。有两种布局:紧凑型 eol 模式,消息显示在行末;宽敞型 below 布局,将完整消息单独显示在下方行中,并用连线指向出错的列。默认情况下,当前光标所在行使用宽敞布局,其余行保持紧凑,这样你正在处理的错误会完整显示,而其他行不会过度喧宾夺主。还有 Error Lens 风格的全行背景高亮,以及右对齐的 sideline 样式——手册(https://www.flycheck.org/en/latest/user/error-reports.html#inline-error-messages)中有每种样式的截图。
使用全局模式即可在任意位置启用:
(global-flycheck-annotate-mode 1)
;; 可选:Error Lens 风格的全行高亮
(setq flycheck-annotate-background t)
关于名称,还有一个小故事。我本想叫它 flycheck-inline-mode,但那个名字已被(现已弃用的)flycheck-inline 包占用。“Diagnostics” 是另一个明显的选择,但 Flycheck 一直称它们为errors(错误)而非 diagnostics(诊断)——flycheck-error、错误列表、flycheck-checkers——我不想为同一件事引入两套术语。所以最终定为 flycheck-annotate-mode。命名(依然)很难。
原生 LSP 支持,无需客户端
更精彩的是:Flycheck 现在可以直接与语言服务器通信——无需 Eglot、无需 lsp-mode、完全不需要完整的 LSP 客户端。
RuboCop 的 LSP 诊断通过原生 LSP 检查器呈现,支持修复
这是为了满足以下需求:越来越多的代码检查工具自带 LSP 服务器——RuboCop、Ruff、Biome、Harper 等——而传统的命令行检查器每次检查都会重新启动检查工具,反复支付启动成本(在 rubocop 加载大型项目时等待过的人都懂)。检查工具自带的 LSP 服务器保持常驻,增量地进行检查,因此检查速度快得多——而且你无需运行完整的语言服务器和客户端就能获得诊断信息。
启用 global-flycheck-lsp-mode,它即可开箱支持 RuboCop(Ruby)、Ruff(Python)、Biome(JavaScript、TypeScript、JSON、CSS)和 Harper(Markdown 散文)。每个服务器只在其程序已安装时才会启动,因此全局启用此模式是安全的。想要不同的工具?只需一行代码:
;; 对 Ruby 使用 Standard 替代 RuboCop
(setf (alist-get 'ruby-mode flycheck-lsp-servers) '("standardrb" "--lsp"))
整个功能构建在 Emacs 自己的 jsonrpc 库之上,因此没有新增依赖。而且,如果服务器提供了修复功能——RuboCop 的自动纠正、Ruff 和 Biome 的快速修复——这些也会作为 Flycheck 的修复呈现。该功能是为仅需诊断的场景设计的;当你需要带有补全和悬停提示的完整语言服务器时,通过 Eglot 运行它并使用 flycheck-eglot-mode。手册中有一个表格(https://www.flycheck.org/en/latest/user/syntax-checks.html#which-lsp-integration-should-i-use)帮助你选择。
项目级诊断
Flycheck 一直以来都是严格的缓冲区局部操作:它检查你正在查看的文件并显示这些文件中的错误。这在大多数情况下都很好,直到你想获得更全局的视图——所有打开文件中的问题,以及像 tsc、cargo check 和 mypy 等检查器确实知道的跨文件错误,但这些错误在逐缓冲区视图中被悄悄丢弃了。
在错误列表中按 P 键,它会切换到项目级范围:汇集所有打开的 Flycheck 缓冲区中的错误,那些跨文件诊断终于有了容身之处。这是 Flycheck 最接近“问题“面板的一次,而且它很快成为我日常使用中最喜欢的功能之一。
项目级范围的错误列表
注意其中 models.py 的类型错误:它是在检查 api.py 时报告的,旧的逐缓冲区视图会直接丢弃它。模式行中的 [project] 标签告诉你正在查看整个项目,而不是单个文件。
检查 TRAMP 远程文件
这个功能说起来很简单,而且坦白说早就该有了:Flycheck 现在可以在 TRAMP 上运行检查器。打开远程主机上的文件,检查器会在那里运行,在远程机器上,使用真实环境——而以前它干脆拒绝运行。如果你通过 TRAMP 在服务器或容器内编辑代码,仅此一项功能可能就值得升级。
还有更多
以上三个主要功能依赖于同一版本中落地的大量支持性工作。重点包括:
- Flycheck 现在可以应用修复。
C-c ! f(flycheck-fix-error-at-point)应用检查器建议的可自动应用的修复,C-c ! F一次性应用缓冲区中的所有修复,并作为一个可撤销的变更;可修复的错误会有一个独特的边缘指示器——一个“可修复“的小灯泡图标。eslint --fix、cargo clippy的建议、ruff、shellcheck以及任何基于 SARIF 的检查器都会携带这些修复,而 Flycheck 过去会解析后丢弃它们。 - 错误列表进行了重大改造:可以按文件、检查器或级别分组错误(并可组合这些维度),折叠组,并通过顶部的条带用鼠标驱动所有操作。
- 错误可以携带次要位置——重定义之前的定义位置、Rust 生命周期错误背后的借用位置。用
C-c ! j跳转到它们,它们会显示在消息之后的内联位置和错误列表中。 - 正在运行的检查会被新开始的检查中断(除非已经取得了实质性进展),因此慢速检查器不再让 Flycheck 感觉迟钝,错误列表也不会再每次光标移动时重新扫描所有行。
按文件和级别分组的错误列表
更多内容请查看更新日志(https://github.com/flycheck/flycheck/blob/master/CHANGELOG.md),但以上就是大致的模样。
整合在一起
如果你想要完整的现代配置——随处实时检查、内联诊断、LSP 服务器的诊断信息流经 Flycheck——以下是一个能全部开启的 use-package 配置块:
(use-package flycheck
:ensure t
:hook ((after-init . global-flycheck-mode)
;; 内联显示诊断信息,紧邻代码 (Error Lens 风格)
(after-init . global-flycheck-annotate-mode))
:config
;; 通过 Flycheck 报告 Eglot 的 LSP 诊断
(global-flycheck-eglot-mode 1))
更喜欢跳过 Eglot,直接从检查工具自带的 LSP 服务器(RuboCop、Ruff、Biome、Harper)读取诊断?将最后一行替换为 (global-flycheck-lsp-mode 1) 即可。当只需要诊断时使用原生检查器,当还需要完整语言服务器的补全和悬停提示时使用 Eglot 桥接。
升级
大多数配置将持续正常工作。需要注意一点:内置的 flycheck-eglot-mode 重用了旧第三方 flycheck-eglot 包的名称,因此如果你已安装该包,请先卸载它再依赖内置模式——否则两者会定义相同的命令并发生冲突。
关于资金支持的一点话
让我稍微直言不讳地说几句。在我维护 Flycheck 的这两三年里,它的 OpenCollective(https://opencollective.com/flycheck)收到的捐款总计不到 300 美元。个人捐赠也没有带来多大变化。我这么说不是为了让人内疚;这只是开源的日常现实。流行的项目工作量很大,而通常的回报是使用自己帮助改进的东西所带来的平静满足感。
不过,如果 Flycheck 为你节省了时间——尤其是在工作中——请考虑通过 OpenCollective(https://opencollective.com/flycheck)、GitHub Sponsors(https://github.com/sponsors/bbatsov)或 README 中链接的其他选项(https://github.com/flycheck/flycheck#sponsoring)提供支持。即使是小额定期捐赠,如果乘以每天依赖这个项目的用户数量,也会对我能投入多少时间产生实质性的影响。
结语
即使是最渺小的人也能改变未来的走向。 —— 加拉德瑞尔
当我接管 Flycheck 时,我的动机很简单:保持项目存活,并维持 Emacs 这一领域的竞争。很长一段时间里,这几乎只意味着保持基本运转。这次发布是许久以来它第一次真正地向前迈出了一大步,我对最终成果感到自豪——现代的诊断用户体验、双管齐下的一流 LSP 支持、项目级诊断和修复功能,同时始终忠于人们喜爱的检查器模型。姑且称之为绝地归来。我希望你会认为,我在追求的目标上至少取得了适度的成功。
衷心感谢所有测试、报告和推动这次发布的人。试试 flycheck-annotate-mode 和 flycheck-lsp-mode,告诉我你的想法。
继续黑客!
相似文章
clj-refactor.el 4.0
clj-refactor.el 4.0,近五年来首个主要版本,引入了热依赖加载、异步重构、差异预览、瞬态菜单以及众多针对Emacs中Clojure开发的改进。
TeXFix-Bench: An Empirically Grounded Multi-Format Benchmark for LLM-Based Document Source Repair
Presents TeXFix-Bench, a multi-format benchmark for LLM-based document source repair, grounded in a mined fault taxonomy across LaTeX, Typst, and Markdown. Benchmarks seven LLMs with 48,651 attempts, showing compile success alone overstates repair quality.
Racket v9.3
Racket v9.3 已发布,包含多项新功能,包括 Markdown 文档生成、改进的教学语言,以及增强的包管理和 FFI 支持。
逃离 IntelliJ:在 Emacs Eglot 上使用 Scala 和 Kotlin 的 LSP
一份关于配置 Emacs Eglot 用于 Scala 和 Kotlin 开发的详细指南,提供了 IntelliJ IDEA 的轻量级替代方案,并包含自定义 LSP 配置。
Hyperpolyglot Lisp:Common Lisp、Racket、Clojure、Emacs Lisp
一份对照参考表,比较Common Lisp、Racket、Clojure和Emacs Lisp的语法与特性。