可塑性计算、Emacs 与您

Hacker News Top 工具

摘要

一篇博客文章,详细说明如何使用 gh CLI 和各种 Emacs 包在 Emacs 中自动化 GitHub 问题管理,展示了可塑性计算。

暂无内容
查看原文
查看缓存全文

缓存时间: 2026/07/22 23:24

# 可塑计算、Emacs 与你 来源:http://yummymelon.com/devnull/malleable-computing-emacs-and-you.html 一切始于一项例行任务。我所有的公开项目都使用 GitHub Issues,但我个人更倾向于在 Org Agenda 中跟踪事项。为了协调两者,我会手动将 GitHub issue 复制到 Org 文件中(通常是标题和描述),*像个动物一样*。 尽管分离开来,但复制的 issue 让我可以将其当作一个草稿板,用于表达任何 Org 格式的内容。通过这种方式,我把 Org 中的复制 issue 既当作专用的笔记区域,也当作一个“暂存”区,用于撰写后续想在公开 issue 中分享的评论。 我手动复制的时间远远超过了我愿意承认的长度。在 Emacs 中重复某个任务足够多次后,一个不可避免的想法就会出现:“我应该自动化这个。” 这篇文章讲述了我是如何自动化这项任务的,并借此凸显 Emacs 的可塑计算能力。同时,它也是我之前文章《在 Emacs 中,一切看起来都像一项服务》(http://yummymelon.com/devnull/in-emacs-everything-looks-like-a-service.html) 的续篇。 ## 需求 在任何自动化工作中,一个关键问题就是“我想要完成什么?” 我希望能够做到: - 轻松地将一个 GitHub issue(标题、描述、一些元数据)复制为一个可在 Agenda 视图中跟踪的 Org 任务。 - 主要从 Emacs 中操作,尽量减少在 Emacs 与浏览器之间的上下文切换。 - 用 Org 语法表达我的想法。 - 创建一个新的 GitHub issue。 - 避免处理 GitHub 认证。 - 从 Emacs 中在浏览器中打开一个 GitHub issue。 另一个关键问题是“我*不想*做什么?” - 安装或编写一个全功能的 GitHub 客户端。 - 过分担心本地(Emacs)与服务器(GitHub)状态之间的同步逻辑。 - 花大量时间在上面(理想情况下一天内能有一个可用的东西,最多不超过一周)。 ## 规格说明 有了上述需求,接下来的问题是“我该怎么构建这个?” 对于这次练习,我决定利用我已有的 GitHub 命令行工具 `gh` (https://cli.github.com/)。这样做的好处是: - GitHub 认证委托给了 `gh`;无需从 Emacs 中直接处理。 - Emacs 可以将 `gh` 当作一个通向 GitHub 的 REST 服务来对待,如下图所示。 [图片示意] 为了完善我们的工具,我们可以利用不同的 Elisp 包和程序: - 对于用户界面,使用 `Transient` (https://www.gnu.org/software/emacs/manual/html_node/transient/) 和 `Variable Pitch Table (vtable)` (https://www.gnu.org/software/emacs/manual/html_node/vtable/) 包分别处理菜单和显示。 - 对于 Org 到 Markdown 的转换,使用 `ox-gfm` (https://melpa.org/#/ox-gfm)。 - 对于 Markdown 到 Org 的转换,使用 `Pandoc` (https://pandoc.org/)。 - 使用 Elisp 原生的 `JSON` (https://www.gnu.org/software/emacs/manual/html_node/elisp/Parsing-JSON.html) 支持来反序列化从 `gh` 返回的 JSON 响应。 ## 实现 上述实现已作为包 `fj` (https://github.com/kickingvegas/fj) 发布,其源代码可在 `fj.el` (https://github.com/kickingvegas/fj/blob/main/lisp/fj.el) 文件中查看。其中值得注意的是函数 `fj-request-issues`,它通过 `gh` 完成获取 GitHub issue 的工作,如下所示。 ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ``` ``` (defun fj-request-issues (repo) "Request issues for REPO." (let* ((fields fj-browser-fields) (cmd-list (list "gh" "--repo" (format "'%s'" repo) "issue" "list" "--limit" (number-to-string fj-request-issue-count) "--json" (string-join fields ",")))) (json-parse-string (shell-command-to-string (string-join cmd-list " ")) :null-object nil))) ``` 考虑一下 `fj-request-issues` 提供的高度抽象: - 列表 `cmd-list` 构成了请求(此处是调用 `gh` 的参数)。 - `shell-command-to-string` 将请求发送给 `gh`。 - 返回的 JSON 响应由 `json-parse-string` 处理,将 JSON 反序列化为一个 Elisp 哈希表。 - 上述所有操作在不到 20 行代码中完成。 返回的哈希表结果随后被处理以填充一个 vtable,如下所示。在 vtable 中,用户可以浏览 issue 列表,同时一个辅助窗口会更新显示所选 issue 的详情。 [图片示意] 为了满足上述需求,我们创建了多个命令和函数来操作该哈希表。它们通过以下 Transient 菜单访问: [图片示意] ## 可塑计算观察 由于 Elisp 是一种动态编程语言 (https://en.wikipedia.org/wiki/Dynamic_programming_language),上述函数(或其变体)可以在运行的 Emacs 会话中编写和求值。在 Emacs 中,原型化代码行为而不需要重启是常规实践。对比之下,那些用静态语言构建且没有可扩展性的工具,必须经历“编辑-编译-调试”的开发周期才能测试行为——前提是源代码可用。 Emacs 提供了多种编辑和求值 Elisp 代码的方式,包括: - 草稿缓冲区 - Elisp 文件 - Org 源块 - IELM REPL - Eshell - `eval-expression` (`M-:`) 由于加载的 Elisp 代码之间没有隔离,它们可以以即兴的方式协同工作。任何可通过 shell 供 Emacs 访问的程序也进一步增加了这种组合。 借助高度抽象,为所需行为编写的代码量可以非常少。截至本文撰写时,`fj.el` 约有 400 行代码,由 `cloc` 测量: ``` github.com/AlDanial/cloc v 2.08 T=0.01 s (146.4 files/s, 76550.1 lines/s) ------------------------------------------------------------------------------- Language files blank comment code ------------------------------------------------------------------------------- Lisp 1 93 38 392 ------------------------------------------------------------------------------- ``` 我花了大约 2.5 小时构建了所需的基本行为(从 GitHub 请求 issue 并显示),剩余的时间覆盖了所有初始需求。之后的一切只是重构。 有兴趣的读者可以查看 `fj.el` 来了解细节。不过,此刻我想借机谈谈软件工程与可塑计算。 ## 软件范围——一些百分比轶事 90/90 规则 (https://en.wikipedia.org/wiki/Ninety%E2%80%93ninety_rule) 指出:“代码的前 90% 占用了开发时间的 90%;剩下的 10% 代码则占用了另外 90% 的开发时间。”与之密切相关的帕累托原则 (https://en.wikipedia.org/wiki/Pareto_principle)(又称 80/20 规则)在软件中(更可能是误用)体现为:只有 20% 的功能会被 80% 的用户实际使用。 从控制论中,有界输入有界输出(BIBO)稳定性 (https://en.wikipedia.org/wiki/BIBO_stability) 的概念。如果系统是 BIBO 稳定的,那么任何有界输入都会产生有界输出。 将这些想法结合起来,如果你想要的功能(BIBO 稳定性)恰好落在那 20% 的可交付范围内,那么你就能*更快*地获得期望结果。 不幸的是,对于许多需要满足广大受众的工具生产者而言,这种观察并不适用。在 Sinofsky 的文章《什么是软件臃肿?》(https://hardcoresoftware.learningbyshipping.com/p/077-what-is-bloat-really?s=w) 中,他描述了 Microsoft Office 的产品定义问题,特别是功能集方面。根据用户研究,他们得出了这样的结论:“数据完全明确:大部分 Office 功能都有人用。但没有一个人会用整个产品。” 在讨论产品定义时,区分两种非正式的动力机制是有帮助的:供给和需求。 对于仅由供给方提供的软件(如 MS Office 的情况),产品定义的重担落在生产者肩上。如果生产者想服务大量受众,那么他们的功能集和相应的开发范围很可能也会非常庞大。在需求侧,消费者可以请求新功能,但其优先级由生产者控制。在这种情况下,生产者和消费者的角色是严格区分的。 可塑软件则提供了另一种可能性:用户有能力适应和重塑他们的数字工具。生产者提供构建模块,让消费者自己制作工具。通过这种模式,现有的代码和程序被重新组合以产生新的行为。换句话说,可塑软件利用了组合爆炸的优势:期望的行为子集很可能存在于将现有库(此处为 Elisp)与不同程序组合的状态空间中。在可塑软件中,生产者和消费者之间的角色不那么分明。一个用可塑积木构建新工具的消费者现在必须承担产品定义的重担。 ## 为 1 人或 N 人构建 为 1 人构建与为 *N* 人构建的范围可能相差一个数量级,关键在于 *N* 不必很大。选择为另一个人写代码(*N=2*)会引发一些本可忽略的担忧: - 错误处理 - 代码可维护性 - 模块化/复用 - 文档 - 单元测试与集成测试 - 打包 - 分发 可塑技术的一个优点和诅咒是它们只提供“刚好够用”的能力。可塑技术鼓励为 1 人构建,因为达到“它对我有效”的状态通常就足以宣告胜利。 对于大多数仅由供给方提供的软件,为 *N* 人构建是必须的。对于大多数用可塑技术构建的软件,为 *N* 人构建是一种选择。 ## 可塑软件与用户自主权 回到 `fj`,为 1 人构建的好处显而易见: 从 Emacs 内部,我现在可以轻松地: - 浏览指定仓库的 GitHub issue。 - 将一个 GitHub issue 复制到 Org 文件中。 - 使用 Org 语法创建一个新的 GitHub issue。 - 在浏览器中打开一个 GitHub issue。 在 Emacs 中实现 `fj` 很直接,因为我可以利用 Elisp 包(内置和第三方)以及外部应用程序(`gh`、`pandoc`)来构建它。一天之内,我就有了一个能做我想要的工具。我不需要请求许可,也不需要特权材料(源代码)来构建 `fj`。有了 Emacs,我*就可以直接做*。与那些彼此隔离、极少集成的应用程序相比,可塑技术带来的这种个人赋权是解放性的。 ## 结语 这篇文章探讨了 Emacs 提供的可塑计算能力,并通过具体例子(`fj.el`)展示了如何利用代码和程序复用,以即兴的方式创造新行为。在合理的预期(需求、功能集、受众)下,可塑技术允许构建那些否则不可能实现的工具。 ## 链接 - https://github.com/kickingvegas/fj - 《什么是真正的软件臃肿?》(https://hardcoresoftware.learningbyshipping.com/p/077-what-is-bloat-really?s=w),Steven Sinofsky。 emacs (http://yummymelon.com/devnull/tag/emacs.html) org mode (http://yummymelon.com/devnull/tag/org-mode.html)

相似文章

软件界的Emacs化

Hacker News Top

作者讲述了在终端中阅读 Markdown 的烦恼,并描述了如何使用 Claude 快速构建一个自定义的 macOS Markdown 查看器(MDV.app),展示了 AI 如何让人能够迅速创建个人软件工具。

Emacs 写作机

Lobsters Hottest

一篇关于将旧 ThinkPad 改造为专用写作设备的博客文章,运行 Debian 和 Emacs,灵感来自 Veronica Explains 的 writerdeck 概念,包含配置和通过 Git 同步文件的技巧。

离开 Magit 后的 Emacs

Lobsters Hottest

作者讲述了他们离开 Emacs 的 Magit Git 界面,转而采用 VC-mode 和自定义 Git 脚本等替代方案的经历,重点介绍了其中的调整和所学到的经验教训。

用机器重建我的博客,为机器服务·

Lobsters Hottest

作者重建了博客,加入了完整的结构化数据标记(JSON-LD、微格式),并配备了一个由提示词引导的AI协作写作助手,该提示词避免了常见的LLM模式,同时通过CI验证防止数据损坏。