Emacs 作为服务编排器:使用 Elisp 构建客户端
Emacs 作为服务编排器:使用 Elisp 构建客户端
Emacs 作为服务编排器
Emacs 不是一个操作系统,但它作为一个高级编排器运行,能够管理操作系统内核层之上的应用程序和实用程序。通过利用对系统服务的内置访问以及 Emacs Lisp (Elisp) 的灵活性,用户可以将几乎任何外部资源——无论是网络 API 还是本地命令行工具——视为由自定义客户端消费的服务。
Emacs 中的客户端-服务器模型
在标准的客户端-服务器模型中,任务被拆分为服务(资源提供者)和客户端(请求者)。Emacs 在实现这种交互的客户端侧方面具有独特的地位,因为它为客户端的三个主要关注点提供了原生库:
1. 用户界面 (UI)
Emacs 提供了一系列内置的 UI 组件来显示数据和捕获输入,包括:
- Minibuffers and Buffers: 用于基础文本交互和数据展示。
- Completion and Transient: 用于高效的用户输入和命令导航。
- Tabulated List Mode and vtable: 用于结构化数据展示。
2. 客户端边缘 (通信)
Emacs 提供必要的管道,通过以下方式与外部服务通信:
- Network Protocols: 内置支持 URL (HTTP/HTTPS), Sockets (TCP/UDP), 和 SMTP。
- Data Serialization: 用于解析和生成 JSON 和 XML 的原生库。
3. 本地数据库 (数据管理)
为了管理交换数据的状态,Emacs 利用:
- Collections: Association lists, property lists, 和 hash tables。
- Persistent Storage: 内置的 SQLite 支持,用于处理更复杂的数据需求。
实现一个服务客户端:以 wttr.in 为例
由于 Elisp 是动态语言,开发者可以快速原型化客户端。例如,一个针对 wttr.in 的天气客户端可以通过以下步骤在大约 67 行代码内实现:
- Request Construction: 使用
url-generic-parse-url构建对wttr.inJSON API 的请求。 - Data Retrieval: 使用
url-retrieve-synchronously获取数据,并使用json-parse-buffer将响应转换为 Elisp hash table。 - Data Extraction: 解析 hash table 以提取特定值(例如温度和天气描述)并将其格式化为人类可读的字符串。
- Display: 将最终字符串输出到 minibuffer,并将其存储在
kill-ring中以备后使用。
将 CLI 工具重新定义为服务
当不需要专门的 Elisp 库时,任何命令行工具都可以被重新定义为“服务”。通过使用 shell-command-to-string,Emacs 可以向本地脚本(例如处理 API 调用的 Python 脚本)发送请求,并将脚本的输出视为服务的响应。这允许在不编写大量样板代码的情况下,实现外部工具的高度即兴集成。
社区观点与技术背景
虽然“一切皆服务”的方法非常强大,但社区对 Emacs 的本质提供了几种细致入微的观点:
- Lisp Machine Heritage: 一些用户认为 Emacs 不太像是一个 OS,而更像是 Lisp Machine 概念的实现——一个内置了文本编辑器的编程环境,而不是相反。
- Comparison to Modern Standards: 虽然 Language Server Protocol (LSP) 标准化了编辑器中许多客户端-服务器交互,但贡献者指出,Emacs 在 LSP 存在之前就已经拥有了长期运行的子进程、类 RPC 交互以及 TRAMP (Transparent Remote Access, Multiple Protocols)。
- The "Blob" Architecture: 一些人将 Emacs 描述为“单一的 Lisp 集合体”,其中核心系统与插件之间的界限变得模糊,从而允许新服务进行无缝集成。
"Emacs 更像是一个内置了文本编辑器的编程环境。有点像某些 SmallTalk 镜像。"
"Emacs 在很久以前就拥有了长期运行的子进程、类 RPC 交互、TRAMP、GUD、REPL 集成等。LSP 标准化了一个重要的接口,但它并不是架构的开端。"",