终极REPL调试工具scope-capture:如何把代码运行时上下文"时光倒流"带回你的REPL
【免费下载链接】scope-captureProject your Clojure(Script) REPL into the same context as your code when it ran项目地址: https://gitcode.com/gh_mirrors/sc/scope-capture
scope-capture是一款面向 Clojure 与 ClojureScript 开发者的轻量级 REPL 调试库,它的核心能力是:在代码运行的"瞬间"给局部变量拍一张快照,然后随时把这份运行时上下文"时光倒流"式地搬回你的 REPL 中自由验证。你再也不需要手动伪造请求、数据库连接或层层嵌套的局部值——让程序自己跑一遍,调试现场就完整还原了。🕰️
它解决什么痛点:为什么需要"上下文快照"
Clojure 社区推崇的 REPL 调试法("无堆栈跟踪调试")依赖两步:
- 重建代码运行时的局部环境(用
def把局部变量搬出来) - 在 REPL 里求值代码缓冲区的表达式,逐步缩小问题范围
听起来简单,但第一步常常让人崩溃:变量越多越嵌套,手动"复刻"这些值既繁琐又容易出错——尤其是 Web 服务里那些难以凭空构造的 HTTP 请求、数据库连接。
scope-capture 的价值主张就是:要复现某段代码的运行时上下文,你只需要让它真正执行一次(不一定从 REPL 启动),其余的一切由它代劳。这带来三个直接好处:
- 🐛调试更轻松:跳过重建环境的时间,直奔 Bug 本身
- 🚀新人上手更快:刚接手项目(甚至是刚学 Clojure)的人,也能机械地、零背景知识地恢复任意代码片段的环境
- 🧪探索成本更低:跑个一次性的变体查询、理解项目数据流,都变得随手可得
快速上手:3步把运行时上下文搬进REPL
第1步:安装到本地开发环境(最快配置方法)
建议不要把 scope-capture 写进项目依赖,而是放进你的本地开发环境,这样它永远不会进入生产构建:
- Leiningen:在
~/.lein/profiles.clj的:user配置中加入依赖[vvvvalvalval/scope-capture "0.3.3"],并添加:injections [(require 'sc.api)] - Boot:在
~/.boot/profile.boot中conj同样的依赖并(require 'sc.api)
版本号可在 project.clj 中确认,发布记录见 CHANGELOG.md。
第2步:用 spy 给代码"装探针"
在你怀疑有 Bug 的表达式外面包一层sc.api/spy宏即可:
(sc.api/spy (- v b))编译时会看到类似日志:At Code Site -3, will save scope with locals [a b x y z u v]。然后正常运行程序——每当这行代码执行,它所在位置的全部局部变量就会被自动存档,日志会给出一个"执行点"编号(如SPY [7 -3] ...)。
💡 小技巧:
spy也可以不包裹任何表达式,单独写(sc.api/spy)就能只记录环境,侵入性更低,详见 doc/Tips-and-Tricks.md。
第3步:用 defsc / letsc 一键复原现场
在 REPL 里输入快照对应的编号,整个运行时环境就"活"了过来:
(sc.api/letsc 7 [a b u v x y z]) => [23 26 4 13 3 4 5]或者用defsc把局部变量def成全局变量——方便配合编辑器"求值缓冲区表达式"的功能逐个验证:
(sc.api/defsc 7) a ;; => 23 (+ x z u) ;; => 12作者本人更偏爱defsc的方式。项目教程里有一个完整的实战案例:调试一个 Haversine 距离计算函数,结果永远是 0.0 米——用defsc恢复现场后逐个求值子表达式,很快就发现lat2和lng2竟然都是从p1而不是p2解构出来的。完整过程见 doc/Tutorial.md 与示例脚本 lab/sc/lab/tutorial.clj。
像断点一样暂停代码:brk 与 loose 系列
sc.api/brk是spy的"断点版":它不立即求值,而是把当前线程挂起,直到你在 REPL 里放行——此时你可以先恢复环境、检查变量,再决定程序怎么走:
(sc.api/brk msg) ;; 程序停在这里,等待你的指令放行时有三种玩法(定义见 src/sc/api.cljc):
| 方法 | 效果 |
|---|---|
sc.api/loose | 正常继续执行 |
sc.api/loose-with | 用你提供的值替换原表达式,继续执行 |
sc.api/loose-with-err | 抛出指定异常,跳过后续副作用 |
配合sc.api/disable!还能一键停用某个"代码点"的记录行为——比如只在循环里断点一次就够了。🎯
新手常见坑与清理技巧清单
- ClojureScript 注意:由于编译与运行不在同一地址空间,
letsc/defsc必须写成(sc.api/letsc [7 -3] ...)的形式,同时给出执行点号和代码点号,这是最常被踩的坑 - 用完即删:
spy调用不应留在生产代码里(会造成内存泄漏);调试完记得(sc.api/dispose! 7)释放已记录的执行点 - 动态 Var 需显式声明:
(sc.api/spy {:sc/dynamic-vars [*out*]} expr) - 大输出嫌吵?换成安静的
spyqt/brkqt,只打印值的类型 - 想开一个"环境常驻"的子 REPL?用 src/sc/repl.clj 里的
sc.repl/ep-repl,之后所有输入都自动包在该快照环境里求值
相关文档与源码导航
| 内容 | 路径 |
|---|---|
| 完整 API 定义(spy / brk / defsc / letsc 等全部宏与函数) | src/sc/api.cljc |
| 详细教程(含断点、子 REPL、自定义日志、远程 spy) | doc/Tutorial.md |
| 实用技巧清单(无参 spy、只捕获错误、合并多个快照、reader 宏) | doc/Tips-and-Tricks.md |
| 可运行的练习示例(JVM 与 ClojureScript) | lab/sc/lab/tutorial.clj、lab/sc/lab/example.cljc |
| REPL 子环境实现 | src/sc/repl.clj |
核心宏的定位速查:spy 宏、brk 宏、letsc 宏、defsc 宏。
常见问题 FAQ
scope-capture 是什么?一个帮你"保存 + 还原"代码局部环境的 REPL 宏库,把运行时上下文带回 REPL 求值,是 Clojure 的 Pry 式体验。
它会影响性能或生产环境吗?设计上只用于开发环境:装在你的:userprofile 里,不进项目依赖;用dispose!、undefsc清理即可,卸载无负担。
和 CIDER/Calva 调试器什么关系?scope-capture 专注一件事:让你在 REPL 中"穿上你程序的双鞋"。它不处理数据可视化、执行追踪等其它调试问题,可以和 tracing 库、pretty-printer 等工具搭配使用,互补而非竞争。
一句话总结:让代码替你重建现场,让你专注于找 Bug——这就是 scope-capture 给 Clojure REPL 工作流带来的"时光倒流"魔法。✨
【免费下载链接】scope-captureProject your Clojure(Script) REPL into the same context as your code when it ran项目地址: https://gitcode.com/gh_mirrors/sc/scope-capture
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考