简介:这份资源是面向 Agda 语言开发者与函数式编程学习者的 VS Code 扩展,用于在编辑器内获得接近 Emacs 的 Agda 交互体验,解决在 VS Code 中编写、加载与类型检查 Agda 代码的痛点。压缩包共 179 个文件,约 457KB,以 67 个 js 与 64 个 res 源码文件为主体,辅以 13 个 out、13 个 in 测试用例、5 个 json 配置、4 个 agda 示例及 less、yml、ttf 等资源,结构上兼顾扩展实现、语言服务器适配与回归测试。已有 236 人学习下载。内容覆盖按键映射说明、归一化级别命令、Agda 语言服务器 LSP 支持以及 CaseSplit、QuotationMark、InputMethod 等示例文件,读者可据此理解扩展的加载流程、命令前缀替换规则与测试组织方式,适合希望迁移 Emacs 工作流或深入定制 Agda 编辑环境的中高级用户参考。
1. 在 VS Code 里写 Agda:这个扩展到底解决了什么痛点
如果你写过 Agda,大概率经历过这样的割裂:一边是 Emacs 里 agda-mode 那套顺滑的交互式证明开发,C-c C-l加载、C-c C-,看目标类型、C-c C-r精化,另一边是团队里其他人都在用 VS Code 写 TypeScript、ReScript、ReasonML,你不想为了一个语言单独开一个编辑器。agda-mode-vscode 就是冲着这个场景来的——它把 Agda 的交互式开发能力搬进了 VS Code,让你在同一个窗口里既能写前端代码,又能做依赖类型的形式化验证。这个扩展适合已经装好 Agda 编译器、想在 VS Code 里获得接近 Emacs agda-mode 体验的开发者,也适合那些被「VS Code 能不能写 Agda」这个问题卡住、搜了半天只看到零散配置片段的人。它不是一个独立语言服务器,而是对 Agda 自带交互协议的一层封装,理解这一点,后面很多行为就说得通了。
2. 装之前先搞清楚:agda-mode-vscode 的运行链路与依赖
2.1 它不是语言服务器,而是 Agda 交互命令的转发层
很多人第一次搜 agda-mode-vscode,会默认它像 rust-analyzer 或 clangd 那样自带一个完整的语言服务器。实际不是。Agda 本身提供了一个--interaction模式,通过标准输入输出收发命令,agda-mode-vscode 做的事情是:在 VS Code 里启动一个 Agda 进程,把你在编辑器里的操作翻译成 Agda 能识别的命令,再把返回结果渲染成目标类型、上下文、错误信息这些面板内容。所以你的机器上必须先有一个能跑的 Agda 可执行文件,扩展本身不包含编译器。
这个链路决定了三件事。第一,Agda 的版本会直接影响扩展行为,不同版本的交互协议有细微差异。第二,如果 Agda 不在 PATH 里,扩展启动时会报找不到命令,而不是给你一个友好的安装引导。第三,所有证明检查的耗时都发生在 Agda 进程里,VS Code 只是前端,卡顿的时候不要怪编辑器。
常见做法是先用包管理器装好 Agda,确认命令行能跑,再装扩展。我一般会先执行agda --version,看到版本号再往下走。
2.2 安装 Agda 编译器的几种路径与选择理由
Agda 的安装方式主要有三种:Haskell 工具链(cabal 或 stack)、系统包管理器、以及预编译二进制。如果你已经在用 Haskell,cabal 是最自然的选择,版本可控,能和库一起管理。系统包管理器(比如 apt、brew)胜在快,但版本往往偏旧,可能和扩展期望的协议对不上。预编译二进制适合不想碰 Haskell 工具链的人,但要注意平台匹配。
下面是我在 Linux 上常用的 cabal 安装流程,其他平台把包管理器命令换掉即可:
# 更新 cabal 包索引 cabal update # 安装指定版本的 Agda,这里以 2.6.4 为例 cabal install Agda-2.6.4 # 确认可执行文件在 PATH 中 which agda agda --version逻辑说明:cabal update拉取最新的包描述,cabal install会编译并安装 Agda 及其依赖。参数上,Agda-2.6.4是版本约束,你可以换成自己需要的版本,但建议和团队统一,否则交互协议差异会导致扩展行为不一致。which agda用来确认安装路径已经进入环境变量,如果这里为空,后面扩展一定找不到。
提示:Windows 上用 cabal 安装 Agda 时,编译时间可能很长,建议预留足够磁盘空间和耐心,或者改用预编译包。
2.3 在 VS Code 中安装扩展并完成首次加载
Agda 就绪后,在 VS Code 扩展面板搜索 agda-mode-vscode 安装即可。安装完不要急着写代码,先做一次最小验证:新建一个.agda文件,写一个最简模块,然后触发加载命令。
-- test.agda module test where -- 定义一个最简单的类型 data Bool : Set where true : Bool false : Bool保存后,用命令面板(Ctrl+Shift+P)执行Agda: Load。如果一切正常,底部状态栏会显示加载成功,光标停在Bool上时能看到类型信息。如果报错,先看输出面板里 Agda 进程的原始输出,那里会写清楚是找不到可执行文件还是语法错误。
参数方面,扩展通常会自动探测 PATH 里的agda。如果你的 Agda 装在非标准路径,需要在 VS Code 设置里搜agda-mode,找到可执行文件路径那一项手动填绝对路径。这一步是新手最容易翻车的地方:明明终端里agda能跑,扩展却报找不到,原因就是 VS Code 启动时的环境变量和你的 shell 不一致。
3. 把交互式证明开发跑起来:加载、目标查看与精化
3.1 加载与错误定位:C-c C-l 的等价操作
Emacs 里 agda-mode 的核心快捷键是C-c C-l,在 VS Code 里对应命令面板的Agda: Load,也可以自己在键位设置里绑一个顺手的组合。加载的作用是让 Agda 解析当前文件、类型检查、并把所有交互信息注册进来。没有加载之前,查看目标类型、精化这些操作都不会有反应。
加载失败时,Agda 会在文件里标出错误位置,同时在输出面板打印完整信息。常见的错误分两类:语法错误和类型错误。语法错误通常指向解析失败的行,类型错误会告诉你期望类型和实际类型。这里有个血泪经验:Agda 的错误信息有时候会指向一个不太直观的位置,尤其是涉及隐式参数的时候,不要只盯着高亮行,往上往下多看几行。
module test where open import Data.Nat using (ℕ; zero; suc) -- 故意写一个类型错误的函数 bad : ℕ → ℕ bad x = true -- 这里会报错:true 不是 ℕ加载这段代码,Agda 会明确告诉你true的类型Bool和期望的ℕ不匹配。这个反馈链路是后面所有交互操作的基础,先确保它能正常工作。
3.2 查看目标与上下文:理解 Agda 的证明状态
写证明的时候,最常用的操作是查看当前目标的类型和上下文里有哪些变量可用。在 Emacs 里是C-c C-,,VS Code 里通过命令面板执行Agda: Goal Type and Context。这个操作会把光标所在位置的洞(hole)的目标类型和上下文变量列出来。
module test where open import Data.Nat using (ℕ; zero; suc) open import Data.Bool using (Bool; true; false) -- 一个带洞的函数,用来观察目标 plus : ℕ → ℕ → ℕ plus x y = {! !}把光标放在{! !}里,执行查看目标,你会看到目标类型是ℕ,上下文里有x : ℕ和y : ℕ。这个信息决定了你下一步能做什么:可以用x、y,也可以对它们做模式匹配。参数上,洞的写法是{! !},两个花括号加感叹号,中间可以写内容作为提示,Agda 会忽略里面的文字但保留位置。
理解上下文和目标的关系,是写 Agda 证明的基本功。很多人卡住不是因为不会语法,而是没养成先看目标再动手的习惯。
3.3 精化与自动求解:C-c C-r 和 C-c C-a 的落地用法
精化(refine)对应Agda: Refine,作用是让 Agda 根据当前目标自动补一个最外层的构造子,并把子目标留成新的洞。自动求解(auto)对应Agda: Auto,会尝试用一组内置策略直接填满当前洞。
module test where open import Data.Nat using (ℕ; zero; suc; _+_) -- 用精化来逐步构造 plus : ℕ → ℕ → ℕ plus zero y = y plus (suc x) y = suc (plus x y)如果你在plus x y = {! !}上执行精化,Agda 会提示你对x做模式匹配,生成zero和suc两个分支。这就是交互式开发的节奏:看目标、精化、填洞、再加载。自动求解适合那些结构简单、库里有现成引理的洞,但它不是万能的,复杂证明还是得手动来。
参数上,精化和自动求解都依赖当前加载的模块和已导入的库。如果库没导入,自动求解能用的引理就少,成功率会明显下降。所以写证明前先把需要的模块open import进来,是个好习惯。
4. 避坑与排查:agda-mode-vscode 最常见的五类问题
4.1 扩展报找不到 agda 可执行文件
现象:安装完扩展,一加载就提示找不到agda命令,但终端里agda --version正常。
原因:VS Code 启动时继承的环境变量和你的交互式 shell 不一致,尤其是用 nvm、asdf 或自定义 PATH 的情况,GUI 启动的 VS Code 拿不到你 shell 里配置的路径。
解决:在 VS Code 设置里搜agda-mode,找到可执行文件路径配置项,填agda的绝对路径。用which agda拿到路径,粘进去,重启 VS Code。
4.2 加载成功但目标查看无反应
现象:文件能加载,状态栏也显示成功,但光标放在洞里执行查看目标,没有任何输出。
原因:光标不在洞里,或者洞的写法不规范。Agda 只认{! !}这种形式,写成{ }或{- -}都不会被识别为交互洞。
解决:确认洞的写法,把光标放在两个感叹号之间。如果还是不行,重新加载一次文件,有时候扩展状态和 Agda 进程会不同步。
4.3 Agda 版本与扩展协议不匹配
现象:加载时报一些看不懂的协议错误,或者某些命令行为异常。
原因:Agda 的交互协议在不同版本间有变化,扩展可能针对某个版本区间做了适配,版本太新或太旧都会出问题。
解决:查扩展的说明,确认它支持的 Agda 版本范围,把 Agda 降到或升到匹配的版本。团队协作时统一版本,能省掉很多这类玄学问题。
4.4 大文件加载慢到怀疑人生
现象:文件一大,每次加载要等很久,改一行重新加载更是煎熬。
原因:Agda 的类型检查是全局的,加载会重新检查整个文件,文件越大越慢。这是 Agda 本身的性质,不是扩展的锅。
解决:把大文件拆成多个模块,用open import组织,改哪个模块加载哪个。另外,Agda 有缓存机制,但需要正确配置,具体看你的安装方式。
4.5 中文注释或特殊字符导致解析异常
现象:文件里写了中文注释,加载时报解析错误,位置指向注释附近。
原因:Agda 对源文件的字符编码有要求,默认应该是 UTF-8。如果文件保存成了其他编码,或者编辑器插入了一些不可见字符,就会出问题。
解决:确认文件编码是 UTF-8,VS Code 右下角可以看到。如果是从别处复制来的代码,用「显示所有字符」检查有没有零宽字符,有就删掉。
5. 进阶技巧:把 agda-mode-vscode 用出接近 Emacs 的效率
5.1 自定义键位绑定,减少命令面板依赖
命令面板能用,但写证明时频繁打开面板会打断节奏。VS Code 的键位设置里可以给 Agda 命令绑快捷键,我一般会把加载、查看目标、精化、自动求解这四个绑到和 Emacs 接近的组合上,肌肉记忆能直接迁移。
// keybindings.json 片段 [ { "key": "ctrl+c ctrl+l", "command": "agda-mode.load", "when": "editorLangId == agda" }, { "key": "ctrl+c ctrl+,", "command": "agda-mode.goal-type-context", "when": "editorLangId == agda" }, { "key": "ctrl+c ctrl+r", "command": "agda-mode.refine", "when": "editorLangId == agda" }, { "key": "ctrl+c ctrl+a", "command": "agda-mode.auto", "when": "editorLangId == agda" } ]逻辑说明:when条件限定只在 Agda 文件里生效,避免和其他语言的快捷键冲突。命令名以扩展实际注册的为准,不同版本可能有细微差异,可以在键位设置里搜agda确认。参数上,ctrl+c ctrl+l这种两段式组合在 VS Code 里是支持的,写起来和 Emacs 的C-c C-l对应。
5.2 用洞和精化做增量式证明开发
写复杂证明时,不要试图一次写完整。我的习惯是先把函数签名和大致结构写出来,在需要证明的地方留洞,加载,然后逐个洞看目标、精化、填。这样每次加载的检查范围可控,出错也容易定位。
module test where open import Data.Nat using (ℕ; zero; suc; _+_) open import Data.Nat.Properties using (+-comm) -- 增量式开发:先留洞,再逐个填 lemma : ∀ (a b : ℕ) → a + b ≡ b + a lemma a b = {! !}加载后看目标,发现就是+-comm的类型,直接填+-comm a b即可。这个流程的关键是:洞不是失败,而是开发状态的一部分。Agda 允许带洞加载,只要洞不影响其他部分的类型检查。
5.3 验证扩展是否正常工作的一套检查清单
装完扩展、配好环境后,我一般会走一遍这个清单,确认链路是通的:
| 检查项 | 操作 | 预期结果 |
|---|---|---|
| Agda 可执行 | 终端执行agda --version | 输出版本号 |
| 扩展识别 | 打开.agda文件,看状态栏 | 显示 Agda 相关状态 |
| 加载功能 | 命令面板执行Agda: Load | 无错误,状态栏提示成功 |
| 目标查看 | 洞里执行查看目标 | 输出目标类型和上下文 |
| 精化功能 | 洞里执行精化 | 生成构造子或提示模式匹配 |
| 错误反馈 | 故意写类型错误,加载 | 标出错误位置和类型信息 |
这套清单走完,基本能确定扩展和 Agda 的配合没问题。后面遇到奇怪行为,也可以回到这个清单逐项排查,比盲目搜问题高效得多。
从那以后我每次在新机器上配 Agda 环境,都强制走一遍这个清单,确认每一步都有反馈再开始写证明。希望帮到你。
本文还有配套的精品资源,点击获取