在 Emacs 中,一切都像服务

Hacker News Top 工具

摘要

这篇文章解释了 Emacs 如何作为各种服务的客户端,利用其内置的 UI、网络通信和数据管理库,支持了“一切都像服务”在 Emacs 中的理念。

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

缓存时间: 2026/07/10 12:12

# 在 Emacs 中,一切看起来都像服务 来源: http://yummymelon.com/devnull/in-emacs-everything-looks-like-a-service.html 人们常说 Emacs 是一个操作系统(OS)。这并不准确,但它能调度操作系统内核之上的应用程序和工具,这一点倒是容易让人联想到操作系统。下面的示意图更准确地展示了 Emacs 与操作系统及其能力的关系。 img Emacs 内置了对 OS 系统服务(文件系统、网络等)的访问,加上运行其他程序的能力,使得在 Emacs 内部即兴模拟客户端行为成为常态。正因为如此,Emacs 用户能够通过为其开发的各种客户端模式完成许多计算需求。这为"只生活在 Emacs 中"的说法提供了依据。 在这篇文章中,我们将探讨 Emacs 让你构建客户端的一些方式。读完这篇文章,你很可能也会相信:在 Emacs 看来,一切都是服务。 ## 客户端-服务器模型 首先,我们来定义一些概念。 **客户端-服务器模型**(https://en.wikipedia.org/wiki/Client%E2%80%93server_model)是一种常见的计算机交互模式,它将任务分割为资源提供者(服务)和资源请求者(客户端)。客户端向服务器发出**请求**,服务器则返回**响应**,如下图所示。 img 根据实现方式的不同,这次交易(请求 + 响应)可以发生在网络上,也可以是系统本地的。基于网络的客户端-服务器模型在 REST 风格软件架构(https://en.wikipedia.org/wiki/REST)中得到了最充分的阐述。下图的时序图展示了 REST 风格客户端-服务器架构的一种常见实现模式。 img ## Emacs 作为客户端 从上面的图可以看出,客户端通常负责三个关注点: - **UI:** 用户界面(如果有的话)。 - **客户端边缘:** 负责与服务通信的子系统。对于网络客户端,这就是网络子系统。 - **本地数据库:** 与服务器交换或同步的数据表示。数据的管理方式由实现需求决定。 针对上述关注点,Emacs 提供了众多内置和第三方库来实现客户端。下面列出了一些内置库及其相应的阅读链接: - UI - Minibuffers(https://www.gnu.org/software/emacs/manual/html_node/elisp/Minibuffers.html) - Buffers(https://www.gnu.org/software/emacs/manual/html_node/elisp/Buffers.html) - Completion(https://www.gnu.org/software/emacs/manual/html_node/elisp/Completion.html) - Tabulated List Mode(https://www.gnu.org/software/emacs/manual/html_node/elisp/Tabulated-List-Mode.html) - Variable Pitch Table (vtable)(https://www.gnu.org/software/emacs/manual/html_node/vtable/) - Transient(https://www.gnu.org/software/emacs/manual/html_node/transient/) - 客户端边缘 - URL(https://www.gnu.org/software/emacs/manual/html_node/url/) - Socket (TCP/UDP)(https://www.gnu.org/software/emacs/manual/html_node/elisp/Network.html) - SMTP(https://www.gnu.org/software/emacs/manual/html_node/smtpmail/) - 序列化/反序列化 - JSON(https://www.gnu.org/software/emacs/manual/html_node/elisp/Parsing-JSON.html) - XML(https://www.gnu.org/software/emacs/manual/html_node/elisp/Parsing-HTML_002fXML.html) - 本地数据库 - 集合 - Association Lists(https://www.gnu.org/software/emacs/manual/html_node/elisp/Association-Lists.html) - Property Lists(https://www.gnu.org/software/emacs/manual/html_node/elisp/Property-Lists.html) - Hash Tables(https://www.gnu.org/software/emacs/manual/html_node/elisp/Hash-Tables.html) - SQLite(https://www.gnu.org/software/emacs/manual/html_node/elisp/Database.html) 需求决定了实现 Emacs 客户端所需的复杂程度。如果某个现有的命令行工具能够完成"重活",那么该工具就可以被重构为可以通过 shell 调用访问的"服务"。 img ## Elisp 上述所有库都通过 Emacs Lisp(Elisp(https://www.gnu.org/software/emacs/manual/html_node/elisp/))编程语言进行访问。Elisp 是一种动态编程语言(https://en.wikipedia.org/wiki/Dynamic_programming_language),允许在运行时进行高度即兴的发挥。这种能力使得可以复杂地调度 Emacs 可用的任何行为,从 Elisp 函数到 shell 命令。 ## 示例:wttr.in 客户端 wttr.in(https://github.com/chubin/wttr.in)是一个面向控制台的天气预报网络服务。它支持 JSON 输出(https://github.com/chubin/wttr.in#different-output-formats),因此我们可以构建一个 Emacs `wttr` 命令,该命令会提示输入地点,发起 HTTP 请求,处理 JSON 响应,并在迷你缓冲区中显示结果。 顶级命令 `wttr` 如下所示。 ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 ``` ``` (defun wttr (location) "从 `https://wttr.in' 获取 LOCATION 的天气状况,在迷你缓冲区显示。 结果也存储在 `kill-ring' 中。" (interactive "s地点 (默认: local): ") (condition-case err (let* ((url (wttr--request-url location)) (jsondb (fetch-json-as-hash-table url)) (msg (wttr--report-message jsondb))) (kill-new msg) (message "%s" msg)) (error (message "ERROR: %s" (cdr err))))) ``` wttr.in 的 URL 由函数 `wttr--request-url` 构造,如下所示。 ``` (defun wttr--request-url (location) "为 LOCATION 构造 wttr.in 的 URL。" (let* ((base-url (url-generic-parse-url "https://wttr.in")) (encoded-location (string-replace " " "+" location)) (query (format "/%s?0&format=j1" encoded-location)) (_dummy (setf (url-filename base-url) query))) (url-recreate-url base-url))) ``` 然后我们可以将该 URL 传递给 `fetch-json-as-hash-table`,后者负责获取 URL 并将 JSON 响应解析为 Elisp `hash-table`。 ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 ``` ``` (defun fetch-json-as-hash-table (url) "获取预期返回 JSON 的 URL,并返回 `hash-table'。" (let ((data-buffer (url-retrieve-synchronously url))) (if (not data-buffer) (error "无法从 %s 获取数据" url) (unwind-protect (with-current-buffer data-buffer ;; 将光标移动到 HTTP 元数据头部之后 (goto-char url-http-end-of-headers) ;; 将 JSON 缓冲区剩余内容解析为 hash-table (json-parse-buffer :object-type 'hash-table)) ;; 总是杀死下载的网络缓冲区以防内存泄漏 (kill-buffer data-buffer))))) ``` 最后,我们可以从 JSON 响应(`jsondb`)中提取所需值,填充要发送到迷你缓冲区的消息。 ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 ``` ``` (defun wttr--report-message (jsondb) "根据 JSONDB 生成天气预报消息。" (let* ((area-buflist ()) (nearest-area (wttr--get-first jsondb "nearest_area")) (area-name (map-elt (wttr--get-first nearest-area "areaName") "value")) (region (map-elt (wttr--get-first nearest-area "region") "value")) (country (map-elt (wttr--get-first nearest-area "country") "value")) (current-condition (wttr--get-first jsondb "current_condition")) (temp_c (map-elt current-condition "temp_C")) (temp_f (map-elt current-condition "temp_F")) (weather-description (map-elt (wttr--get-first current-condition "weatherDesc") "value"))) (mapc (lambda (x) (if (and x (not (string-equal x ""))) (push x area-buflist))) (list area-name region country)) (format "%s: %s°C, %s°F %s" (string-join (reverse area-buflist) ", ") temp_c temp_f weather-description))) ``` wttr.el 源码(https://gist.github.com/kickingvegas/40290cef2751c133666eb19921f91eee) ## 结语 至此,希望你已相信本文标题的断言:在 Emacs 中,一切看起来都像服务。此外,Emacs 提供的许多 API 都工作在较高的抽象层次。想想看,`wttr.el` 的代码量只有 67 行(使用 `cloc` 工具计算的结果)。 如果这还嫌多,那么想象另一种实现:将实际的网络请求和 JSON 处理放在一个名为 `weather` 的 Python 脚本中。那么调用它的 Elisp 命令就只是下面这段代码。 ``` (defun weather (location) "调用 weather 脚本处理 LOCATION,并在迷你缓冲区中显示结果。" (interactive "s地点 (默认: local): ") (let* ((weather-cmd "weather") (cmd (if location (format "%s %s" weather-cmd location) weather-cmd)) (result (shell-command-to-string cmd))) (kill-new result) (message result))) ``` 通过上述实现,shell 命令实际上就成了发送请求的"服务"。 由于 Elisp 是一种动态编程语言,它可以以即兴的方式将 Elisp 库与命令行工具集成在一起。 这种能力对认识到其机会的用户来说极具吸引力。 emacs(http://yummymelon.com/devnull/tag/emacs.html)

相似文章

推荐一下……理解 Emacs 的模式

Lobsters Hottest

文章解释了 Emacs 的架构模式,重点介绍了通用缓冲区数据模型和增量补全读取(ICR),并将其比作玫瑰的根与花瓣。文章强调了 Emacs 如何统一界面并通过 Elisp 实现可扩展性。

我可以推荐 Emacs 的创新 UI——eww 吗?

Lobsters Hottest

文章推荐使用 Emacs 的 eww 网页浏览器,强调其缺少 JavaScript 的特性改善了许多网站的体验,并指出 Emacs 提供了独特的 UI 创新,如逐张图片调整大小和键盘导航。

Emacs 在流行文化中的出现

Hacker News Top

一篇博客文章,记录了 Emacs 文本编辑器在电影和电视中的出现,包括《社交网络》、《创:战纪》、《北极冲击》和《硅谷》。

软件界的Emacs化

Hacker News Top

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

Emacs 的更多内置功能

Lobsters Hottest

一篇博客文章,介绍了Emacs中不太为人所知但实用的内置功能,延续了一个旨在提高原版Emacs功能可发现性的系列。