e 是一个使用Chez Scheme编写的可定制自感知类Emacs编辑器

Hacker News Top 工具

摘要

e 是一个小型、可定制的类Emacs文本编辑器,使用Chez Scheme编写,具有热重载、Scheme集成以及适用于控制台环境的最小依赖等特点。

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

缓存时间: 2026/08/29 09:34

paveluv/e

来源:https://github.com/paveluv/e

e

一个用 Chez Scheme(https://cisco.github.io/ChezScheme/)编写的微型控制台文本编辑器。它在精神上类似 Emacs——拥有缓冲区、窗口、剪切环、增量搜索和 Scheme 顶层环境——但它并非 Emacs 克隆。它借鉴了保持小巧的理念,并就此止步。

  • 该编辑器本身就是一个 Scheme 系统。一切都是 R6RS 库:一个具有公开、不可变 API 的最小核心,以及基于它构建的扩展模块(语法模式、括号匹配、编辑辅助工具、M-x 本身)。核心通过语言本身(而非纪律)来强制边界:内部不可见,导出不可重新赋值。
  • M-x 依据编辑器的实时顶层环境来求值 Scheme 代码,支持符号补全、输入时的参数提示、历史记录以及一个转录缓冲区。
  • 模块支持热重载。在编辑器内保存模块源代码会将其重新加载到运行中的会话中;在其他地方编辑的源代码可通过 reload-module! 加载。注册项会被替换,依赖模块会重新编译,缓冲区保持不变。e 本身就是在其内部环境中开发的。
  • 除了 Chez Scheme 和类 Unix 终端外,无其他依赖。无需构建或安装步骤:检出后即可原地运行。首次启动会编译库文件;后续启动约需 100 毫秒。
  • 撤销操作会报告其撤销的内容:Undo insert "hello"Undo (replace-all! "xx" "yy")。输入的字符会合并为连续操作,粘贴是一步,M-x 命令则是一个带标签的步骤。

快速开始

作为日常编辑器,克隆到 ~/.e

``sh git clone https://github.com/paveluv/e ~/.e ~/.e/e file.txt # 将 ~/.e 添加到 PATH 以直接使用 e


或者作为项目内的内置工具,这样任何克隆项目的人都会获得一个可工作的编辑器:

``sh
git clone https://github.com/paveluv/e ~/git/your_project/.e
rm -rf ~/git/your_project/.e/.git    # 使其成为你仓库的普通文件
~/git/your_project/.e/e file.txt

每个安装都是自包含的:加载器严格使用脚本旁边的 lib/ 目录,编译后的对象本地保留在 eo/ 中,当源文件(或其导入的任何内容)发生变化时会自动重新编译。放入内置检出目录 lib/ 的项目特定模块仅限于该项目使用。

终端大小通过 ioctl 检测,并在窗口大小变化时跟踪;如果不可用,请设置 LINESCOLUMNS。在 FreeBSD 上,其 Chez 移植版重命名了 scheme-script 解释器,请将 shebang 改为 #!/usr/bin/env -S chez-scheme --script 或运行 chez-scheme --script e(详情见加载器头部注释)。

键绑定

文件与退出

按键操作
C-x C-s保存(无名缓冲区会提示输入名称)
C-x C-w另存为:提示输入路径,缓冲区将访问该路径
C-x C-f在单独缓冲区中访问文件(如需要则创建)
C-x C-c退出(仅当某些缓冲区与其文件不同时才询问)

缓冲区与窗口

按键操作
C-x b切换缓冲区(默认:最近使用的其他缓冲区;输入新名称会创建缓冲区)
C-x C-b打开 *buffers*:使用上/下键移动,回车选择;点击立即选择
M-Up按字母顺序切换到上一个缓冲区
M-Down按字母顺序切换到下一个缓冲区
C-x k关闭缓冲区(默认:当前缓冲区;若已修改则询问)
C-x 2将当前窗口垂直分割为两个(堆叠)
C-x 3将当前窗口水平分割为两个(并排)
C-x 1删除所有其他窗口
C-x 0删除当前窗口
C-x o移动到下一个窗口
C-x t切换此窗口中长行的软换行

每个窗口都有自己的状态行、光标位置和滚动位置;同一个缓冲区可以同时显示在多个窗口中。缓冲区具有按缓冲区记录的历史和标记;窗口则拥有独立的光标位置、滚动、换行和状态。默认显示右侧滚动条;C-x l 切换缓冲区拥有的行号栏。详见 缓冲区 了解切换、*buffers*、文件完整性、目标窗口、鼠标行为、滚动条、换行、分割以及公共 API。

移动

按键操作
C-b / C-f / 方向键向左/向右移动一个字符
C-p / C-n / 方向键向上/向下移动一行
C-a / C-e,Home/End行首/行尾
C-v / M-v,PgDn/PgUp向下/上翻页
M-< / M->缓冲区开头/结尾

编辑

按键操作
C-d,Delete删除光标后的字符
Backspace删除光标前的字符
C-o在下方插入新行,光标保持原位
C-k删除至行尾(重复操作会累加)
C-@ (C-Space)设置标记
C-w删除标记与光标之间的区域
C-y粘贴上一次删除的内容
C-_撤销
C-M-_重做
TAB按照当前模式的规则缩进该行
M-%从光标处开始查询替换:y/空格替换,n/Delete跳过,q/回车/C-g停止
M-.描述光标处的符号(Scheme 缓冲区;见下文)
C-x C-e将当前缓冲区作为 Scheme 代码求值
C-l重绘屏幕并重新读取其尺寸
C-g取消(提示、搜索、标记、正在运行的求值)
C-h k描述一个按键及其绑定来源

按键定制

所有键盘命令都可以在 config.e 中重新绑定。用户绑定会覆盖内置和模块默认值,C-h k 会描述一个按键、其来源及其上下文含义:

``scheme (bind-key! “C-c s” save!!) (unbind-key! “C-v”)


详见 [按键绑定配置](docs/KEY_BINDING.md) 了解按键语法、上下文映射、可用操作、优先级、解绑和模块默认值。

### 搜索

`C-s` 启动增量搜索:输入扩展搜索字符串,再次按 `C-s` 跳转到下一个匹配项(循环),Backspace 缩短搜索字符串,`回车`/`ESC` 静默接受,`C-g` 取消并返回光标至搜索开始的位置。在新的空搜索中,`C-s` 会调出并搜索上一次的搜索字符串。光标会落在当前匹配项之后,因此在搜索前设置的标记会将找到的文本包含在区域内。
搜索时,当前窗口中搜索字符串的匹配项会被高亮显示。匹配采用智能大小写折叠,如同 Emacs:仅当搜索字符串全为小写时才忽略大小写,输入一个大写字母则变为精确匹配(此时提示显示为 `I-search (exact):`)。
`M-c` 可随时切换当前搜索的大小写模式,在 `config.e` 中设置 `(search-fold-case #f)` 可使每次搜索都精确匹配。其他所有操作——包括 `M-%` 查询替换——总是精确匹配。

### 缩进与格式化

缩进是唯一强制执行的规则——行内的空格由作者决定(表格、对齐和手动排版传递结构信息),行永不合并或拆分。Scheme 规则类似 Emacs:主体形式(`define`、`lambda`、`let` 系列、`when` 等)将其主体相对于起始括号缩进两列,单独的闭括号位于其对应的开括号下方,而函数应用的续行有两个*停止点*:超过起始括号两列,或当其与起始括号同行时与第一个参数对齐。作者可选择:

    (very-long-function-name param1 param2
      param3)
    (very-long-function-name param1 param2
                             param3)

`TAB` 会缩进当前行,在其停止点之间循环——向右最近的停止点,循环回到开头——并将光标置于缩进处(空行会填充至此);模式通过注册缩进器来启用此功能,scheme-mode 即如此。`indent-region!` 和 `indent-buffer!` 从顶部开始逐行缩进,作为一个撤销步骤,每行最终停留于其当前缩进最近的停止点:在停止点则保持不变,在第一个停止点之前则采用第一个,在最后一个之后则采用最后一个。`format-region!` 和 `format-buffer!` 还会将字符串外的制表符扩展为空格(`(scheme-tab-width 2)`;`#f` 保留制表符)、删除尾随空格、删除尾随空行使文件以恰好一个换行符结尾,并应用括号约定——`let` 系列、`do`、`parameterize` 和 `with-syntax` 的绑定,以及 `cond`、`case`、`case-lambda`、`guard`、`syntax-rules` 和 `syntax-case` 的子句使用 `[ ]`;其他情况使用 `( )`——参数 `scheme-format-brackets` 可关闭此功能。区域格式化仅重写其完全包含的括号对,因此永远不会破坏缓冲区的括号平衡;字符串内部和边注永远不会被修改。模块通过 `register-indenter!` 和 `register-formatter!` 为每种模式提供规则。Scheme 引擎是纯 `(scheme-format)` 库,它也驱动 `tools/scheme-format`:通过 shell 使用 `scheme-format [-i] [file ...]` 进行相同的处理,或无参数时从标准输入到标准输出。启用 `(scheme-format-on-save #t)`(默认开启)后,每次保存 Scheme 缓冲区时,会先通过保存前钩子进行格式化;模块可通过 `add-pre-save-hook!` 和 `add-post-save-hook!` 添加自己的钩子(模块重载就是一个保存后钩子)。

### 提示符输入

提示符输入支持行编辑,具有常用绑定(`C-a`/`C-e`、方向键、Home/End、通过共享剪切环的 `C-k`/`C-y`),并可通过 TAB 补全:文件提示符中补全文件名,缓冲区提示符中补全缓冲区名。TAB 会扩展至最长公共前缀;当无更多可扩展项时,再次按 TAB 会弹出一个 `*completions*` 窗口,提示完成后该窗口消失。超过屏幕宽度的输入会包裹到续行,并以结尾 `\` 标记,缩进在提示文本下方,上方的窗口会缩小以腾出空间;达到八行时,提示区域会改为滚动。长回声区消息以相同方式换行。在换行的提示符中,方向键在可视行之间移动,在上下边缘则转为历史导航。

## Scheme 求值

`M-x` 在编辑器的实时顶层环境中求值 Scheme 代码,其中可用公开的 API 和已加载的模块。`C-x C-e` 求值当前缓冲区。结果出现在回声区和结构化日志中:

    M-x (buffer-name (current-buffer))
    eval: (buffer-name (current-buffer)) => "sa.txt"

详见 [求值](docs/EVAL.md) 了解多行输入、补全、`eval!` 目标、输出捕获、结果复制、中断、历史记录和配置。

## 回声区与日志

回声区是日志的临时尾部:命令记录的每条消息都堆叠在自己的行上,前面有其组件名称的灰色前缀,因此一次保存操作(先重载模块再报告合并)会一次性显示所有三行。该区域随着消息到来而增长,上方的窗口缩小到最小,超过此限度后最旧的行会被让出;下一次键盘或鼠标事件会将其恢复为单行。在 `(parameterize ([message-progress #t]) ...)` 下记录的消息是进度信息:它会替代其组件的最新行(而非堆叠)——绝不会替代其他组件的——因此下载会报告十四个步骤为一行,实时更新,而 `*log*` 仍记录每一步。

当它恢复单行时,没有任何信息丢失:所有经过回声区的消息都会落入日志——编辑器的 syslog,具有时间(纳秒精度)、组件和文本的结构化记录。`*log*` 缓冲区是一个*视图*——一个根据记录实时渲染的非交互式应用——始终存在于缓冲区列表中(`C-x b *log*`),只读,在可见时刷新,其状态栏显示 `[]`,而普通缓冲区显示 `--`。光标在末尾(`M->`)时,它会实时跟踪日志;在其他位置时,视口保持不动,记录持续到达。`(log-view 'eval)` 可即时创建过滤视图——`*log eval*`。条目是带有注册展示方式的结构化数据:模块为其组件注册一个格式化器(以及可选的样式器),在回声区和视图中以相同方式使用——eval 日志记录成对 `(query . result)`,格式化为 `query => result` 并带有 Scheme 高亮,而其历史记录只显示查询。`message-source` 设为 `#f` 的消息是指示器——像大写锁定灯一样显示,从不被记录。`log-entries` 返回记录本身,供从 M-x 进行切片,`log-history` 从任何组件派生命令历史(find-file 和 save-as 通过这种方式浏览其过去的路径),`log!` 可添加你自己的记录。

通用编辑辅助工具位于 `(edit)` 模块中,接受可选的 `where` 参数。省略时,表示选中区域或整个当前缓冲区;其他选项可以是缓冲区或其名称、缓冲区上的谓词、区域或任意这些项的列表:

    M-x (replace-all! "xx" "yy")                    ; 当前缓冲区
    M-x (replace-all! "xx" "yy" buffer-file)        ; 所有文件缓冲区
    M-x (count-matches "xx" "notes.md")             ; 计数,不编辑

每个目标缓冲区获得一个标记为该命令本身的撤销步骤,光标位置保持不变。交互式 `replace!!`(`M-%`)则逐次查询出现情况,高亮每个匹配项,并提示输入未提供的参数。

Scheme 手册在编辑器内部可用:

    M-x (describe eq-hashtable-ref)

`C-h f`(`describe!!`)提示输入有文档的函数名称,并支持补全。其输入使用与其他提示符相同的匹配样式:部分名称为斜体,完整的 Scheme 名称为正体,e 特定名称会被高亮显示。它会打开一个 `*describe*` 缓冲区,其中包含参考文档中的条目:形式、返回值、库、源码以及完整说明文本。当描述的名称是命令时,该页面还会列出当前绑定到它的所有按键。`*describe*` 是一个实时视图,因此重新绑定或解绑按键会在下次重绘时更新已可见的页面。文档集涵盖 R6RS(来自 TSPL4)和 Chez 扩展(来自 Chez Scheme 用户指南)。运行一次 `M-x (fetch-describe-data!)` 即可下载并提取:约 1,400 个条目,存放在 `lib/` 旁边的 `data/describe/` 目录中,不包含在 Git 中。下载使用 curl;`describe.e` 完成其余工作。数据库是结构化的,可从 M-x 查询:`doc-lookup` 返回某个名称的条目,`doc-entries` 返回所有条目(可选地通过谓词过滤),`doc-*` 访问器可拆解条目,因此整个文档集可按源码、章节、库或其他任何方式切片。在 Scheme 缓冲区中,`M-.` 描述光标所在的符号。

模块可通过 `register-descriptions!` 为其公开的命令添加页面。每个条目与文档集条目具有相同的字段:`(names forms returns libraries source chapter url description)`。注册项由调用模块拥有,并在其重新加载时与其其他钩子一起替换。URL 可能为 `#f`。`(edit)` 模块使用此钩子来记录 `replace!!` 和 `replace-all!`,因此这些命令无需下载的 Scheme 文档集即可获得文档。

## 美化括号

`M-x (pretty-scheme-clusters!)` 会将当前缓冲区切换到一种模式,其中每个构造的括号显示为其所属集群对应的 Unicode 字符对,同时缓冲区和文件保留纯 ASCII:

    「define (twice x)
      ⟨let ([y (* x 2)])
        ⦅if (odd? y) y ⟦begin x⟧⦆⟩」

相似文章

eh: 一个极简的类似vi的编辑器

Lobsters Hottest

eh 是一个用 C 语言编写的极简类似vi的编辑器,具有 UTF-8 支持、正则表达式搜索/替换、shell 过滤器和多级撤销/重做功能。早期版本在 2024 年的 IOCCC 中获奖。

Jolt:使用Chez Scheme实现的Clojure编译器

Hacker News Top

Jolt是一个自托管的Clojure实现,可编译为Scheme(本地使用Chez,通过Gambit支持JavaScript),提供独立二进制文件、真正的并发性以及完整的Clojure语义,无需JVM。

Chez Scheme 在 Debian 中的现状

Lobsters Hottest

Chez Scheme 10.4.0 在长时间间隔后已上传到 Debian unstable;该软件包已移交至 Debian Scheme Dream Team,交叉编译问题已修复,且未来版本现在要求可重现构建。

Jolt:在Chez Scheme上运行Clojure

Lobsters Hottest

Jolt是一个新的Clojure实现,目标平台为Chez Scheme,旨在提供即插即用的替代方案,具有快速启动和低内存占用,利用Chez的JIT和GC来避免JVM的开销。