用 Scheme 给 Helix 写 Wakatime 插件
我日常用的编辑器是 Helix。我从来没深入用过 Vim,所以转到 Helix 并没有什么适应成本——反而更自然:Helix 开箱即用的体验很好,而且"先选择再操作"(select-then-act)的编辑模型比 Vim 的"先动词再名词"对我来说更直觉。 Helix 主线目前没有插件系统,这一直是社区里最大的痛点之一。不过 mattwparas 维护了一个 fork(steel-event-system 分支),集成了 Steel——一个嵌入式 Scheme 实现——作为插件运行时。Scheme 是 Lisp 的一个方言,而 Steel 是 Scheme 的一个方言,所以你大概能猜到写插件的体验是什么画风:很多括号。 我在 AUR 上打包了这个 fork:helix-steel-git,Arch 用户可以直接装。 其实我很早就知道这个 fork 的存在,但一直没有真正去用——原因很简单,我是真的怕括号。然而最近我彻底抛弃了 VSCode,全面转向 Helix 作为唯一的编辑器,随之而来的问题是:对插件的需求越来越强烈。而 Steel 生态目前能用的插件屈指可数,想要什么功能基本只能自己动手。于是我终于硬着头皮开始写 Scheme 了。 第一版:让 LLM 来 第一步是从社区已有的插件里复制了一套模板,建好 wakatime.hx 仓库,然后直接把需求丢给 Claude,让它生成第一版代码。毕竟我对 Lisp 一无所知,连基本语法都得现查。 Claude 确实给了我一个能跑的版本。问题是——LLM 生成的 Lisp 代码,人类读起来比手写的还要痛苦。比如获取编辑器版本号这段: (define (editor-version) (if *wakatime-editor-version* *wakatime-editor-version* (begin (let ([spawned (spawn-process (with-stdout-piped (command "hx" (list "--version"))))]) (if (Ok? spawned) (let ([output (wait->stdout (Ok->value spawned))]) (if (Ok? output) (let ([version (extract-version (Ok->value output))]) (set! *wakatime-editor-version* version) version) "unknown")) "unknown"))))) 嵌套的 if-let-if-let-if,层层剥洋葱式地处理 Ok? 结果——典型的"LLM 不知道这门语言的惯用写法,只好用最保守的方式堆逻辑"。能跑,但读起来让人头大。 ...