翻译: GNU Emacs 使用手册
- TAGS: Emacs
[ 最后修订与导出时间: 2026-02-26 00:20:15 +0800 ,总字数: 145995 ]
Emacs 编辑器
Emacs 是一款功能先进、可扩展、可定制且自带文档的编辑器。本手册详细介绍 Emacs 的编辑使用方法及部分定制技巧,对应 GNU Emacs 30.2 版本。
GNU Emacs 官方网站:https://www.gnu.org/software/emacs/
如需查看其他格式的手册,请点击此处。
您也可通过自由软件基金会FSF商店购买纸质版本。
有关 Emacs 扩展开发的详细信息,请参阅《Emacs Lisp 参考手册》中的 “Emacs Lisp” 章节。
本文档为《GNU Emacs 手册》,已更新至 Emacs 30.2 版本。
版权所有 © 1985–2025 自由软件基金会(Free Software Foundation, Inc.)
您可以根据 GNU 自由文档许可证(GNU Free Documentation License)1.3 版或自由软件基金会发布的任何后续版本的条款,复制、分发和 / 或修改本文档;其中 “不可修改章节”(Invariant Sections)为《GNU 宣言》(The GNU Manifesto)、《发行条款》(Distribution)和《GNU 通用公共许可证》(GNU GENERAL PUBLIC LICENSE),“封面文字”(Front-Cover Texts)为 “一份 GNU 手册”(A GNU Manual),“封底文字”(Back-Cover Texts)如下(a)项所示。完整的许可证文本包含在标题为 “GNU 自由文档许可证”(GNU Free Documentation License)的章节中。
(a)自由软件基金会(FSF)的封底文字为:“您拥有复制和修改本 GNU 手册的自由。通过 FSF 购买副本将支持我们开发 GNU 软件并推广软件自由。”
发行说明
GNU Emacs 是一款自由软件,这意味着任何人都可以自由使用它,并在特定条件下自由地重新分发。GNU Emacs 并不属于公有领域,它受版权保护,其分发行为也有相关限制,但这些限制的设立,是为了保障每一位愿意遵守协作规范的使用者都能享有应有的自由。禁止的行为是,试图阻止他人进一步分享从你手中获得的任何版本的 GNU Emacs。具体的使用与分发条件详见随 Emacs 一同发布的《GNU 通用公共许可证》,本手册中也收录了该许可证的内容 1。参见《GNU 通用公共许可证》章节。
获取 GNU Emacs 副本的一种方式,是从其他已拥有该软件的人处拷贝。你无需征求我们的许可,也不必告知其他任何人,直接复制即可。若你能接入互联网,可通过匿名文件传输协议(FTP)获取 GNU Emacs 的最新分发版本;更多相关信息,请访问我们的官方网站:https://www.gnu.org/software/emacs。
你也可能在购买计算机时获得 GNU Emacs。计算机制造商可依据适用于所有用户的同一套条款,自由分发该软件的副本。这些条款要求制造商向你提供完整的源代码,其中需包含他们可能做出的任何修改,同时必须允许你依据《通用公共许可证》的常规条款,重新分发从他们那里获得的 GNU Emacs。换句话说,你获得这款程序时,它必须是自由的,这种自由不应只属于制造商。
如果你认为 GNU Emacs 对你有所帮助,不妨向自由软件基金会(Free Software Foundation)捐赠,以支持我们的工作。在美国,向自由软件基金会的捐赠可享受税收减免政策。若你在工作场所使用 GNU Emacs,建议你提议所在公司进行捐赠。捐赠渠道请访问:https://my.fsf.org/donate/。关于其他可以提供帮助的方式,详见:https://www.gnu.org/help/help.html。
我们同时发售本手册的纸质版,以及罗伯特・J・查塞尔所著《Emacs Lisp 编程入门》一书的纸质版。你可以访问我们的线上商店:https://shop.fsf.org/。销售所得收入将全部用于支持基金会的使命:开发新的自由软件,并对包括 GNU Emacs 在内的现有程序进行优化改进。
若你需要联系自由软件基金会,可访问官网联系方式页面:https://www.fsf.org/about/contact/,或写信至以下地址:
自由软件基金会
米尔克街 31 号,邮编 # 960789
马萨诸塞州 波士顿,邮编 02196
美国
引言
你正在阅读的是 GNU Emacs 的相关内容,它是 Emacs 这款高级(advanced)、自文档化(self-documenthing)、可定制(customizable)、可扩展(extensible)编辑器的 GNU 实现版本。(GNU 的全称是 “GNU’s Not Unix”,其中首字母 “G” 发音并非不发声。)
我们称 Emacs 为高级编辑器,是因为它的功能远不止简单的文本插入与删除。它可以控制子进程、自动缩进程序代码、同时显示多个文件、像编辑本地文件一样编辑远程文件,此外还有诸多强大功能。Emacs 的编辑命令可基于字符、单词、行、句子、段落和页面执行操作,同时也适配各类编程语言中的表达式与注释。
自文档化(self-documenting)指的是,你可以随时使用一类名为 “帮助命令” 的特殊指令,查看当前可用的功能选项、查询任意命令的作用,或是检索与指定主题相关的所有命令。详见 “帮助” 章节。
可定制(customizable)指的是,你能够以简单的方式轻松修改 Emacs 命令的行为。例如,若你使用的编程语言规定注释以 <** 开头、以 **> 结尾,你可以让 Emacs 的注释操作命令适配这类符号(详见 “注释操作” 章节)。再举个例子,你可以将上下左右这些基础光标移动命令,重新绑定到键盘上你觉得顺手的任意按键。详见 “自定义” 章节。
可扩展(extensible)指的是,你可以突破简单定制的范畴,创建全新的命令。新命令本质上是用 Lisp 语言编写的程序,可通过 Emacs 内置的 Lisp 解释器运行。即便是在编辑会话过程中,你也能重新定义已有命令,无需重启 Emacs。Emacs 的绝大多数编辑命令本身就是用 Lisp 编写的;仅有少数命令为追求执行效率而改用 C 语言实现,它们其实原本也可以用 Lisp 编写。编写扩展属于编程工作,但非编程人员也能直接使用现成的扩展功能。如果你想要学习 Emacs Lisp 编程,可参阅《Emacs Lisp 编程入门》一书的 “前言(Preface)” 章节。
1. 屏幕布局
在图形化显示器中(例如在 GNU/Linux 系统上使用 X 窗口系统时),Emacs 会占用一个图形化窗口。在文本终端中,Emacs 则会占据整个终端屏幕。我们将 Emacs 所占用的图形化窗口或终端屏幕统称为框架(frame)。Emacs 在这两种框架中的运行方式基本一致。默认情况下,启动 Emacs 只会打开一个框架,但你也可以根据需要创建更多框架(详见 “框架与图形化显示” 章节)。
每个框架都包含多个不同的区域(region)。框架顶部是菜单栏(menu bar),你可以通过一系列菜单调用各类命令。在图形化显示器中,菜单栏正下方是工具栏(tool bar),这是一排图标,点击即可执行相应的编辑命令。框架最底端是回显区(echo area),这里会显示提示信息,同时也是你在 Emacs 发出询问时输入信息的区域。
框架的主要区域位于工具栏(tool bar)(若存在)下方、回显区上方,该区域被称为窗口(window)。在本手册后续内容中,“窗口(window)” 一词均指代此含义。图形化显示系统中 “窗口” 的常用定义与本书不同;前文已说明,我们将这类图形化窗口称为 “框架(frames)”。
Emacs 窗口是用于显示缓冲区(buffer)的区域 —— 缓冲区即你正在编辑或查看的文本或其他图形内容。在图形化显示器中,窗口的一侧会配有滚动条,拖动滚动条即可滚动浏览缓冲区内容。窗口的最后一行是模式行(mode line),它会显示缓冲区的各类相关信息,例如是否存在未保存的修改、当前启用的编辑模式、光标所在的行号等。
启动 Emacs 时,框架中默认仅有一个窗口。不过,你可以将这个窗口进行水平或垂直分割,从而创建多个窗口,每个窗口都能独立显示一个缓冲区(详见 “多窗口” 章节)。
在任意时刻,都有且仅有一个窗口处于选中状态。在图形化显示器中,选中的窗口会显示一个更醒目的光标(通常为实心闪烁样式);其他未选中的窗口则会显示较不显眼的光标(通常为空心方块样式)。在文本终端中,屏幕上只会显示一个光标,且该光标始终位于选中的窗口内。选中窗口所显示的缓冲区被称为当前缓冲区(current buffer),所有编辑操作均在此缓冲区中进行。大多数 Emacs 命令都会默认作用于当前缓冲区;未选中窗口中显示的文本仅供参考。若你在图形化显示器中同时打开了多个框架,选中某一框架的同时,也会选中该框架内的一个窗口。
1.1. 光标位置(Point)
选中窗口中的光标标记着大多数编辑命令的生效位置,该位置被称为光标位置(point) 2。许多 Emacs 命令可以将光标位置移动到缓冲区的不同地方;例如,你可以在目标位置单击鼠标左键(通常为鼠标的第一按键),以此来定位光标位置。
默认情况下,选中窗口中的光标会显示为实心方块,看起来是停留在某个字符之上,但你应当将光标位置理解为两个字符之间的间隙—— 它 位于光标所覆盖字符的前方 。举例来说,若文本内容为 frob 且光标覆盖在字母 b 上,那么光标位置就在字母 o 和 b 之间。此时若在该位置插入字符 !,文本会变为 fro!b,光标位置则会处于 ! 和 b 之间。如此一来,光标依旧会像之前一样覆盖在字母 b 上。
若你在 Emacs 中同时编辑多个文件(每个文件对应一个独立的缓冲区),那么每个缓冲区都有其专属的光标位置。即便某个缓冲区当前未被显示,当你后续再次打开它时,该缓冲区仍会保留此前的光标位置。此外,若同一个缓冲区在多个窗口中同时显示,这些窗口的每一个也会分别拥有独立的光标位置。
有关控制 Emacs 光标显示方式的相关选项,详见 “光标显示” 章节。
1.2. 回显区(Echo Area)
框架最底端的一行区域是回显区(echo area),它被用于显示各类用途的简短文本信息。
回显区之所以得名,是因为它的用途之一是回显功能—— 也就是在你输入多字符命令时,同步显示已输入的命令字符。单字符命令不会触发回显;而多字符命令(详见 “按键” 章节),若你在输入过程中停顿超过一秒钟,Emacs 就会回显该命令当前已输入的所有字符,以此提示你继续输入剩余部分。一旦回显功能触发,后续输入的命令字符就会实时显示在回显区中。这种设计既能让操作熟练的用户获得流畅的响应速度,又能为操作不熟练的用户提供充分的反馈信息。
当某个命令无法执行时,回显区也会用于显示错误提示信息。这类错误信息通常还会伴随蜂鸣提示音,或是屏幕闪烁的提醒效果。
部分命令会在回显区显示提示性信息,告知用户该命令已执行的操作,或是提供一些特定信息。与错误信息不同,这类提示性信息不会伴随蜂鸣或屏幕闪烁。例如,按下快捷键 C-x = (按住 Ctrl 键的同时按下 x 键,松开 Ctrl 键后再按下 = 键),回显区就会显示一条信息,内容包含光标位置处的字符详情、该字符在缓冲区中的位置,以及它在窗口中对应的列数。对于执行耗时较长的命令,在运行过程中往往会在回显区显示以 '…' 结尾的提示信息(有时还会以百分比的形式标注进度),待命令执行完毕后,再在信息末尾补充显示 done(完成)。
回显区显示的所有提示性信息,都会自动保存到一个名为 *Messages* 的特殊缓冲区中。(关于缓冲区的概念,我们将在后续内容中介绍,详见 “使用多缓冲区” 章节。)如果你错过了一条一闪而过的提示信息,只需切换到 *Messages* 缓冲区,就能重新查看该信息。 *Messages* 缓冲区的存储行数存在上限,这个上限由变量 message-log-max 定义。(变量的相关概念同样会在后续说明,详见 “变量” 章节。)当缓冲区内容达到行数上限时,每在末尾新增一行信息,就会自动删除缓冲区最开头的一行旧信息。
有关控制 Emacs 回显区使用方式的相关选项,详见 “显示自定义” 章节。
回显区还会被用于显示迷你缓冲区(minibuffer)。迷你缓冲区是一个特殊的输入窗口,你可以在这里输入命令的参数,例如待编辑文件的名称。当迷你缓冲区处于激活状态时,回显区显示的文本会以提示字符串开头,同时激活状态的光标会出现在迷你缓冲区中 —— 此时迷你缓冲区会被临时认定为选中窗口。你可以随时按下 C-g 快捷键退出迷你缓冲区。详见 “迷你缓冲区” 章节。
1.3. 模式行(Mode Line)
每个窗口的底部都有一行模式行,用于显示当前缓冲区的状态信息。当框架中只有一个窗口时,模式行位于回显区(echo area)的正上方,也就是整个框架的倒数第二行。在图形化显示器中,模式行的外观呈现为一个 3D 立体方框样式。为了突出显示,Emacs 通常会将选中窗口的模式行设置为与未选中窗口不同的颜色。
模式行显示的文本遵循以下格式:
cs:ch-dfr buf pos line (major minor)
在文本终端中,上述文本的后方会显示一串连字符,一直延伸至窗口的右边缘;而在图形化显示器中,这串连字符会被省略。
各部分含义说明:
cs 及其后的冒号:
这部分用于描述当前缓冲区使用的字符编码集和换行符规则。通常情况下,Emacs 会自动处理这些设置,但有时查看这些信息也会很有帮助。
- cs 代表缓冲区中文本的字符编码集(详见 “编码系统” 章节)。如果显示为连字符('-'),表示未启用特殊的字符编码集处理(换行符规则可能除外,具体见下一段);如果显示为等号('='),表示完全不进行编码转换,这种情况通常用于编辑非文本类数据文件;其他字符则代表不同的编码系统,例如 1 代表 ISO Latin-1 编码。
- 在文本终端中,cs 前方还会显示两个额外字符,分别表示键盘输入和终端输出所使用的编码系统。此外,若你启用了输入法,cs 前方还会显示标识当前输入法的字符串(详见 “输入法” 章节)。
- cs 后方的字符通常是冒号。如果显示为其他字符,则说明该文件采用了非标准的换行符规则。文本文件的换行通常由换行符分隔,但有时也会采用另外两种规则:MS-DOS 系统的规则是用回车符加换行符表示换行,编辑这类文件时,冒号会变成反斜杠(\)或 (DOS)(具体显示取决于操作系统);早期 Macintosh 系统的规则是用回车符替代换行符,编辑这类文件时,冒号会变成正斜杠(/)或 (Mac)。在部分系统中,对于使用换行符作为分隔符的文件,Emacs 会用 (Unix) 替代冒号进行显示。
- 对于通过 emacsclient 创建的框架(详见 “调用 emacsclient” 章节),cs 后方的字符会显示为 @。这种标识常见于以守护进程模式运行的 Emacs 进程所创建的框架(详见 “将 Emacs 用作服务器” 章节)。
ch
这部分用于标识缓冲区的修改状态。如果窗口中显示的缓冲区内容与磁盘上对应的文件内容一致(即缓冲区未被修改),会显示两个连字符('–');如果缓冲区已被修改,则显示两个星号(**);对于只读缓冲区,若内容已修改会显示 %*,未修改则显示 %%。
ch 后的字符
该位置通常显示为连字符('-')。但如果当前缓冲区的 default-directory(详见 “文件名” 章节)指向远程主机,该位置会显示为 @。
d
仅当窗口被专用绑定到当前缓冲区时才会显示。若为强专用绑定,显示为大写字母 'D';若为其他类型的专用绑定,显示为小写字母 'd';若窗口未被专用绑定,则不显示该字符。详见《Emacs Lisp 参考手册》中关于专用窗口的内容。
若你按下
M-x a u TAB,此时 TAB 键会查找以 “au” 开头的补全候选项。U:**- *Completions* All L1 (Completion List)
fr
代表当前选中框架的名称(详见 “框架与图形化显示” 章节),仅在文本终端中显示。初始框架的名称为 'F1'。
buf
是窗口中显示的缓冲区名称,通常与你正在编辑的文件名一致。详见 “使用多缓冲区” 章节。
pos
用于提示窗口外的文本位置情况:若缓冲区内容较少且全部能在窗口中显示,会显示 'All';若当前查看的是缓冲区开头部分,显示 'Top';若查看的是末尾部分,显示 'Bot';其他情况则显示 'nn%',其中 'nn' 代表窗口上方的内容占整个缓冲区的百分比。启用
size-indication-mode后,还可以在模式行中显示缓冲区的大小。详见 “可选模式行功能” 章节。line
格式为字母 'L' 后接光标位置对应的行号。(启用 列号模式(
column-number-mode) 后,还可以显示当前光标所在的列数。详见 “可选模式行功能” 章节)。(major minor)
- major 代表缓冲区使用的
主模式名称。主模式是缓冲区的核心编辑模式,例如文本模式、Lisp 模式、C 语言模式等。详见 “主模式” 章节。部分主模式还会在名称后显示额外状态信息,例如编译缓冲区和 Shell 缓冲区会显示子进程的运行状态。 - minor 列出当前启用的部分
次模式。次模式是可选的编辑模式,能够在主模式的基础上提供额外功能。详见 “次模式“ 章节。
此外,部分功能即便不属于真正的次模式,只要处于开启状态,也会和次模式一同显示在该区域:
- major 代表缓冲区使用的
补充说明
若 Emacs 处于 递归编辑层级 ,包裹模式名称的圆括号会被方括号([…])包围;如果处于多层递归编辑状态,会显示双层方括号,以此类推。由于递归编辑层级是全局生效的,因此所有窗口的模式行都会显示对应的方括号标识。详见 “递归编辑层级” 章节。
你既可以修改模式行(mode line)的外观样式,也可以调整其内容格式,具体方法详见 “可选模式行功能” 章节。此外,模式行支持鼠标交互:点击模式行的不同区域可以执行对应的命令(详见 “模式行鼠标命令” 章节);将鼠标悬停在模式行的交互区域时,还会弹出工具提示框,显示该区域可触发的命令说明(详见 “工具提示” 章节)。
1.4. 菜单栏(Menu Bar)
每个 Emacs 框架(frame)的顶部通常都会有一个菜单栏(menu bar),你可以通过它执行各类常用操作。此处无需逐一列举这些操作,你自行查看会更为直观。
在支持鼠标的显示器上,你可以用鼠标从菜单栏中选择命令。菜单项右侧边缘的箭头,表示该选项会展开一个子菜单(submenu);菜单项(menu item)末尾带有 '…' 时,意味着执行该命令前,程序会提示你输入更多信息。
菜单栏中的部分命令同时配有常规的快捷键绑定(key bindings);若存在快捷键,会直接显示在对应菜单项的后方。若要查看某菜单项对应的完整命令名称及文档说明,可按下快捷键 C-h k ,随后按照常规方式用鼠标选中该菜单项即可(详见 “快捷键文档查询” 章节)。
除了使用鼠标,你也可以按下功能键 F10 来激活菜单栏的首个选项(该操作会执行 menu-bar-open 命令)。激活后,你可以通过方向键,或是快捷键 C-b(左)、C-f(右)、C-p(上)、C-n(下)来浏览菜单。按下回车键(RET)即可执行选中的菜单项;按下 C-g 或连续三次按下 ESC 键可退出菜单浏览模式。(注意:如果 Emacs 是基于图形界面工具包编译的,菜单的绘制与控制将由该工具包接管,此时退出菜单浏览模式的快捷键序列可能与上文描述不同。)
在文本终端中,你可以选择在回显区(echo area)中访问菜单栏(menu-bar)。要实现此功能,需将变量 tty-menu-open-use-tmm 自定义设置为非空值(non-nil)。完成设置后,按下 F10 键将执行 tmm-menubar 命令,而非直接展开菜单。(你也可以按下 M-` 组合键,该快捷键始终会调用 tmm-menubar 命令。) tmm-menubar 命令支持通过键盘选择菜单项,选中的候选选项会显示在回显区中。你可以使用上下方向键在菜单中切换不同选项,按下回车键确认选择。此外,每个菜单项都会分配一个字母或数字作为快捷选择符(通常取自菜单项名称中的某个单词首字母),该字符与菜单项名称之间用 '==>' 分隔,直接输入对应的字母或数字即可快速选中目标菜单项。
2. 用户输入类型
GNU Emacs 主要为 键盘操作 而设计。尽管你可以通过鼠标点击菜单栏和工具栏来执行编辑命令,但这种方式的效率通常不如键盘操作高。
Emacs 的键盘输入基于一套 高度扩展的 ASCII 字符集 。普通字符(例如 a、B、3、= 以及空格字符(记为 SPC))可直接敲击对应按键输入。控制字符(例如回车键 RET、制表符 TAB、删除键 DEL、退出键 ESC、功能键 F1、主页键 Home 以及左方向键 LEFT)同样通过这种方式输入,非英语键盘上的特定字符也是如此(详见 “国际字符集支持” 章节)。
Emacs 同样支持通过 修饰键(modifier keys) 输入控制字符。两种最常用的修饰键分别是 Control 键 (通常标注为 Ctrl)与 Meta 键 (通常标注为 Alt) 3。例如,输入 Control-a 的操作是按住 Ctrl 键的同时按下 a 键,我们将其简写为 C-a。同理,Meta-a(简写为 M-a)的输入方式是按住 Alt 键并按下 a 键。修饰键也可搭配非字母数字类字符使用,例如 C-F1 或 M-LEFT。
你也可以通过以 ESC 开头的双字符序列 来输入 Meta 字符。例如,输入 ESC 后再按下 a,等效于输入 M-a ;输入 ESC 后再按下 C-a ,等效于输入 C-M-a (即同时按住 Ctrl 键和 Alt 键,再按下 a 键)。与 Meta 键的操作方式不同, ESC 键需要作为独立字符输入:输入时无需按住 ESC 键再敲击后续字符,而是先按下并松开 ESC 键,之后再输入目标字符。该功能在部分 Meta 键工作不稳定的文本终端中十分实用。
Emacs 还支持另外 3 种修饰键,详见 “修饰键” 章节。
Emacs 对鼠标按键、鼠标滚轮,以及触控板、触摸屏等其他指针设备也提供了完善的支持,具体细节请参阅 “鼠标输入” 章节。
在图形化显示器中,窗口管理器可能会 拦截部分键盘输入 ,包括 M-TAB 、 M-SPC 、 C-M-d 和 C-M-l 。若遇到此类问题,你可以通过自定义窗口管理器设置,使其不再拦截这些按键;也可以重新绑定受影响的 Emacs 命令(详见 “自定义” 章节)。
普通字符、控制字符,以及鼠标点击等非键盘输入,在 Emacs 中统称为 输入事件 。关于 Emacs 内部如何处理输入事件的详细说明,参见《Emacs Lisp 参考手册》中的 “输入事件” 章节。
3. 按键(Keys)
部分 Emacs 命令仅需一个输入事件即可调用;例如, C-f 可在缓冲区中向前移动一个字符。另一些命令则需要两个或更多输入事件才能调用,比如 C-x C-f 与 C-x 4 C-f 。
按键序列 (简称按键)是指一组具有独立意义的一个或多个输入事件。若某一按键序列可以调用命令,则称之为 完整按键(complete key) ;例如, C-f 、 C-x C-f 和 C-x 4 C-f 均属于完整按键。若某一按键序列长度不足以调用命令,则称之为 前缀按键(prefix keys) ;从上述示例可知, C-x 和 C-x 4 均为前缀按键。所有按键序列要么是完整按键,要么是前缀按键。
前缀按键可与后续输入事件组合,形成更长的按键序列。例如, C-x 属于前缀按键,因此单独按下 C-x 并不会调用命令;相反,Emacs 会等待后续输入(若停顿超过一秒,它会回显 C-x 按键以提示用户输入后续内容,详见《回显区》章节)。 C-x 与下一个输入事件组合后,会生成一个双事件按键序列,该序列本身既可以是前缀按键(如 C-x 4 ),也可以是完整按键(如 C-x C-f )。按键序列的长度没有限制,但实际使用中很少超过三到四个输入事件。
无法在完整按键后追加输入事件。例如,由于 C-f 是完整按键,双事件序列 C-f C-k 会被视为两个独立的按键序列,而非一个。
默认情况下,Emacs 中的前缀按键(prefix keys)包括 C-c、C-h、C-x、C-x RET、C-x @、C-x a、C-x n、C-x r、C-x t、C-x v、C-x 4、C-x 5、C-x 6、ESC 和 M-g。(F1 和 F2 分别是 C-h 和 C-x 6 的别名)。该列表并非固定不变;用户可通过自定义 Emacs 来创建新的前缀按键,甚至可以移除部分标准前缀按键,但 不建议多数用户这样操作 ;例如,若移除 C-x 4 的前缀定义,那么 C-x 4 C-f 将变成无效的按键序列。详见《自定义按键绑定》章节。
在按下前缀按键后,再按下帮助字符( C-h 或 F1 ),会显示所有以此前缀开头的命令列表。此规则有一个例外: ESC C-h 等价于 C-M-h ,二者功能完全不同。不过,用户可以使用 F1 来查看所有以 ESC 开头的命令列表,即 ESC F1 。
4. 鼠标输入
默认情况下,Emacs 支持所有常规鼠标操作,例如单击鼠标左键定位光标、拖动鼠标指针选中区域。所有鼠标操作都可用于绑定命令,绑定方式与将命令绑定到键盘事件的方式完全相同(详见《按键》章节)。本节概述 Emacs 中的鼠标使用方法;关于 Emacs 鼠标命令的更多细节,可参考《用于编辑的鼠标命令》及其后续章节。
单击鼠标左键时,Emacs 会接收到 mouse-1 事件。若要查看该事件绑定的命令,你可以按下 C-h c ,随后单击鼠标左键。同理,鼠标中键对应的事件为 mouse-2 ,鼠标右键对应的事件为 mouse-3 。如果你的鼠标带有滚轮,滚轮事件通常会被绑定为 wheel-down (滚轮向下)、 wheel-up (滚轮向上),或 mouse-4 、 mouse-5 ,具体取决于操作系统的配置。
通常而言,旧式 X 系统与终端(详见《在文本终端中使用鼠标》)会上报 mouse-4 和 mouse-5 事件,其他所有系统则会上报 wheel-down 和 wheel-up 事件。
部分鼠标还配备了水平滚轮,触控板一般也支持水平滚动功能。在终端与旧式 X 系统之外的所有系统中,这类事件会被上报为 wheel-left (向左滚动)和 wheel-right (向右滚动);而在终端与旧式 X 系统中,对应的事件名称为 mouse-6 和 mouse-7 。
你也可以将键盘修饰键与鼠标事件组合使用,例如,你可以绑定一个特殊命令,使其在按住 Meta 键并单击鼠标中键时触发。这种情况下,对应的事件名称为 M-mouse-2 。
在部分系统中,你还可以为触屏事件绑定处理命令。此类事件的名称为 touchscreen-update (触屏更新)与 touchscreen-end (触屏结束)。
5. 按键与命令
本手册中多处会介绍特定按键的功能,但 Emacs 并非直接为按键赋予含义,而是先为 命名命令 定义功能,再通过 按键绑定 的方式,将按键与命令关联,以此赋予按键对应的作用。
每个命令都有一个由程序员定义的名称,这类名称通常由若干英文单词通过连字符连接而成,例如 next-line (下移一行)或 forward-word (前移一词)。在 Emacs 内部,每个命令本质上是一种特殊的 Lisp 函数,命令对应的具体操作,都是通过执行该函数来完成的。相关内容可参见《Emacs Lisp 参考手册》中的《什么是函数》章节。
按键与命令之间的绑定关系,会被记录在名为 键映射表(keymaps) 的表格中,详见《键映射表》章节。
当我们说 “快捷键 C-n 可实现垂直向下移动一行” 时,其实是简化了一个在日常使用中无需关注、但对 Emacs 定制至关重要的细节:真正执行 “垂直向下移动” 操作的是命令 next-line , C-n 之所以能实现该功能,是因为它被绑定到了 next-line 命令上。如果你将 C-n 重新绑定到 forward-word 命令,那么此后按下 C-n 时,光标就会向前移动一个单词,而非向下移动一行。
在本手册中,我们常会将 C-n 这类按键直接称作 “命令”,但严格来说,按键只是被绑定到了对应的命令上。 一般情况下,我们会在提及触发命令的按键后,用 括号 标注出真正执行操作的命令名称。例如,“命令 C-n (next-line) 可使光标垂直向下移动”,这句话的含义是: next-line 命令的功能是让光标垂直下移,而快捷键 C-n 默认与该命令绑定。
既然我们正在讨论定制相关的内容,就需要向你介绍 变量 的概念。在对命令的描述中,你常会看到这样的表述:“若要修改此功能,请设置变量 mumble-foo ”。变量是用于存储数值的命名标识符,本手册中介绍的大部分变量都支持用户定制:Emacs 的部分命令或功能模块会读取这些变量的值,并根据你设定的数值调整自身的行为逻辑。在你暂时没有定制需求时,可以忽略这些变量相关的内容;当你需要进行定制时,先阅读《变量》章节的基础内容,再查看各具体变量的说明,就能理解其作用了。
6. 触摸屏输入与虚拟键盘
Emacs 最初的设计,默认其用户是在台式计算机或计算机终端上使用它,这类设备会配备键盘,或许还会搭配鼠标这类合适的指针设备(参见《鼠标输入》章节)。
Emacs 同样支持接收来自其他输入设备的指令,即使用户将它安装在那些用其他输入方式、取代了传统 “键盘 + 鼠标” 组合的计算机上,也能顺畅地与之交互。
6.1. 触摸屏上使用 Emacs
触摸屏输入,是指通过手指、手写笔等操控工具,在显示 Emacs 窗口的显示器或计算机终端上执行点触、移动等操作,以此对窗口内的内容进行控制。
系统会将操控工具的 点触顺序 与 位置 这两个要素,和预先定义好的操作模式(称为 “手势”)进行比对,一旦匹配成功,就会在操控工具下方的文本区域执行该手势对应的一系列操作。目前 Emacs 支持识别的手势包括以下几种:
轻点(Tapping)
快速点触屏幕后抬起操控工具,该操作会选中被点触的窗口,并执行该窗口内此位置上绑定到
mouse-1的命令。如果该位置存在链接(参见《使用鼠标跳转至引用内容》章节),Emacs 会直接跳转至该链接(此行为与模拟mouse-1事件的操作有所区别)。滚动(Scrolling)
在屏幕上执行垂直或水平方向的连续移动,窗口会以操控工具初始位置为基准,朝着移动的方向滚动内容。用户可通过配置选项
touch-screen-enable-hscroll,控制该手势是否触发水平滚动功能(参见《水平滚动》章节)。拖拽(Dragging)
长按屏幕(将操控工具置于屏幕上并保持片刻)后再移动到其他位置,该操作会将光标移动到操控工具的初始位置,且在移动过程中选中工具下方的文本,效果等同于按住鼠标左键并拖动鼠标。相关内容可参见《用于编辑的鼠标命令》章节。
在触摸屏上精确定位操控工具存在一定难度,这会对文本选中操作造成影响。启用用户配置选项
touch-screen-word-select后,拖拽操作会自动选中操控工具下方的 完整单词 (默认情况下,选中范围仅会扩展至工具下方的单个字符)。同理,单次手势操作有时难以精准选中目标文本的全部内容。若启用用户配置选项
touch-screen-extend-selection,在窗口内的光标或选区标记位置轻点,即可启动一次新的 “拖拽” 手势,后续的移动操作会沿着移动方向扩展选区范围。操控工具在屏幕上的遮挡,可能会导致用户难以精准调整选区范围,而在回显区显示光标位置可缓解这一问题。当变量
touch-screen-preview-select的值非空时,在操控工具移动过程中,回显区(参见《回显区》章节)会显示光标所在行的内容,下方还会额外显示一行,用于标注光标在该行内的相对位置。捏合(Pinching)
将两个操控工具分开放置在屏幕上,再通过调整二者位置来增大或缩小间距,文本缩放比例(参见《文本缩放》章节)会随间距的变化比例同步调整。
当操控工具在屏幕上的停留时间超过 0.7 秒时,Emacs 会判定为一次长按操作。用户可通过自定义变量 touch-screen-delay ,调整该判定延迟的时长。
6.2. 搭配虚拟键盘使用 Emacs
当系统未连接物理键盘时,其窗口系统可能会提供一款屏幕内置的键盘 —— 即广泛使用的 虚拟键盘(virtual keyboard) 。该键盘包含多行可点击的按键,能够像实体键盘一样,向应用程序传递键盘输入指令。
由于虚拟键盘会占用宝贵的屏幕空间,因此当获得焦点的程序未请求文本输入时,虚拟键盘会处于隐藏状态。这就要求各类程序在准备好接收键盘输入后,主动将虚拟键盘调出。运行于 X 窗口系统的设备,会自动判断是否需要显示虚拟键盘;但在安卓等其他系统中,显示虚拟键盘的任务则由 Emacs 负责,触发场景通常为 触摸屏轻点手势 (参见《在触摸屏上使用 Emacs》章节),或是迷你缓冲区被调用时(参见《迷你缓冲区》章节)。
当轻点手势触发某条命令执行时,Emacs 会在列表 touch-screen-set-point-commands 中检索该命令,以此判断它是否用于设置光标位置。若该命令存在于列表中,且新光标位置对应的文本并非只读状态,Emacs 会激活虚拟键盘,以便用户输入内容。
touch-screen-set-point-commands 的默认值仅包含命令 mouse-set-point (参见《用于编辑的鼠标命令》章节),该命令是 mouse-1 的默认绑定命令,同时也是触摸屏轻点手势对应的默认命令。
用户配置选项 touch-screen-display-keyboard 可强制 Emacs 在 所有轻点手势触发时都显示虚拟键盘 ,即便对应文本处于只读状态;该选项也支持局部缓冲区配置,在此情况下,只要轻点显示该缓冲区的窗口,Emacs 就会自动调出虚拟键盘。
除此之外,Emacs 还提供了若干用于显示或隐藏虚拟键盘的函数,具体细节可参见《Emacs Lisp 参考手册》中的《屏幕内置键盘》章节。
由于 Emacs 在执行命令期间,可能无法调出虚拟键盘,因此它在那些通常不配备物理键盘的窗口系统中,实现了一项特殊功能:双击设备上始终存在的某个硬件按键,即可触发退出操作。相关内容可参见《退出与终止》章节。
X 窗口系统默认未启用此类按键,但用户可通过变量 x-quit-keysym 进行配置;在安卓系统中,默认的触发按键为音量减小键,用户同样可通过变量 android-quit-keycode 对其进行修改。
大多数为虚拟键盘设计的输入法,其文本编辑逻辑与桌面端输入法存在差异。
在传统桌面窗口系统中,输入法会直接在屏幕上显示正在输入的字符内容,待用户确认后,再将对应的按键事件发送给 Emacs。
与之不同的是,虚拟键盘输入法会 直接对各窗口帧的活动窗口执行文本编辑操作 ,这种方式在 X 窗口系统中被称为 文本转换 或 字符串转换 。
只要局部缓冲区变量 text-conversion-style 的值非空,Emacs 就会启用这类输入法,这一机制通常适用于 text-mode (文本模式)与 prog-mode (编程模式)的派生模式。
文本转换操作的执行是异步的:当 Emacs 收到输入法的转换请求,且当前未处于 “已读取前缀按键、等待完整按键序列” 的状态时(参见《按键》章节),就会触发文本转换。转换完成后,系统会发送一个 text-conversion 事件,具体可参见《Emacs Lisp 参考手册》中的《杂项事件》章节。
若输入法需要对缓冲区中的某一区域进行处理,该区域会被标记为 待转换区域*(或 *称预处理区域 )。变量 text-conversion-face 用于控制是否使用特殊的字体样式显示该区域,若启用,则可指定具体的字体样式。
7. 启动 Emacs
调用 Emacs 的常规方式是在 Shell 中执行命令 emacs 。在图形界面终端中运行 Unix Shell 时,你可以通过 emacs & 在后台启动 Emacs,这种方式不会占用终端窗口,你仍然可以在终端中执行其他 Shell 命令。(在微软视窗系统中启动 Emacs 的类似方法,参见《微软视窗系统下的 Emacs 启动方式》章节)。
Emacs 启动后,初始窗口会显示一个名为 *GNU Emacs* 的特殊缓冲区。这个启动界面包含了关于 Emacs 的介绍信息,以及对新手用户实用的常用任务链接。例如,激活 “Emacs Tutorial” 链接会打开 Emacs 教程,其功能等同于执行命令 C-h t (help-with-tutorial)。激活链接的方式有两种:一是将光标移至链接处并按下 RET (回车键),二是用 mouse-1 (鼠标左键) 点击该链接。
你可以通过 命令行参数 ,让 Emacs 在启动时直接打开一个或多个文件。例如,执行 emacs foo.txt 会启动 Emacs,并在缓冲区中显示文件 foo.txt 的内容。该功能主要是为了兼容其他编辑器 —— 这类编辑器通常设计为从 Shell 启动,用于短时间的编辑会话。若以这种方式启动 Emacs,初始窗口会被拆分为两个子窗口:一个显示指定的文件,另一个显示启动界面。相关内容参见《多窗口》章节。
通常情况下,每次编辑文件都重新启动 Emacs 既无必要,也会造成资源浪费。推荐的使用方式是:登录系统后只启动一次 Emacs,所有的编辑工作都在同一个 Emacs 会话中完成。关于打开多个文件的方法,参见《文件处理》章节。采用这种方式使用 Emacs 时,会话会累积大量实用的上下文信息,例如 king ring(剪切环) 、 registers(寄存器) 、 undo history(撤销历史) 和 mark ring data(标记环数据) ,这些功能会让编辑操作更加便捷,本手册后续章节会对其进行详细介绍。
当 Emacs 处于运行状态时,若想从其他程序中编辑文件,你可以借助辅助程序 emacsclient ,在已有的 Emacs 会话中打开目标文件。相关内容参见《将 Emacs 用作服务器》章节。
Emacs 支持其他类型的命令行参数,例如指定加载特定的 Lisp 文件、设置初始窗口的位置等。具体内容参见《Emacs 启动命令行参数》章节。
若变量 inhibit-startup-screen 的值非空,Emacs 将不会显示启动界面。这种情况下,如果命令行中指定了一个或多个文件,Emacs 会直接显示这些文件;反之,会打开一个名为 *scratch* 的缓冲区,你可以在该缓冲区中交互式地执行 Emacs Lisp 表达式。相关内容参见《Lisp 交互缓冲区》章节。你可以通过 自定义功能 (参见《简易自定义界面》章节),或编辑初始化文件(参见《Emacs 初始化文件》章节)来设置 inhibit-startup-screen 的值 4。
你也可以通过设置变量 initial-buffer-choice ,强制 Emacs 在启动时显示指定的文件或目录 —— 只需将该变量的值设为对应文件或目录的路径字符串即可。 initial-buffer-choice 的值也可以是一个无参函数,该函数需要返回一个缓冲区供 Emacs 显示。若 initial-buffer-choice 的值非空,即便命令行中指定了文件,Emacs 仍会加载这些文件,但不会在启动时直接显示。
8. 退出 Emacs
C-x C-c- 退出 Emacs(执行
save-buffers-kill-terminal命令)。 C-z- 在文本终端中,挂起 Emacs;在图形界面中,将选中的窗口图标化(或 “最小化”)(执行
suspend-frame命令)。
退出 Emacs 即终止 Emacs 程序。操作方法为按下 C-x C-c (save-buffers-kill-terminal)。采用双字符按键序列是为了防止误触。若按下该组合键时,存在任何已修改但未保存的文件缓冲区,Emacs 会首先提示你保存这些缓冲区。若你未将所有缓冲区保存,程序会再次请求确认,因为未保存的修改将会丢失。此外,若仍有子进程在运行,Emacs 也会要求确认退出操作 —— 退出 Emacs 的同时会终止这些子进程(参见《从 Emacs 执行 Shell 命令》章节)。
若你将 Emacs 用作服务器, C-x C-c 的行为会略有不同。在客户端窗口中按下该组合键,只会关闭对应的客户端连接。详见《将 Emacs 用作服务器》章节。
你可以选择让 Emacs 在退出时记录特定的会话信息,例如当前打开的文件。这些信息会在下次启动 Emacs 时生效。详见《保存 Emacs 会话》章节。
若变量 confirm-kill-emacs 的值为非 nil,按下 C-x C-c 时,Emacs 会将该变量的值视作一个断言函数并调用它。若函数返回值为非 nil,则终止当前会话;反之,Emacs 会继续运行。将函数 yes-or-no-p 设为 confirm-kill-emacs 的值是一种便捷的用法。该变量的默认值为 nil 。
若变量 confirm-kill-processes 的值为 nil ,Emacs 在终止其启动的子进程前不会请求确认。该变量的默认值为 t 。
若需进一步自定义 Emacs 的退出行为,请参见《GNU Emacs Lisp 参考手册》中的《退出 Emacs》章节。
若需直接退出 Emacs 且不弹出保存提示,可执行命令 M-x kill-emacs 。
C-z 对应命令 suspend-frame 。在图形界面中,该命令会将选中的 Emacs 窗口最小化(或图标化),隐藏窗口的同时保留恢复的可能(具体隐藏方式取决于对应的窗口系统)。在文本终端中, C-z 会挂起 Emacs,临时暂停程序运行并将控制权交还给父进程(通常为 Shell);在多数 Shell 环境中,你可以通过命令 %emacs 恢复挂起的 Emacs 进程。
文本终端通常会监听一些特殊字符,用于终止或挂起当前运行的程序。但在 Emacs 运行期间,终端的这一功能会被禁用。Emacs 将 C-z 和 C-x C-c 设为退出相关快捷键,灵感源于部分操作系统中用 C-z 和 C-c 终止或暂停程序的设计,但二者仅存在这一设计关联,与操作系统本身的功能并无直接联系。你可以自定义这些按键,将其绑定为任意命令(参见《键盘映射》章节)。
9. 基本编辑命令
本节将介绍文本输入、修改以及文件保存的基础操作。如果你是 Emacs 新手,建议先通过按下组合键 C-h t (help-with-tutorial) 运行 Emacs 实践教程。
9.1. 插入文本
按下对应按键,即可输入普通可打印字符(例如 a、B、3、=)。输入的字符会被插入到光标位置的缓冲区中,且光标会随文本插入自动后移,始终停留在新插入文本的后方。相关概念详见光标章节。
按下 RET 回车键(newline)可以换行并开始新的一行。(你的键盘上 RET 键可能标注为 Return 、 Enter ,或是一个指向左侧的特殊箭头符号,但本手册统一称其为 RET 。)该命令会在缓冲区中插入一个换行符,随后根据当前主模式自动缩进(详见缩进章节)。
- 若光标位于行尾,执行该命令会在当前行下方新建一行空白行,并对新行进行缩进;
- 若光标位于行中间,则会在光标位置将当前行拆分为两行,并缩进新行。
- 如需关闭自动缩进功能,你可以选择禁用 Electric Indent 模式(详见缩进便捷功能章节),或是按下
C-j键 —— 该快捷键仅插入换行符,不会触发自动缩进。
正如本手册后续章节所述,你可以通过开启次要模式(minor mades)来修改 Emacs 的文本插入行为。例如:
- 自动换行模式(Auto Fill mode) 会在文本行过长时自动换行(详见文本填充章节);
- 覆盖模式(Overwrite mode) 会让新输入的字符覆盖原有文本,而非将原有文本右移(详见次要模式章节)。
只有可打印字符能够通过按键直接输入;其他按键仅作为编辑命令触发相应功能,不会插入按键对应的字符。例如,默认情况下按下 DEL 键会执行 delete-backward-char 命令(部分模式会将其绑定为其他命令),而非插入一个字面意义上的 DEL 字符(ASCII 编码为 127)。
若要插入非打印字符,或是键盘上没有对应按键的字符,需要先按下 C-q (quoted-insert) 进行转义。 C-q 有两种使用方式:
- 按下
C-q后,再按下任意非打印字符(包括C-g),即可插入该字符本身。例如C-q DEL会插入一个字面意义上的 DEL 字符。 - 按下
C-q后,输入一串八进制数字,即可插入对应八进制编码的字符。你可以输入任意位数的八进制数字,遇到非数字字符时,输入序列即终止。- 若终止符为
RET,则该RET仅用于结束序列,不会额外执行换行操作; 若终止符为其他非数字字符,则该字符会在终止序列后作为普通输入生效。例如,输入
C-q 1 0 1 B会插入字符 AB(八进制 101 对应 ASCII 字符 A)。注意:在普通的非二进制覆盖模式下,八进制序列输入功能会被禁用。这样设计是为了方便你直接插入数字字符,而非用其覆盖原有文本。
- 若终止符为
若你希望使用十进制或十六进制编码(而非八进制)插入字符,可以将变量 read-quoted-char-radix 的值设为 10(十进制)或 16(十六进制)。当基数设为 16 时,字母 a 到 f 会被当作数字的一部分参与编码,且不区分大小写。
部分常用的 Unicode 字符可通过以 C-x 8 开头的快捷键插入。例如:
- 按下
C-x 8 [会插入左单引号 ‘(Unicode 编码 U+2018,也被称为左弯引号); - 同理,
C-x 8 ]、C-x 8 {、C-x 8 }会分别插入右单引号 ’、左双引号 “ 和右双引号 ”。 - 此外,若你的键盘上的
Alt键可正常工作,按下Alt+[等效于C-x 8 [(插入左单引号),以此类推(除非Alt键后紧跟的是RET键)。若要查看所有支持C-x 8快捷键的字符,可按下C-x 8 C-h查询。
你也可以使用命令 C-x 8 RET (insert-char) 插入字符:该命令会通过迷你缓冲区提示你输入目标字符的 Unicode 名称或编码。
- 输入名称时,命令支持补全功能(详见补全章节);
- 输入编码时,需使用十六进制数(Unicode 标准格式),或带基数标识的数字(例如八进制数
#o23072)。相关规则详见《Emacs Lisp 参考手册》中的整数基础章节。 - 输入完成后,对应字符会被插入到缓冲区中。
以下几种操作均可插入同一个左单引号 ‘ 字符:
C-x 8 RET left single quotation mark RET C-x 8 RET left sin TAB RET #利用补全功能 C-x 8 RET 2018 RET #输入十六进制编码) C-x 8 [ Alt+[ #若 Alt 键可用 ` #在智能引号模式(Electric Quote mode) 下
在执行 C-q 或 C-x 8 ... 类命令时,可添加数字参数(详见数字参数章节),指定要插入的字符重复次数。
除 C-x 8 外,你还可以通过以下方式插入特殊字符:先按下 C-u C-x \ iso-transl RET 选择对应的临时输入法,随后按下 C-x \ [ ,同样可以插入左单引号 ‘(详见临时输入法章节)。
此外,在部分场景下,即便不使用 C-x 8 类命令,输入特定格式的引号也会自动转换为弯引号:
输入 `like this' (使用反引号和单引号),会自动转换为 ‘like this’ ;
输入 ``like this'' (使用双反引号和双单引号),会自动转换为 “like this” 。相关规则详见引号格式章节。
9.2. 更改光标位置
若要执行 插入字符之外 的操作,你必须掌握移动光标的方法(详见光标章节)。快捷键 C-f 、 C-b 、 C-n 、 C-p 分别用于将光标向右、向左、向下、向上移动。你也可以使用多数键盘上的 arrow keys(方向键) : RIGHT 、 LEFT 、 DOWN 、和 UP 移动光标,但许多 Emacs 用户认为,方向键的操作效率低于控制键,原因是操作方向键时需要移动手部位置。
你还可以点击鼠标左键,将光标移动到点击的位置。Emacs 同时提供了多种功能更丰富的快捷键,可实现光标的灵活移动。
C-f- 向前移动一个字符 (
forward-char) RIGHT- 对应命令 (
right-char) 功能与C-f类似,但在从右至左排版的段落中行为会有差异(详见双向文本编辑章节) C-b- 向后移动一个字符 (
backward-char) LEFT- 对应命令 (
left-char) 功能与C-b类似,但在从右至左排版的段落中行为会有差异(详见双向文本编辑章节) C-nDOWN- 向下移动一行 (
next-line) 。该命令会尽量保持光标水平位置不变,例如若光标初始在某行中间位置,移动后会停在下一行的对应水平位置 C-pUP- 向上移动一行 (
previous-line) 。与C-n类似,该命令会保持光标在该行的水平位置 C-aHome- 移动到行首 (
move-beginning-of-line) C-eEnd- 移动到行尾 (
move-end-of-line) M-f- 向前移动一个单词 (
forward-word)。详见单词章节 C-RIGHTM-RIGHT- 功能与
M-f类似,但在从右至左排版的段落中会向后移动一个单词(详见双向文本编辑章节) M-b- 向后移动一个单词 (
backward-word)。详见单词章节 C-LEFTM-LEFT- 功能与
M-b类似,但在从右至左排版的段落中会向前移动一个单词(详见双向文本编辑章节) M-r在不移动屏幕文本的前提下,将光标定位到窗口内垂直居中行的左边缘;连续重复执行该命令时,光标会按循环顺序依次定位到窗口的首行、末行左边缘,以此类推,对应命令 (
move-to-window-line-top-bottom) 。- 若带数字参数,则表示将光标定位到从窗口顶部向下数的第 n 行(参数 0 代表窗口首行);
- 若参数为负数,则表示从窗口底部向上数的第 n 行(参数 -1 代表窗口末行)。
关于数字参数的更多用法,详见数字参数章节。
M-<- 移动到缓冲区开头 (
beginning-of-buffer) 。若带数字参数n,则移动到距离缓冲区开头n/10比例的位置。在图形界面中,快捷键C-HOME等效于此命令。 M->- 移动到缓冲区末尾 (
end-of-buffer) 。在图形界面中,快捷键C-END等效于此命令。 C-vPageDownnext- 向下滚动一屏内容,并在需要时将光标移动到屏幕可视区域内 (
scroll-up-command),详见滚动章节。 M-vPageUpprior- 向上滚动一屏内容,并在需要时将光标移动到屏幕可视区域内 (
scroll-down-command),详见滚动章节。- 快捷键命名逻辑:在 Emacs 术语中,
prior对应键盘上的PageUp键,next对应PageDown键,两者仅命名不同,功能完全一致。
- 快捷键命名逻辑:在 Emacs 术语中,
M-g c- 读取一个数字
n,并将光标移动到缓冲区的第n个字符位置(缓冲区起始位置为 1)。若光标当前位于或紧邻缓冲区中的某个数字,则该数字会作为n的默认值,直接按下RET回车键即可使用。你也可以通过给M-g c添加数字前缀参数的方式指定 n。 M-g M-gM-g g读取一个数字
n,并将光标移动到第n行的开头(对应命令goto-line,缓冲区首行编号为 1)。若光标当前位于或紧邻缓冲区中的某个数字,则该数字会作为n的默认值,直接按下RET回车键即可使用。你也可以通过给M-g M-g添加数字前缀参数的方式指定 n。若为该命令添加普通前缀参数,其行为会有所不同,详见创建与选择缓冲区章节;此外,你也可以使用goto-line-relative命令,将光标移动到相对于受限缓冲区可见部分的指定行。goto-line命令拥有独立的历史记录列表(详见迷你缓冲区历史章节)。通过自定义用户选项goto-line-history-local,你可以选择让所有缓冲区共享同一历史列表(默认行为),或是为每个缓冲区分别配置独立的历史列表。M-g TAB- 读取一个数字
n,并将光标移动到当前行的第n列(最左侧列编号为 0)。若为该命令添加前缀参数,则直接以参数的数值作为目标列号。 C-x C-n- 将光标当前所在列设为当前缓冲区的半永久目标列(对应命令
set-goal-column)。当半永久目标列生效时,C-n、C-p、PageUp和PageDown命令在垂直移动光标后,会自动将光标定位到该目标列,或尽可能接近该列的位置。目标列设置会一直生效,直至被手动取消。 C-u C-x C-n- 取消半永久目标列设置。此后
C-n和C-p命令会恢复默认行为,即保持光标水平位置不变。
当缓冲区中的某行文本长度超过窗口宽度时,Emacs 通常会将其拆分为多行显示,这种显示行也被称为 可视行(visul lines) 。为方便操作, C-n 、 C-p 以及方向键 down 、 up 均基于可视行移动光标。若你希望这些命令基于 逻辑行(logical line) (即缓冲区中实际存储的文本行)移动,可将变量 line-move-visual 的值设为 nil ;此时若某一逻辑行被拆分为多行可视行,光标会直接跳过额外的可视行。相关细节详见续行章节,变量设置方法详见变量章节。
与 C-n 、 C-p 不同,Emacs 中大多数针对行的命令均基于逻辑行(logical lines)执行。例如 C-a (move-beginning-of-line 移动到行首) 和 C-e ( move-end-of-line 移动到行尾)命令,分别作用于逻辑行的开头和结尾。后续遇到基于可视行工作的命令(如 C-n 、 C-p )时,我们会特别指出。
当 line-move-visual 的值为 nil 时,你还可以将变量 track-eol 设为非 nil 值。此时若光标初始位于某逻辑行的行尾,执行 C-n 或 C-p 命令后,光标会移动到下一行或上一行的行尾。该变量的默认值为 nil 。
默认情况下,若在缓冲区的最后一行执行 C-n 命令,光标会停在该行末尾不再移动。但如果你将变量 next-line-add-newlines 设为非 nil 值,在缓冲区最后一行执行 C-n 时,Emacs 会自动在末尾新建一行,并将光标移动到新行中。
9.3. 删除文本
DELBACKSPACE- 删除光标前的字符;若 选区处于激活状态 ,则删除选区内的文本 (
delete-backward-char) Delete- 删除光标后的字符或 字符簇 ;若选区处于激活状态,则删除选区内的文本 (
delete-forward-char) C-d- 删除光标后的字符 (
delete-char) C-k- 剪切至行尾 (
kill-line) M-d- 向前剪切至下一个单词的末尾 (
kill-word) 即移除光标右边一整个词 M-DELC-<backspace>M-BACKSPACE- 向后剪切至前一个单词的开头 (
backward-kill-word),即移除光标左侧一整个词
DEL (delete-backward-char) 命令会删除光标前的字符,并将光标及后方所有字符向前移动一位。若光标原本位于行首,执行该命令会删除前一行的换行符,从而将当前行与上一行合并。
但如果 选区处于激活状态 ,DEL 命令的行为会变为删除选区内的所有文本。关于选区的详细说明,详见标记与选区章节。
在大多数键盘上, DEL 键标注的是 BACKSPACE ,但本手册统一称其为 DEL(注意不要与 Delete 键混淆,后文会介绍 Delete 键的功能)。在部分文本终端中,Emacs 可能无法正确识别 DEL 键,若遇到此问题可参考当 DEL 键无法删除时章节的解决方案。
Delete (delete-forward-char) 命令的删除方向与 DEL 相反:它会删除光标后的字符,也就是光标当前所在位置的字符。若光标原本位于行尾,执行该命令会将当前行与下一行合并。和 DEL 命令一样,当选区处于激活状态时,该命令会删除选区内的文本(详见标记与选区章节)。
此外,若光标后的字符与后续字符组合成一个 字符簇 (即多个字符合并为一个显示单元), Delete 命令会一次性删除整个字符簇序列。这一点与 DEL 命令不同 —— DEL 命令始终只会删除单个字符,即便该字符属于某个字符簇。
C-d (delete-char) 命令同样用于删除光标后的字符,功能与 Delete 键类似,但 该命令不会受选区激活状态的影响 ,始终只删除光标后的单个字符。
关于上述删除命令的更多详细信息,详见删除操作章节。
C-k (kill-line) 命令用于一次性剪切整行内容。
- 若在一行文本的开头或中间位置按下
C-k,该命令会剪切从光标位置到行尾的所有文本; - 若在行尾按下
C-k,该命令会将当前行与下一行合并。
关于 C-k 及相关命令的更多信息,详见剪切与移动文本章节。
9.4. 撤销更改
C-/C-x uC-_- 撤销一条撤销记录项 —— 通常对应一次命令的操作效果(
undo撤销功能)。(在纯文本模式的显示终端中,第一个快捷键可能无法使用)
Emacs 会记录缓冲区文本中发生的所有更改操作,因此你可以撤销近期的修改。这项功能通过 undo 实现,该命令绑定了三组快捷键: C-/ 、 C-x u 和 C-_ 。默认情况下,执行该命令会撤销上一次的修改操作,并将光标移回修改前的位置。需要注意的是,撤销命令 仅作用于缓冲区的文本更改 ,无法用于撤销光标的移动操作。
在支持所有按键组合 Control 修饰键的终端上,调用撤销功能最便捷的方式是使用 C-/ ,因为该快捷键无需搭配 Shift 键。而在仅支持 ASCII 控制字符的终端中, C-/ 这一组合键本身并不存在,但在多数此类终端中,按下 C-/ 实际会向 Emacs 发送 C-_ 指令;此外还有不少终端允许你在输入 C-_ 时省略 Shift 键(等效于按下 C-- ),因此这种方式也成了调用撤销功能的便捷选择。
尽管每次编辑命令通常会在撤销记录中生成独立的条目,但一些极为简单的命令可能会被合并为一条记录。反之,对于部分复杂命令,一条撤销记录有时仅能覆盖其操作的一部分内容。
重复按下 C-/ (或其等效快捷键),每一次重复操作都会撤销更早的一项修改,直至撤销记录的上限。若所有已记录的修改操作都已被撤销,撤销命令会输出一条错误提示信息,且不会执行任何操作。
如需了解撤销命令的更多详情,请参见《撤销》章节。
9.5. 文件操作
你在 Emacs 缓冲区中输入的文本,仅会在当前 Emacs 会话期间保留。若要将文本永久保存,你必须将其写入 文件 中。
假设你的主目录下有一个名为 test.emacs 的文件。若要在 Emacs 中开始编辑该文件,输入以下指令:
C-x C-f test.emacs RET
在此命令中,文件名是作为 C-x C-f (find-file) 命令的参数传入的。该命令会通过 minibuffer迷你缓冲区 读取参数,你需要按下回车键( RET )来确认参数输入(详见《迷你缓冲区》章节)。
Emacs 执行这条命令的方式是 访问 该文件:它会创建一个新的缓冲区,将文件内容复制到该缓冲区中,随后显示这个缓冲区以供编辑。如果你修改了缓冲区中的文本,可以按下 C-x C-s (save-buffer),将修改后的内容写回 test.emacs 文件,实现文本的永久保存。在执行保存操作之前,修改后的文本仅存在于 Emacs 缓冲区中,源文件 test.emacs 不会发生任何变化。
若要新建一个文件,只需直接使用 C-x C-f 命令访问这个尚不存在的文件即可。此时 Emacs 会创建一个空缓冲区,你可以在其中输入需要写入该文件的文本内容。当你第一次按下 C-x C-s 保存这个缓冲区时,Emacs 才会在系统中实际创建该文件。
如需了解在 Emacs 中使用文件的更多详情,请参见《文件处理》章节。
9.6. 帮助功能
如果你忘记了某个按键的功能,可以按下 C-h k (describe-key),紧接着按下想要查询的按键;例如,输入 C-h k C-n 就能查看 C-n 按键的作用。
前缀键 C-h 代表 “帮助” 功能。功能键 F1 是 C-h 的等效快捷键。除了 C-h k 之外,还有许多其他的帮助命令,可提供各类不同的帮助信息。
有关详情,请参见《帮助》章节。
9.7. 空行处理
本节介绍用于插入和删除空行的专用命令与操作技巧。
- C-o
- 在光标后方插入一行空行 (
open-line) 。 - C-x C-o
- 将连续的多行空行删减为仅保留一行 (
delete-blank-lines) 。
我们此前已经了解到,按下 RET (newline) 可以新建一行文本。不过,先插入一行空行,再在空行中输入目标文本,会让操作过程更清晰易见。使用快捷键 C-o (open-line)即可轻松实现这个操作,该命令会在光标位置后方插入一个换行符,同时保持光标停留在换行符之前。执行 C-o 后,你就可以直接在新行中输入文本内容。
连续按下多次 C-o ,即可插入多行空行;也可以为该命令指定 数字参数 ,以此设定需要插入的空行数量,具体用法详见《数字参数》章节。若你设置了 填充前缀 ,当在行首位置按下 C-o 时,该命令会自动在新插入的空行中添加填充前缀,相关内容详见《填充前缀》章节。
删除多余空行的便捷方法是使用命令 C-x C-o (delete-blank-lines) 。当光标处于连续多行空行的范围内时, C-x C-o 会将这些空行删减为仅保留一行;当光标位于单行空行上时, C-x C-o 会直接删除这行空行;当光标位于非空行上时, C-x C-o 会删除该行之后的所有空行(如果存在的话)。
9.8. 续行(Continuation Lines)
有时,缓冲区中的一行文本——即 logical line逻辑行 ——过长而无法完全显示在窗口内,Emacs会将其显示为两行或多行 screen lines 屏幕行 (或 visual lines视觉行 )。这称为 line wrapping换行 或 continuation续行 ,而较长的逻辑行则被称为 continued line续行 。在图形界面中,Emacs 会通过左右窗口边缘的弯曲小箭头,以此标记续行的起止;在文本终端中,Emacs 则会在屏幕行的右边界显示一个反斜杠字符 ‘\‘ 来标识续行。
大多数针对 “行” 执行操作的命令,作用对象都是 逻辑行 而非屏幕行。例如, C-k 命令会删除一整行逻辑行。但前文提到的 C-n (next-line) 和 C-p (previous-line) 是两个特殊例外:它们的作用是分别将光标向下或向上移动 一行屏幕行 (详见《光标位置调整》章节)。
Emacs 也可以选择对过长的逻辑行执行 truncate截断 而非续行处理。截断模式下,每条逻辑行仅占用一行屏幕行;如果逻辑行长度超过窗口宽度,超出部分将不会被显示。在图形化界面中,被截断的行会在窗口右侧提示区显示一个小型直箭头;在文本终端中,该行的右边界则会显示一个美元符号 $。详见《行截断》章节。
默认情况下,续行的折行位置是窗口的右边缘。由于折行可能发生在单词的中间位置,这类续行的可读性会比较差。常规的解决方法是在逻辑行过长之前手动插入换行符,将其拆分为较短的行。如果你需要,也可以启用 auto-fill-mode 自动填充模式,让 Emacs 在逻辑行长度达到阈值时自动插入换行符。详见《文本填充》章节。
默认状态下,每条续行的首字符都会对齐到其所在屏幕行的行首位置。次要模式 visual-wrap-prefix-mode 及其全局版本 global-visual-wrap-prefix-mode (详见《次要模式》章节),可以让续行在显示时自动根据该行上下文生成 填充前缀 (详见《填充前缀》章节)并进行缩进。这些前缀仅用于显示效果,不会对缓冲区中的文本内容产生任何修改。
在某些场景下,你可能需要编辑包含大量超长逻辑行的文件,此时逐一添加换行符来拆分这些行并不现实。这种情况下,你可以启用 Visual Line mode 视觉行模式 ,该模式支持 word wrapping按单词折行 :Emacs 不会在窗口右边缘的位置强制折行,而是会在最接近右边缘的单词边界处(即空格或制表符的位置)进行折行。同时,视觉行模式还会重新定义部分编辑命令(如 C-a 、 C-n 、 C-k )的作用对象,使其针对 屏幕行 而非逻辑行执行操作。详见《视觉行模式》章节。
9.9. 光标位置信息
本节介绍用于获取缓冲区各部分尺寸与位置信息,以及统计单词和行数的命令。
M-x what-line- 显示光标所在行的行号。
M-x line-number-modeM-x column-number-mode- 切换当前行号或列号的自动显示功能。相关内容请参考《可选模式行功能》。若你希望在每一行前方显示行号,请参考《显示自定义》。
M-=- 统计并显示选区范围内的行数、句数、单词数和字符数 (
count-words-region) 。关于选区的说明,请参考《标记与选区》。 M-x count-words- 统计并显示整个缓冲区的行数、句数、单词数和字符数。若选区处于激活状态(详见《标记与选区》),则会改为统计选区范围内的对应数据。
C-x =- 显示光标后一个字符的字符编码、光标在缓冲区中的字符位置,以及光标所在列号 (
what-cursor-position) 。 M-x hl-line-mode- 启用或关闭当前行的高亮显示功能。相关内容请参考《光标显示》。
M-x size-indication-mode- 切换缓冲区大小的自动显示功能。相关内容请参考《可选模式行功能》。
M-x what-line 命令会在回显区显示当前行的行号。由于模式行本身就会显示当前行号(详见《模式行》),该命令通常显得有些多余。但在缓冲区被缩窄的情况下,模式行仅会显示相对于当前可访问区域的行号(详见《缓冲区缩窄》)。相比之下, what-line 会同时显示相对于缩窄区域的行号,以及相对于整个缓冲区的行号。
M-= (count-words-region) 会在回显区输出一条消息,报告选区范围内的行数、句数、单词数和字符数(选区的概念解释见《标记与选区》)。若带上前缀参数执行 C-u M-= ,该命令则会统计整个缓冲区的对应数据。
M-x count-words 命令的功能与上述命令一致,只是调用方式不同。当选区处于激活状态时,它会统计选区数据;反之,则统计整个缓冲区的数据。
C-x = (what-cursor-position) 会显示当前光标位置的相关信息,以及该位置对应的缓冲区内容。它会在回显区输出如下格式的一行内容:
Char: c (99, #o143, #x63) point=28062 of 36168 (78%) column=53 字符:c(十进制 99,八进制 #o143,十六进制 #x63) 光标位置=28062/36168(78%) 列号=53
在 “Char:” 之后,显示的是光标所在位置的缓冲区字符;括号内的内容依次为该字符对应的十进制、八进制和十六进制编码。关于 C-x = 命令显示字符信息的更多细节,请参考《国际字符集简介》。“point=” 后面的数字是光标在缓冲区中的字符计数位置(缓冲区第一个字符的位置为 1,第二个为 2,以此类推);其后的数字代表缓冲区的总字符数;括号内的百分比表示光标位置占总字符数的比例。“column=” 后面的数字是光标在窗口中的水平位置,即相对于窗口左边缘的列数。
若用户选项 what-cursor-show-names 的值不为 nil,则还会显示该字符在 Unicode 字符数据库中定义的名称。此时,括号内的内容会变为:
(99, #o143, #x63, LATIN SMALL LETTER C)
当缓冲区被缩窄,导致开头和结尾的部分文本暂时无法访问时, C-x = 会额外显示一行描述当前可访问范围的内容。例如:
Char: C (67, #o103, #x43) point=252 of 889 (28%) <231-599> column=0
其中,额外显示的两个数字分别代表光标允许处于的最小和最大字符位置,这两个数值之间的字符即为当前可访问的内容。相关内容请参考《缓冲区缩窄》。
与之相关但功能不同的还有 display-line-numbers-mode (详见《显示自定义》)。
9.10. 数字参数(Numeric Arguments)
在数学与计算机领域的术语中,参数( argument ) 指的是 “传递给函数或操作的数据”。你可以为任意 Emacs 命令指定一个数值参数(也称为 prefix argument前缀参数)。部分命令会将该参数解读为重复执行的次数。例如,为 C-f 命令指定参数 10 时,光标会向前移动 10 个字符,而非默认的 1 个。对于这类命令,不指定参数等效于参数值为 1,而负参数则会让命令的执行方向或作用效果反向。
指定数值参数的常用方式
指定数值参数最简便的方法是:按住 Meta 键的同时输入数字或负号。例如:
M-5 C-n
该操作会让光标向下移动 5 行。 M-1 、 M-2 等按键,以及 M-- ,都被绑定到了对应的命令 (digit-argument 与 negative-argument),其作用是为后续要执行的命令设置参数。不带数字的 M-- 默认表示参数值为 -1。
若要输入多位数的参数,输入第一个数字时需要按住 Meta 键,后续数字则无需再按住。例如,要向下移动 50 行,可按下:
M-5 0 C-n
需要注意的是,这个操作 并不会 像你可能预想的那样,先插入 5 个字符 0 再向下移动 1 行 —— 这里的 0 会被视作前缀参数的一部分。
(如果确实需要插入 5 个字符 0 该怎么做?可以按下 M-5 C-u 0 。其中 C-u 的作用是终止前缀参数的输入,让后续的按键触发你真正要执行的命令。请注意, C-u 的这个用法仅适用于此场景,其常规作用见下文说明。)
除了 M-1、M-2 这种方式,还可以通过 C-u (universal-argument) 搭配数字来指定数值参数;若要指定负参数,则输入 C-u 后接负号与数字即可。不带数字的负号默认表示参数值为 -1。
C-u 的特殊用法
单独按下 C-u 有一个特殊含义 ——“ 4 倍执行 ”:它会将后续命令的默认执行次数乘以 4。连续按下两次 C-u(即 C-u C-u )则是乘以 16。例如, C-u C-u C-f 会让光标向前移动 16 个字符。其他实用的组合操作包括:
C-u C-n:向下移动 4 行C-u C-u C-n:向下移动 16 行(约占屏幕的一部分)C-u C-u C-o:插入 16 个空行C-u C-k:删除 4 行文本
为自插入字符指定数值参数
在输入一个自插入字符之前指定数值参数,可以一次性插入该字符的多个副本。如果该字符不是数字,操作会非常直观:
- 例如
C-u 6 4 a会插入 64 个字符 a。 - 但这个方法不适用于插入数字字符 ——
C-u 6 4 1会被识别为指定参数值为 641,而非插入 64 个字符 1。若要实现后者,需要用另一个C-u分隔参数与要插入的数字,例如按下C-u 6 4 C-u 1,即可插入 64 个字符 1。
命令对数值参数的不同解读方式
忽略参数值,仅判断是否存在参数
部分命令只关心是否传入了参数,而不关注参数的具体数值。例如命令
M-q(fill-paragraph段落重排):无参数时仅对段落进行换行整理;有参数时则会同时对文本进行两端对齐处理。(关于M-q的更多信息,请参考《文本填充》章节)。对于这类命令,只需按下一次C-u来指定参数即可。参数作为重复次数,但无参数时执行特殊逻辑
部分命令会将参数值作为重复次数,但无参数时会执行特殊操作。例如命令
C-k(kill-line) :传入参数 n 时,会删除 n 行文本(包含行尾的换行符);但无参数时的行为特殊 —— 它会删除光标到下一个换行符之间的文本;若光标恰好位于行尾,则直接删除该行的换行符。因此,连续执行两次无参数的C-k可以删除一行非空文本,效果等同于执行带参数 1 的C-k命令。(关于C-k的更多信息,请参考《文本的删除与移动》章节)。特殊的参数处理规则
少数命令会将单独的
C-u与普通数值参数区别对待,还有极少数命令会将仅含负号的参数(M--)与参数值 -1 区别对待。这些特殊情况会在对应命令的说明中提及;设计这些特殊规则的目的是让单个命令的使用更便捷,相关细节可查阅命令的文档字符串。
术语说明
我们使用 prefix argument 前缀参数 这一术语,是为了强调这类参数需要在命令执行之前输入,以此区分迷你缓冲区参数(参考《迷你缓冲区》章节)—— 这类参数需要在调用命令之后输入。
在图形化界面中, C-0 、 C-1 等按键的作用与 M-0 、 M-1 等完全相同。
9.11. 重复执行命令
许多简单命令(例如通过单个按键或 M-x 命令名 RET 调用的命令),都可以通过为其指定一个用作 重复次数 的数值参数来重复执行(详见《数值参数》章节)。但如果待重复的命令需要提示输入内容,或者会以其他方式使用数值参数,这种方法就不再适用。
命令 C-x z (repeat) 提供了另一种多次重复执行 Emacs 命令的方式。该命令会重复执行 上一条 Emacs 命令,无论这条命令原本是什么。重复执行时会沿用该命令上一次使用的参数,不会在每次重复时重新读取新参数。
若要将命令重复执行多次,只需在按下 C-x z 后继续输入更多的 z :每多输入一个 z ,命令就会再重复执行一次。当你输入 z 以外的字符,或按下鼠标按键时,重复执行的操作就会终止。
举个例子,假设你按下 C-u 2 0 C-d 删除了 20 个字符。此时你可以通过输入 C-x z z z ,将这条删除命令(包含它的参数)再重复执行 3 次,总共删除 80 个字符。其中第一次的 C-x z 会让命令重复执行 1 次,后续每多输入一个 z ,就会再重复执行 1 次。
你还可以激活 repeat-mode 重复模式,该模式允许你通过输入单个字符,来重复执行那些绑定在 两个或更多按键组合 上的命令。例如,在你按下 C-x u ( undo 详见《撤销》章节)撤销最近一次编辑操作后,可以继续输入 u u u… 来撤销更多次编辑。同理,若要切换到相隔多个窗口的目标窗口,你可以直接输入 C-x o o o… ,而无需重复按下完整的 C-x o C-x o C-x o… 组合键。
这个功能的实现逻辑是:当你按下调用命令的完整按键序列后,Emacs 会进入一个 临时重复模式 ;可供重复执行的单键快捷操作会显示在回显区中。
并非所有命令都支持在 repeat-mode 重复模式下重复执行;你可以输入 M-x describe-repeat-maps RET 查看支持该功能的命令列表。
临时重复模式启用的单键快捷操作 不必完全相同 :例如,在你按下 C-x { 后,输入 {、}、^、v 中的任意一个字符,或是以任意顺序混合输入这些字符,都能以对应的方式调整选中窗口的大小。类似地,在按下 M-g n 或 M-g p 后,以任意顺序输入一系列 n 或 p,就能重复执行 next-error 下一个错误和 previous-error 上一个错误命令,在 *compilation* (编译)或 *grep* (全局搜索)缓冲区中进行导航(详见《编译模式》章节)。
当你输入除重复命令绑定按键之外的任意按键时,临时重复模式会立即退出,且你输入的这个按键会按常规逻辑执行对应的操作。你也可以自定义一个专用按键,使其仅用于退出临时重复模式,而不执行该按键本身对应的命令。要实现这个功能,只需将用户选项 repeat-exit-key 自定义为目标按键即可;一个很自然的选择是将 RET (回车键)设为退出键。
最后,你还可以设置让重复链在闲置一段时间后自动中断:将用户选项 repeat-exit-timeout 自定义为指定的闲置时长(单位为秒),超过这个时长后,临时重复模式就会自动关闭。
10. 迷你缓冲区(Minibuffer)
minibuffer 迷你缓冲区是 Emacs 命令读取复杂参数的区域,例如文件名、缓冲区名称、Emacs 命令名或 Lisp 表达式。我们将其称为 “minibuffer迷你缓冲区”,是因为它是一块占用屏幕空间较小的专用缓冲区。你可以在迷你缓冲区中使用常规的 Emacs 编辑命令,对参数文本进行编辑。
10.1. 使用迷你缓冲区
当迷你缓冲区处于激活状态时,它会显示在回显区中,且带有一个光标。迷你缓冲区的开头会显示一段提示信息,通常以冒号结尾。这段提示会说明当前需要输入的参数类型,以及该参数的用途。提示信息会使用 minibuffer-prompt 这个显示样式进行高亮(详见《文本显示样式》章节)。
输入迷你缓冲区参数最简便的方式是:直接输入文本内容,然后按下回车键( RET )提交参数并退出迷你缓冲区。此外,你也可以按下 C-g 键,通过取消当前请求参数的命令来退出迷你缓冲区(详见《退出与终止》章节)。
在某些情况下,提示信息中会在冒号前的括号内显示一个默认参数。如果你直接按下回车键,程序就会使用这个默认参数执行命令。例如,读取缓冲区名称的命令通常会将某个缓冲区名称设为默认值;你按下回车键后,命令就会对这个默认缓冲区执行操作。你可以通过用户选项 minibuffer-default-prompt-format 来自定义默认参数的显示方式。
如果你启用了"Minibuffer Electric Default 迷你缓冲区动态默认模式" (这是一个全局次要模式),那么一旦你修改了迷你缓冲区中的内容,Emacs 就会自动隐藏默认参数(因为此时按下回车键提交的不再是该默认值)。当你恢复迷你缓冲区的原始文本内容后,提示信息会再次显示默认参数。要启用这个次要模式,可输入 M-x minibuffer-electric-default-mode 执行命令。
由于迷你缓冲区显示在回显区中,它的使用可能会与回显区的其他用途产生冲突。如果迷你缓冲区处于激活状态时,系统输出了错误信息或提示信息,该信息会以方括号包裹的形式,显示在迷你缓冲区文本的后方,持续几秒钟后自动消失;或者在你输入任意内容后立即消失。在迷你缓冲区的使用过程中,Emacs 不会回显你按下的按键。
使用迷你缓冲区时,你可以切换到其他frame(框架),方便查阅需要输入的文本内容(详见《框架命令》章节)。默认情况下,处于激活状态的迷你缓冲区会跟随切换到新的frame。如果你将用户选项 minibuffer-follows-selected-frame 的值设为 nil,迷你缓冲区就会停留在最初打开它的frame,你必须切换回该frame,才能完成(或终止)当前命令。若将该选项的值设为一个既非 nil 也非 t 的值,那么只有在当前命令中打开了递归迷你缓冲区后(详见《Emacs Lisp 手册》中的《递归迷你缓冲区》章节),迷你缓冲区才会跟随切换frame。这个选项的主要作用是让 Emacs 的行为与 28.1 版本之前的旧逻辑保持(大致)一致。请注意,当你最终完成迷你缓冲区的操作后,命令的执行效果始终会作用于最初打开迷你缓冲区的那个frame。唯一的例外情况是:如果该frame已经不存在,命令才会在当前选中的frame(框架)中执行。
10.2. 文件名专用迷你缓冲区
诸如 C-x C-f (find-file) 这类命令,会通过迷你缓冲区读取文件名参数(详见《文件》章节)。当迷你缓冲区用于读取文件名时,其初始文本通常会以一个斜杠结尾,这段文本即为默认目录。例如,迷你缓冲区可能会显示如下内容:
Find file: /u2/emacs/src/
这里的 'Find file:' 是提示信息,'u2/emacs/src' 是默认目录。此时若输入 buffer.c 作为参数,就代表指定了文件 /u2/emacs/src/buffer.c 。关于默认目录的更多信息,请参考《文件名》章节。
你可以按下 M-n 来调取其他可选的文件名默认值,相关操作详见《迷你缓冲区历史记录》章节。
你可以使用 .. 来指定上级目录下的文件:路径 /a/b/../foo.el 等价于 /a/foo.el 。此外,也可以按下 M-DEL 来反向删除目录名称(详见《单词操作》章节)。
若要指定一个完全不同目录下的文件,你可以按下 C-a C-k 清除整个默认目录文本(详见《迷你缓冲区中的编辑操作》章节)。或者你也可以忽略默认目录,直接在其后方输入以斜杠或波浪号开头的绝对文件名。例如,你可以通过以下方式指定文件 /etc/termcap :
Find file: /u2/emacs/src//etc/termcap
连续两个斜杠会让 Emacs 忽略这对斜杠中第二个斜杠之前的所有内容。在上面的例子中, /u2/emacs/src/ 会被忽略,因此你实际指定的参数是 /etc/termcap 。如果终端支持的话,文件名中被忽略的部分会显示为灰色。(若要关闭该灰色显示功能,可执行命令 M-x file-name-shadow-mode 关闭文件名遮蔽模式。)
在补全远程文件名时(详见《远程文件》章节),连续两个斜杠的行为会略有不同:它只会让 Emacs 忽略文件名部分,保留其余内容(如连接方式、主机地址、用户名等)不变。连续输入三个斜杠,则会忽略远程文件名中的所有内容。相关细节请参考《Tramp 手册》中的《文件名补全》小节。
Emacs 会将 ~/ 解析为你的主目录。因此路径 ~/foo/bar.txt 指的是,位于主目录下 foo 目录中的文件 bar.txt 。除此之外, ~user-id/ 表示登录名为 user-id 的用户对应的主目录。波浪号之前的所有前置目录名称都会被忽略:例如路径 /u2/emacs/~/foo/bar.txt 等价于 ~/foo/bar.txt 。
在 MS-Windows 和 MS-DOS 操作系统中,用户不一定都有主目录,因此 Emacs 会采用多种替代方案。关于 MS-Windows 系统的相关配置,请参考《MS-Windows中的 HOME 目录与启动目录》章节;关于 MS-DOS 操作系统的配置,请参考《MS-DOS 操作系统中的文件名》章节。在这些系统中, ~user-id/ 格式仅对当前用户生效,也就是说,只有当用户标识与当前登录用户的用户名一致时,该格式才能正常解析。
若要阻止 Emacs 在读取文件名时自动插入默认目录,可将变量 insert-default-directory 的值设为 nil。设置后,迷你缓冲区的初始状态会变为空白,但相对文件名参数的解析仍然会基于原有的默认目录。
你同样可以在迷你缓冲区中输入远程文件名,相关内容请参考《远程文件》章节。
10.3. 迷你缓冲区中的编辑操作
迷你缓冲区本质上也是一个 Emacs 缓冲区,只是特性比较特殊,你可以使用常规的 Emacs 命令编辑参数文本。(但提示信息是 read-only 只读的,无法修改。)
由于在迷你缓冲区中按下 RET 键的作用是提交参数,因此无法用它来插入换行符。若要插入换行,你可以按下 C-q C-j ,这个组合键会插入一个 C-j 控制字符,其功能与换行符完全等价(详见《插入文本》章节)。除此之外,也可以使用 C-o (open-line) 命令来实现换行(详见《空行操作》章节)。
在迷你缓冲区中,按键 TAB 、 SPC 和 ? 通常会被绑定到补全命令,让你无需手动输入完整文本,就能快速补全目标内容,相关内容详见《补全功能》章节。和 RET 键类似,你可以通过 C-q 来插入 TAB 、 SPC 或 ? 这些字符本身。如果希望按下 SPC 和 ? 时直接插入字符,而非触发补全功能,可以在初始化文件中添加以下配置:
(keymap-unset minibuffer-local-completion-map "SPC") (keymap-unset minibuffer-local-completion-map "?")
为了操作便利,在迷你缓冲区中按下 C-a ( move-beginning-of-line 移动到行首) 时,光标会跳到参数文本的开头,而非提示信息的开头。例如,你可以通过 C-a C-k 的组合操作快速清除整个参数文本。
当迷你缓冲区处于激活状态时,回显区的表现和普通 Emacs 窗口基本一致。比如,你可以按下 C-x o 切换到其他窗口,在那里编辑文本后,再切回迷你缓冲区窗口继续输入参数。你甚至可以在其他窗口中剪切文本,回到迷你缓冲区后将其粘贴到参数中。不过迷你缓冲区窗口也存在一些限制,比如无法对其执行拆分操作(详见《多窗口》章节)。
默认情况下,迷你缓冲区窗口只占用一行屏幕空间。但如果输入的文本超过一行,它会自动扩展高度以容纳内容。变量 resize-mini-windows 用于控制迷你缓冲区的自动调整行为,其默认值为 grow-only ,也就是前面所说的 “只自动扩展,不自动收缩”。若将该变量设为 t ,当删除迷你缓冲区中的部分文本行后,窗口也会自动收缩,最小会缩至一行高度。若设为 nil,迷你缓冲区窗口不会自动调整大小,但你可以用常规的窗口调整命令手动修改其尺寸(详见《多窗口》章节)。
变量 max-mini-window-height 用于限制迷你缓冲区窗口的最大高度。若将其设为浮点数,代表窗口最大高度为框架高度的对应比例;若设为整数,则代表窗口最大可占用的行数;若设为 nil ,则禁用迷你缓冲区的自动调整功能。该变量的默认值为 0.25。
当某些命令在其他窗口中显示帮助文本时,你可以在迷你缓冲区中按下 C-M-v 来滚动查看这些帮助信息。同时也支持使用 M-PageUp 和 M-PageDown (等价于 M-prior 和 M-next )来滚动,这个功能在查看冗长的补全候选列表时尤为实用(详见《使用其他窗口》章节)。
默认情况下,Emacs 不允许在迷你缓冲区处于激活状态时,调用那些同样会使用迷你缓冲区的命令。若要启用这一功能,需要将变量 enable-recursive-minibuffers 的值设为 t 。当迷你缓冲区被递归调用时,你可能还需要启用 minibuffer-depth-indicate-mode ,让提示信息中显示当前的递归深度。
迷你缓冲区处于激活状态时,通常会进入 minibuffer-mode (迷你缓冲区模式)。这是一个 Emacs 内部模式,没有额外的特殊功能。
当迷你缓冲区未激活时,会处于 minibuffer-inactive-mode (迷你缓冲区非激活模式),此时在该区域单击鼠标左键,会打开 *Messages* 缓冲区。如果你的迷你缓冲区有专用的框架,Emacs 还会在该区域识别部分快捷键,例如按下 n 键可以新建一个框架。
10.4. 自动补全(Completion)
你通常可以借助一项名为 completion补全 的功能来辅助输入参数。该功能的作用是:在你输入部分参数文本后,Emacs 能够根据已输入的内容,自动补全剩余的全部或部分文本。
当补全功能可用时,迷你缓冲区中的部分按键(通常是 TAB 、 RET 和 SPC )会被重新绑定为专用的补全命令(详见《补全命令》章节)。这些命令会依据当前请求参数的命令所提供的补全候选列表,尝试补全迷你缓冲区中的文本。你通常可以按下 ? 来查看完整的补全候选列表。
尽管补全功能通常在迷你缓冲区中使用,但该功能有时也可在普通缓冲区中启用。相关内容详见《符号名称补全》章节。
10.4.1. 补全示例
通过一个简单示例或许能更好地理解补全功能。 M-x 命令会通过迷你缓冲区读取待执行的命令名,因此补全功能的实现逻辑是:将迷你缓冲区中的输入文本与已有的 Emacs 命令名进行匹配。假设你希望执行 auto-fill-mode (自动填充模式)命令,既可以直接输入 M-x auto-fill-mode RET 来调用,不过使用补全功能会更便捷。
若你按下 M-x a u TAB ,此时 TAB 键会查找以 “au” 开头的补全候选项(本例中即命令名)。符合条件的命令有多个,包括 auto-fill-mode 和 autoconf-mode 等,但这些命令名的共同前缀是 “auto”,因此迷你缓冲区中的 “au” 会补全为 “auto”。(在你的 Emacs 会话中,可能还定义了更多命令。例如,若存在一个名为 authorize-me 的命令,Emacs 最多只能补全到 “aut” 这一步。)
如果立即再次按下 TAB ,由于无法确定下一个字符(可能是 “-”、“a” 或 “c”),TAB 不会添加任何字符,而是会在另一个窗口中显示所有可能的补全候选列表。
接下来输入 “-f”,此时迷你缓冲区中的内容为 “auto-f”,而以该字符串开头的命令名只有 auto-fill-mode 。若此时按下 TAB ,补全功能会将参数剩余部分 “auto-fill-mode” 填充到迷你缓冲区中。
由此可见,只需输入 a u TAB - f TAB ,就能完成 "auto-fill-mode" 命令名的输入。
即便光标不在迷你缓冲区的末尾, TAB 键同样可以触发补全。这种情况下,补全功能会同时在光标位置和迷你缓冲区末尾填充文本。例如,你先输入 M-x autocm ,再按下 C-b 将光标移到 “m” 之前,此时按下 TAB ,Emacs 会在光标位置插入文本 “onf-”,并在迷你缓冲区末尾插入 “ode”,最终迷你缓冲区中的内容会变为 “autoconf-mode”。
10.4.2. 补全命令
以下是启用补全功能时,迷你缓冲区(minibuffer)中定义的 list of the completion补全命令列表 。
TAB- 尽可能补全迷你缓冲区中的文本;若无法补全,则显示所有可能的补全候选列表
SPC- (空格键) 补全光标位置之前的文本,仅补全到下一个连字符或空格处
C-x UP- 调用迷你缓冲区历史记录进行文本补全
C-x DOWN- 调用迷你缓冲区默认值进行文本补全
RET- (回车键) 将迷你缓冲区中的文本作为参数提交,提交前可能会先执行补全操作。详情参见《补全退出》
?- 显示补全候选列表及若干实用快捷键绑定 (
minibuffer-completion-help) M-DOWNM-UP- 遍历补全候选列表
M-vM-g M-cPageUpprior- 在迷你缓冲区中操作时,切换到显示补全列表的窗口
RET- 在补全缓冲区中, 选中光标所在位置的补全候选
mouse-1mouse-2- (鼠标按键) 在补全缓冲区中,点击鼠标选中对应位置的补全候选
TABRIGHTn- 在补全缓冲区中, 移动到下一个补全候选
S-TABLEFTp- 在补全缓冲区中,移动到上一个补全候选
q- 关闭补全窗口,切换回迷你缓冲区窗口
z- 关闭并删除补全缓冲区及其对应的窗口
TAB (minibuffer-complete) 是最基础的补全命令。它会搜索所有与迷你缓冲区现有文本匹配的补全候选,并尽可能完成补全。补全候选的筛选规则详见《补全候选的选择方式》。
SPC (minibuffer-complete-word) 的补全逻辑与 TAB 类似,但仅补全到下一个连字符或空格为止。例如:若迷你缓冲区中已有文本 'auto-f',按下空格键后,补全结果为 'auto-fill-' (而非完整的 'auto-fill-mode');再次按下空格键,才会补全为完整的 'auto-fill-mode' 。注意:该命令不适用于包含空格的参数类型(如文件名)。
当 TAB 或 SPC 无法完成补全时,会在新窗口中显示匹配的补全候选列表(若存在候选),同时展示若干用于选择候选的实用命令。你也可以按下 ? (minibuffer-completion-help) ,主动调出补全列表和帮助信息。补全列表的相关操作命令如下:
在迷你缓冲区或补全缓冲区中,按下 M-DOWN (minibuffer-next-completion) 和 M-UP (minibuffer-previous-completion) 可遍历补全缓冲区中的候选。当变量 minibuffer-completion-auto-choose 设为非 nil(默认配置)时,遍历操作会同时将当前候选插入迷你缓冲区;若该变量设为 nil ,可按下 M-RET (minibuffer-choose-completion) 将候选插入迷你缓冲区。默认情况下,按下 M-RET 会退出迷你缓冲区;若添加前缀参数( C-u M-RET ),则仅插入候选,不退出迷你缓冲区。
在迷你缓冲区中按下 M-v ,可切换到显示补全列表的窗口 (switch-to-completions),方便后续操作。 PageUp 、 prior 、 M-g M-c 也可实现该功能。你也可以通过其他方式切换窗口(详见《多窗口操作》)。
在补全缓冲区中,按下 RET 可选中光标所在候选 (choose-completion) ;点击鼠标左键( mouse-1 )或中键( mouse-2 )也可选中对应位置的候选。若添加前缀参数( C-u RET ),则仅将光标所在候选插入迷你缓冲区,不退出迷你缓冲区,便于重新选择其他候选。
在补全缓冲区中,按下 TAB 、 RIGHT 或 n 可移动到下一个补全候选 (next-completion) ;按下 S-TAB 、 LEFT 或 p 可移动到上一个补全候选 (previous-completion) 。
你还可以基于当前命令的迷你缓冲区输入历史完成补全:按下 C-x UP (minibuffer-complete-history) ,其功能与 TAB 类似,但补全候选来源为迷你缓冲区历史记录,而非默认候选池。类似地, C-x DOWN (minibuffer-complete-defaults) 会调用当前命令提供的默认输入项进行补全。
最后,按下 q 会关闭补全窗口并切换回迷你缓冲区窗口 (quit-window) ;按下 z 会直接关闭并删除补全缓冲区及其窗口 (kill-current-buffer)。
若将变量 minibuffer-visible-completions 自定义为非 nil 值,箭头键的绑定功能会发生变化:此时箭头键不再用于移动迷你缓冲区中的光标,而是像默认的元箭头键(M-方向箭头)一样遍历补全候选;同时,按下 RET 会选中当前候选(功能等同于默认的 M-RET )。按下 C-g 会隐藏补全窗口,但保持迷你缓冲区处于激活状态,你可以继续在提示符后输入内容。
10.4.3. 补全退出
当一个命令通过带补全功能的迷你缓冲区读取参数时,它同时会控制你按下 RET 回车键 (minibuffer-complete-and-exit) 提交参数时的行为。该行为分为四种类型:
Strict completion严格补全仅接受精确匹配的补全结果。只有当迷你缓冲区中的文本本身是精确匹配项,或者可以补全为某个精确匹配项时,按下RET回车键才会退出迷你缓冲区。反之,Emacs 会拒绝退出迷你缓冲区;它会尝试执行补全操作,若无法完成补全,则会在迷你缓冲区文本后短暂显示"[Not match]"。(你仍然可以按下C-g取消该命令,从而退出迷你缓冲区。)使用这种行为的命令示例是
M-x,因为对它而言,接受一个不存在的命令名是没有意义的。Cautious completion谨慎补全的行为与严格补全类似,区别在于只有当文本本身已是精确匹配项时,按下RET回车键才会退出。如果文本可以补全为某个精确匹配项,按下RET回车键只会执行补全操作,但不会立即退出;你需要再次按下RET回车键才能退出迷你缓冲区。例如,读取必须已存在的文件对应的文件名时,就会使用谨慎补全。
Permissive completion宽松补全允许输入任意内容;补全候选项仅作为参考建议。按下RET回车键不会触发补全操作,只会直接提交你当前输入的内容作为参数。Permissive completion with confirmation带确认的宽松补全的行为与宽松补全基本一致,仅存在一个例外情况:当你按下制表键(TAB)并将文本补全至某个中间状态(即尚未形成精确匹配的补全结果)时,若紧接着按下RET回车键,系统不会直接提交该参数。取而代之的是,Emacs 会在文本后短暂显示"[Confirm]"以要求你确认;再次按下RET回车键,即可确认并提交该文本。这种机制可以避免一个常见误操作 —— 用户在未意识到TAB制表键未完成预期补全的情况下,误按RET回车键提交内容。你可以通过自定义变量
confirm-nonexistent-file-or-buffer来调整上述确认行为。该变量的默认值为after-completion,对应上述的默认行为。若将其值改为nil,Emacs 将不再要求确认,直接退化为宽松补全模式。若将其值设为其他非 nil 的值,无论前序操作是否为按下TAB制表键,Emacs 都会要求你进行确认。大多数读取文件名的命令(如
C-x C-f)和读取缓冲区名的命令(如C-x b),都会采用这种带确认的宽松补全行为。
10.4.4. 补全候选的选择规则
补全命令的工作原理是,从一长串可能的补全候选项中,筛选出与你在迷你缓冲区中输入内容相匹配的较小子集。在《补全示例》一节中,我们给出了一个此类匹配的简单案例。判断何为匹配项的过程十分精细复杂,Emacs 会尝试在大多数情况下提供合理的补全结果。
Emacs 会借助 一种或多种 completion styles补全风格 来执行补全操作 —— 补全风格是一套判定迷你缓冲区文本与补全候选项是否匹配的标准。补全过程中,Emacs 会依次尝试每种补全风格:若某一种风格筛选出一个或多个匹配项,这些匹配项就会被作为补全候选项列表;若该风格未筛选出任何匹配项,Emacs 就会继续尝试下一种风格。
列表变量 completion-styles 用于指定要使用的补全风格,列表中的每个元素都是一种补全风格的名称(一个 Lisp 符号)。所有可用的风格符号都存储在变量 completion-styles-alist 中(参见《Emacs Lisp 参考手册》中的《补全变量》一节)。默认的补全风格按顺序排列如下:
basic(基础风格)- 匹配的补全候选项,其开头部分必须与迷你缓冲区中光标之前的文本完全一致。此外,若迷你缓冲区中光标之后还有文本内容,则补全候选项的剩余部分必须包含该文本作为子串。
partial-completion(部分补全风格)这种主动的补全风格会将迷你缓冲区中的文本按连字符或空格分割为多个单词,再对每个单词分别进行补全。(例如,补全命令名时,输入 "em-l-m" 会被补全为 "emacs-lisp-mode"。)
除此之外,迷你缓冲区文本中的星号 '*' 会被当作通配符 —— 它可以匹配补全候选项对应位置上的任意字符序列。
emacs22(Emacs22 风格)- 这种补全风格与 basic 风格类似,区别在于它会忽略迷你缓冲区中光标之后的文本。该风格以此命名,是因为它与 Emacs 22 版本中的补全行为完全一致。
以下额外的补全风格同样已被定义,你可以根据需要将它们添加到 completion-styles 中(参见《自定义》一节):
substring(子串风格)匹配的补全候选项,必须同时包含迷你缓冲区中光标之前的文本和光标之后的文本作为子串,且两个子串的顺序需与原文本一致。
例如,若迷你缓冲区中的文本为 'foobar',且光标位于 'foo' 与 'bar' 之间,那么 'afoobbarc' 会被判定为匹配项(其中 a、b、c 可以是任意字符串,包括空字符串)。
flex(灵活风格)- 这种主动的补全风格也被称为
flx、fuzzy模糊补全或scatter分散补全,它会尝试基于 有序子串 进行补全。例如,它会将 'foo' 判定为与 'frodo' 或 'fbarbazoo' 相匹配。 initials(首字母风格)- 这种补全风格的匹配策略非常主动,它会尝试针对缩写词和首字母缩写进行补全。例如,补全命令名时,它会将 'lch' 匹配为 'list-command-history'。
还有一种极为简单的补全风格名为 emacs21 。在该风格下,若迷你缓冲区中的文本为 'foobar',则只有以 'foobar' 开头的候选项会被视为匹配项。
你可以通过设置变量 completion-category-overrides ,在不同场景下使用不同的补全风格。例如,该变量的默认配置规定,针对缓冲区名称仅使用 basic 和 substring 这两种补全风格。
10.4.5. 补全选项
在补全 区分大小写的参数 (例如命令名)时,大小写是有意义的。例如,补全命令名时,输入 'AU' 不会补全为 'auto-fill-mode'。而在补全不区分大小写的参数时,大小写差异会被忽略。
补全文件名时,若变量 read-file-name-completion-ignore-case 的值为非 nil,则大小写差异会被忽略。该变量的默认值在区分大小写文件名的系统(如 GNU/Linux)上为 nil ;在不区分大小写文件名的系统(如微软 Windows)上为非 nil。补全缓冲区名时,若变量 read-buffer-completion-ignore-case 的值为非 nil,则大小写差异会被忽略,该变量的默认值为 nil。
补全文件名时,Emacs 通常会忽略某些被判定为 不太可能 被选中的候选项,具体由列表变量 completion-ignored-extensions 决定。该列表中的每个元素都应为一个字符串;任何文件名以该字符串结尾的文件,都会被排除在补全候选项之外。若列表元素以斜杠(/)结尾,则代表该元素是一个子目录名。 completion-ignored-extensions 的标准值包含多个元素,例如 ".o"、".elc" 和 ”~“ 。举例来说,若某个目录下存在 "foo.c" 和 "foo.elc" 两个文件,输入 'foo' 会补全为 'foo.c'。但如果 所有 可能的补全候选项的后缀都属于被忽略的字符串,那么这些候选项将不会被忽略:以上述例子来说,输入 'foo.e' 会补全为 'foo.elc'。需要注意的是,当 Emacs 在补全列表中展示候选项时,会忽略 completion-ignored-extensions 这个变量的配置。
Shell 补全是文件名补全的扩展版本,具体可参见《Shell 模式选项》一节。
若将 completion-auto-help 的值设为 nil ,补全命令将不会自动显示补全列表缓冲区;你必须手动输入 ? 来查看候选项列表。若该变量的值为 lazy ,Emacs 仅会在第二次尝试补全时展示补全列表缓冲区。也就是说,若当前无内容可补全,第一次按下 TAB 会提示 “Next char not unique 后续字符不唯一”;第二次按下 TAB 才会弹出补全列表缓冲区。若该变量的值为 always ,则每次尝试补全时,补全列表缓冲区都会自动显示。
补全列表缓冲区首次显示后,其后续的显示状态同样由 completion-auto-help 控制。若该变量的值为 t 或 lazy ,当 Emacs 能够完成补全时,展示补全候选项的窗口会自动收起;而当你输入新内容后再次出现无法补全的情况时,该窗口可能会重新弹出。若变量值为 always ,则只有当你退出补全操作时,该窗口才会收起。若变量值为 visible ,则该配置属于混合模式:在决定是否弹出补全列表窗口时,它的行为与 t 一致;在决定是否收起该窗口时,它的行为与 always 一致。
Emacs 支持在弹出补全列表窗口时,自动选中该窗口。若要启用此功能,可将用户选项 completion-auto-select 自定义为 t ,这会改变 TAB 键在补全窗口弹出时的行为:按下 TAB 会切换到补全列表缓冲区,之后你可以通过光标移动命令选中目标候选项,并按下 RET 确认选择。若 completion-auto-select 的值为 second-tab ,则第一次按下 TAB 会弹出补全列表缓冲区,第二次按下 TAB 才会切换到该缓冲区。
当补全列表窗口被选中时(无论是通过自定义 completion-auto-select 实现,还是手动按下 C-x o 切换窗口),按下 UP and DOWN 上下方向键 ( previous-line-completion 和 next-line-completion ) 可以在补全候选项之间逐行切换;若在按下方向键时附带数字前缀参数,则可以一次跳过对应行数的候选项。若变量 completion-auto-wrap 的值为非 nil,那么当光标移动到候选项列表的顶端或底端时,会自动循环到另一端。
若变量 completion-cycle-threshold 的值为非 nil,补全命令可以循环遍历所有补全候选项。在默认情况下,当迷你缓冲区中的文本存在多个补全候选项时,补全命令会补全到这些候选项的最长公共子串。若将 completion-cycle-threshold 的值改为 t ,补全命令会优先补全为第一个候选项;此后每次调用补全命令,都会循环替换为下一个候选项。若将该变量的值设为一个数字 n ,则只有当候选项数量小于等于 n 时,补全命令才会触发这种循环补全的行为。
默认情况下,Emacs 会弹出一个新的缓冲区来展示补全候选项,且候选项会按 horizontal 水平方向排列,排列的列数取决于当前窗口的宽度。你可以通过自定义用户选项 completions-format 来修改这一排列方式。若该选项的值为 vertical ,Emacs 会改为垂直排列候选项;若值为 one-column ,则所有候选项会被排列在同一列中。
用户选项 completions-sort 用于控制 *Completions* 缓冲区中候选项的排序规则,其默认值为 alphabetical ,即按字母顺序排序。若值为 nil ,则禁用排序功能;若值为 historical ,则会先按字母顺序排序,再根据迷你缓冲区的历史记录调整候选项的顺序。该选项的值也可以是一个函数,Emacs 会将补全候选项列表传入该函数,函数需返回排序后的候选项列表。
若变量 completions-max-height 的值为非 nil,则该变量会限制补全窗口的大小。其取值为行数,且包含模式行、标题行以及底部分隔线(若存在)。如果需要更精细地控制补全窗口的显示属性,可以使用变量 display-buffer-alist (具体可参见《Emacs Lisp 参考手册》中的《缓冲区显示的动作列表》一节)。
变量 completions-header-format 是一个格式规范字符串,用于控制补全候选项列表上方的信息提示行。若该字符串中包含 '%s' 占位符,则该占位符会被替换为补全列表缓冲区中展示的候选项数量。若要隐藏该信息提示行,可将该变量自定义为 nil 。该变量的值(即提示行字符串)可以设置文本属性,从而改变提示行的视觉外观;常用的属性包括 face (字体)和 cursor-intangible (光标不可见)(具体可参见《Emacs Lisp 参考手册》中的《具有特殊含义的属性》一节)。
若变量 completions-highlight-face 指向某个有效的字体(face),则当前补全候选项(即按下 RET 或鼠标点击时会被选中的候选项)会用该字体高亮显示。该变量的默认值为 completions-highlight ;若将其设为 nil ,则会禁用高亮功能。此特性依赖于特殊文本属性 cursor-face 实现。
10.5. 迷你缓冲区历史记录
你在迷你缓冲区中输入的所有内容都会保存到 minibuffer histroy list迷你缓冲区历史记录列表 中,方便你后续随时复用。这其中包含补全候选内容(例如文件名、缓冲区名称、命令名称等)以及其他任何类型的迷你缓冲区输入。你可以使用以下命令,快速将历史输入或备选输入调取到迷你缓冲区中:
M-p- 跳转到迷你缓冲区历史记录的上一项,即更早输入的参数 (
previous-history-element)。 M-n- 跳转到迷你缓冲区历史记录的下一项 (
next-history-element)。 UP- ↑(上箭头)
DOWN- ↓ (下箭头) 功能类似
M-p和M-n,但会优先在当前多行历史项内向上或向下跳转一行,再切换至上或下一条历史记录 (previous-line-or-history-element和next-line-or-history-element) M-r regexp RET- 跳转到历史记录中更早且匹配该正则表达式的项 (
previous-matching-history-element) M-s 正则表达式 回车- 跳转到历史记录中更晚且匹配该正则表达式的项
(
next-matching-history-element)
在迷你缓冲区中,按下 M-p (previous-history-element) 会逐条遍历历史记录列表,每按一次就会将历史记录中更早的一项调取到迷你缓冲区,并覆盖当前内容。按下 M-n (next-history-element) 则会反向遍历历史记录,将更新的条目调取到迷你缓冲区。
若在迷你缓冲区中按下 M-n 时,历史记录中已经没有更新的条目(例如你此前从未按过 M-p ),Emacs 会尝试从 默认参数列表 中调取内容:这些是你大概率会输入的常用值。你可以将这个过程理解为遍历 “future history未来历史记录”。
文件名对应的 “future history” 包含多种实用备选内容,例如当前光标所在位置的文件名或网址。这类场景下,“future history” 的默认内容由选项 file-name-at-point-functions 所指定的函数控制。默认情况下,该选项会调用 ffap 包(参见《定位光标处的文件与网址》章节),此包会根据光标周围的文本内容自动推测默认的文件或网址。若要关闭该推测功能,可将该选项自定义为 nil ,此时文件名的 “未来历史记录” 将只包含当前缓冲区打开的文件(若有)以及默认目录。
上下箭头键的功能与 M-p 、 M-n 类似,但如果当前历史项是多行内容,它们会先在该多行内容内向上或向下跳转一行,再切换到上一条或下一条历史记录。
如果你编辑了通过 M-p 或 M-n 调取到迷你缓冲区的文本, 不会修改 该内容在历史记录列表中的原始条目。但当你提交编辑后的内容时,该内容会被添加到历史记录列表的末尾。
你可以使用 M-r (previous-matching-history-element) 搜索历史记录中更早的匹配项,使用 M-s (next-matching-history-element) 搜索更新的匹配项。这两个命令都会要求你输入一个正则表达式作为参数,并将第一个匹配的条目调取到迷你缓冲区。关于正则表达式的语法说明,参见《正则表达式语法》章节。若为命令添加数字前缀参数 n ,则会调取第 n 个匹配项。这两个命令比较特殊:即便它们是在迷你缓冲区中被调用,依然会通过迷你缓冲区读取正则表达式参数。正则表达式中的大写字母会使搜索过程区分大小写(参见《搜索过程中的宽松匹配》章节)。
你也可以使用 增量搜索 功能遍历历史记录,详情参见《迷你缓冲区搜索》章节。
Emacs 会为不同类型的参数维护 独立的历史记录列表 。例如,所有读取文件名的命令会共用一个文件名历史列表;其他独立的历史列表还包括:缓冲区名称列表、命令名称列表(供 M-x 使用)以及命令参数列表(供 query-replace 这类命令使用)。
变量 history-length 用于指定迷你缓冲区历史记录列表的 最大长度 :当列表长度超出限制时,新增条目会自动删除最旧的条目。若将该变量设为 t ,则历史记录列表无长度限制。
变量 history-delete-duplicates 用于指定是否 删除历史记录中的重复项 :若该变量值为非 nil,新增条目时会删除列表中所有与之内容相同的旧条目。该变量的默认值为 nil 。
10.6. 重复执行迷你缓冲区命令
所有使用过一次迷你缓冲区的命令,都会连同其参数值一起被记录在一个特殊的历史列表 —— 命令历史列表中,你可以借此重复执行整条命令。尤其是 M-x 的每次调用都会被记录在内,因为 M-x 本身就是通过迷你缓冲区来读取命令名称的。
C-x ESC ESC- 从命令历史列表中重新执行最近一条使用过迷你缓冲区的命令 (
repeat-complex-command) M-x list-command-history- 显示完整的命令历史列表,列出所有可通过
C-x ESC ESC重复执行的命令,按从新到旧的顺序排列。
C-x ESC ESC 用于重新执行最近一条调用过迷你缓冲区的命令。不带参数时,它会重复执行最后一条该类命令;带数字前缀参数时,则指定要重复的命令位置:参数 1 代表最后一条,2 代表倒数第二条,以此类推。
C-x ESC ESC 的工作原理是,将此前执行的命令转换为一段 Lisp 表达式 ,并在迷你缓冲区中初始化显示这段表达式文本。即便你不懂 Lisp 语言,也能轻松识别出当前显示的待重复命令。直接按下回车键( RET ),就会原封不动地重复执行该命令;你也可以先编辑这段 Lisp 表达式来修改命令,再按下 RET 回车执行。执行后的命令会被添加到命令历史列表的头部,除非它与列表中最新的条目完全相同。
在 C-x ESC ESC 唤起的迷你缓冲区中,你可以使用常规的迷你缓冲区历史记录命令(参见《迷你缓冲区历史记录》章节)来遍历历史列表。找到想要重复的历史命令后,可像平常一样编辑其表达式,再按回车键执行。
严格来说, 增量搜索 并没有使用迷你缓冲区。因此,尽管它的行为类似复杂命令,但默认不会出现在 C-x ESC ESC 对应的历史列表中。若要让增量搜索命令被纳入该历史列表,可将变量 isearch-resume-in-command-history 的值设为非 nil。详情参见《增量搜索》章节。
所有此前调用过迷你缓冲区的命令,会以 Lisp 列表的形式存储在变量 command-history 中。列表中的每个元素都是一段 Lisp 表达式,描述了一条命令及其对应的参数。Lisp 程序可通过调用函数 eval ,传入 command-history 中的元素来重新执行对应命令。
10.7. 输入密码
有时你需要在 Emacs 中输入密码。例如,当你通过 FTP 等网络协议让 Emacs 访问另一台机器上的文件时,往往需要提供密码才能获取该机器的访问权限(参见《远程文件》章节)。
输入密码的操作和使用迷你缓冲区类似。Emacs 会在回显区显示一个提示(如"Password: ");输入所需密码后,按下回车键( RET )提交即可。为防止他人窥看密码,你输入的每个字符都会以星号(“*”)代替原本的样式显示。
输入密码时,迷你缓冲区的大部分功能和命令都无法使用。此时没有历史记录和补全功能,且在提交密码前,你无法切换窗口或执行 Emacs 的其他任何操作。
输入密码的过程中,你可以按下删除键( DEL )向后删除,移除最后输入的一个字符。快捷键 C-u 会清空当前已输入的所有内容。 C-g 可退出密码提示(参见《退出与终止》章节)。 C-y 会将当前的剪切内容粘贴到密码输入框中(参见《文本的剪切与移动》章节)。按下制表键( TAB )可以切换密码的可见状态。你既可以按回车键( RET ),也可以按退出键( ESC )来提交密码。其他任何可插入字符的按键会将对应的字符输入到密码中,除此之外的所有输入都会被忽略。
模式行中还会有一个图标用于指示密码的可见状态。在该图标上单击鼠标左键( mouse-1 ),同样可以切换密码的可见性。
10.8. 确认提示(Yes or No Prompts)
Emacs 命令在执行过程中,可能会要求你回答一个 "是/否" 类问题。这类询问主要分为两种类型。
第一种是结尾标注 “(y or n)” 的是 / 否询问。你只需按下单个按键('y' 代表是,'n' 代表否),即可立即退出迷你缓冲区并提交响应。例如,当你按下 C-x C-w ( write-file 写入文件命令)保存缓冲区,并输入了一个已存在的文件名时,Emacs 会弹出如下提示:
File ‘foo.el’ exists; overwrite? (y or n)
第二种是结尾标注 “(yes or no)” 的是 / 否询问(如果你自定义了 yes-or-no-prompt 变量,则会显示该变量的值),这类询问通常用于 误操作会引发严重后果 的场景。例如,当你对一个包含未保存修改的文件访问缓冲区执行 C-x k ( kill-buffer 关闭缓冲区命令)时,Emacs 会在迷你缓冲区中弹出如下提示:
Buffer foo.el modified; kill anyway? (yes or no)
此时你需要在迷你缓冲区中完整输入 "yes" 或 "no",再按下回车键( RET )来提交响应。
对于这两种是 / 否询问,迷你缓冲区都支持前文所述的基础操作:你可以用 C-l 重定所选窗口的显示中心,用 C-v (或 PageDown )向下滚动、 M-v (或 PageUp )向上滚动该窗口,用 C-x o 切换到其他窗口,用 M-p 和 M-n 执行历史记录相关命令等。按下 C-g 可以取消当前询问,同时退出迷你缓冲区和发起询问的命令(参见《退出与终止》章节)。
11. 通过名称执行命令
每个 Emacs 命令都有一个可用于执行它的名称。为方便使用,许多命令还配有快捷键绑定。你既可以通过按下快捷键来执行这些命令,也可以通过输入命令名称来执行。大多数 Emacs 命令没有快捷键绑定,因此 通过名称执行是唯一的方式 。(有关如何设置快捷键绑定的方法,参见《自定义快捷键绑定》章节。)
按照惯例,命令名称由一个或多个单词组成,单词之间用连字符分隔;例如 auto-fill-mode (自动换行模式)或 manual-entry (手动录入)。命令名称大多使用完整的英文单词,以便于记忆。
若要通过名称执行命令,需先按下 M-x ,接着输入命令名称,最后按下回车键( RET )确认。 M-x 会借助迷你缓冲区读取命令名称,迷你缓冲区开头会显示字符串 'M-x' 作为提示,提醒你输入需要执行的命令名称。按下 RET 回车键后,迷你缓冲区会关闭并执行该命令。关于迷你缓冲区的更多信息,参见《迷你缓冲区》章节。
你可以使用补全功能来输入命令名称。例如,要调用 forward-char (向前移动字符)命令,你可以直接输入:
M-x forward-char RET
或者使用补全的方式输入:
M-x forw TAB c RET
需要注意的是, forward-char 与按下快捷键 C-f 所执行的是同一个命令。即便命令有对应的快捷键绑定,也不会影响你通过名称执行它。
当 M-x 对命令进行补全时,会忽略所有在 Emacs 早期主版本中被标记为废弃的命令;对于这类命令,你必须输入完整的名称才能执行。而在当前 Emacs 版本中被标记为废弃的命令,仍会出现在补全列表里。(废弃命令指的是已有更新、更优替代方案,且计划在未来某个 Emacs 版本中移除的命令。)
此外, M-x 的补全功能可以排除与当前缓冲区主模式(参见《主模式》章节)和次要模式(参见《次要模式》章节)不相关、且通常无法在当前模式下工作的命令。默认情况下,不会排除任何命令,但你可以自定义选项 read-extended-command-predicate ,将这些不相关的命令从补全结果中剔除。
与之相反,Emacs 也可以只显示与当前缓冲区密切相关的命令。 M-S-x (即 “Meta 键 + Shift 键 + x 键”)命令的工作方式与 M-x 类似,但它不会列出 Emacs 支持的所有(或大部分)命令,只会显示那些被标记为 “属于” 当前主模式或已启用的次要模式的命令。
若要取消 M-x 操作且不执行任何命令,无需输入命令名称,直接按下 C-g 即可。这会让你回到正常的命令操作状态。
如果要向通过 M-x 调用的命令传递数值参数,需要在按下 M-x 之前指定该数值参数。在读取命令名称的过程中,参数值会显示在迷你缓冲区的提示信息中,最终 M-x 会将该参数传递给对应的命令。例如,要向 forward-char 命令传递数值参数 42,可以输入:
C-u 42 M-x forward-char RET
当你通过 M-x 执行的命令存在对应的快捷键绑定时,Emacs 会在命令执行完毕后,在回显区提示该快捷键。例如,若你输入 M-x forward-word ,回显区会显示该命令也可通过按下 M-f 来执行。你可以通过将变量 suggest-key-bindings 的值设为 nil ,来关闭这类提示信息。该变量的值也可以设为一个数字,此时 Emacs 会将快捷键提示信息显示对应秒数后再清除。默认情况下,提示信息会显示 2 秒。
此外,当 suggest-key-bindings 的值不为 nil 时, M-x 的补全列表中会为所有带有快捷键绑定的命令,显示对应的等效快捷键。
对于没有快捷键绑定的命令,在 M-x 提示符下,你无需输入完整的命令名称即可调用。如果命令的简写形式比完整名称短很多,且变量 extended-command-suggest-shorter 的值不为 nil,Emacs 会在回显区提示该简写形式。 suggest-key-bindings 的设置同样会影响这类提示信息的显示。
在本手册中,当提及通过名称执行命令时,我们通常会省略用于确认命令名称的回车键( RET )。因此,我们可能会写成 M-x auto-fill-mode ,而非 M-x auto-fill-mode RET 。只有在需要强调的情况下(例如命令后需要跟参数时),才会特别提及回车键。
M-x 的底层实现是通过执行 execute-extended-command 命令完成的,该命令的作用是读取另一个命令的名称并调用它。
12. 帮助功能
Emacs 提供了种类丰富的帮助命令,所有命令均可通过前缀键 C-h (或等效的功能键 F1 )调用。下文将对这些帮助命令进行详细说明。你也可以按下 C-h C-h 来查看完整的帮助命令列表 (help-for-help) 。在命令列表界面,可使用 SPC (空格键)和 DEL (删除键)滚动列表,然后输入想要执行的帮助命令;若要取消操作,按下 C-g 即可。
许多帮助命令会在一个特殊的 help buffer帮助缓冲区 中展示信息。在该缓冲区中,你可以用 SPC 和 DEL 滚动内容,按下 RET (回车键)则可跳转至超链接。详情参见《帮助模式》章节。
默认情况下,帮助命令会在独立窗口中打开帮助缓冲区,但不会自动选中该窗口。这一行为由变量 help-window-select 控制:它的默认值为 nil ;若将其自定义为 t ,帮助命令会强制选中帮助窗口;若设为 other ,则仅当选中的frame中存在两个以上窗口时,才会选中帮助窗口。
与之相对, *Help* 缓冲区中的许多命令会弹出新窗口来展示执行结果。例如,点击链接查看源代码、或使用 i 命令查看手册条目时,默认都会弹出新窗口。如果将变量 help-window-keep-selected 的值改为非 nil,系统就会复用当前显示 *Help* 缓冲区的窗口,而非新建窗口。
若你想使用某个功能,但不清楚它的名称或查找路径,我们推荐三种方法:首先尝试关键词检索命令,其次检索手册索引,接着查阅常见问题(FAQ)和软件包关键词,最后再尝试列出外部软件包。
C-h a TOPICS RET- 检索名称匹配参数
TOPICS的命令。参数可以是单个关键词、多个空格分隔的关键词,或正则表达式(详情参见《正则表达式》章节)。Apropos C-h d TOPICS RET- 类似上一条命令,但检索范围是文档字符串的文本内容,而非命令与函数的名称。
C-h r i TOPIC RET- 在 Emacs 信息手册的索引中检索
TOPIC,并展示首个匹配结果。按下,逗号可查看后续匹配项。 C-h r s TOPIC RET- 类似上一条命令,但检索范围是手册的正文内容,而非索引。
C-h C-f- 通过信息阅读器(Info)打开 Emacs 常见问题(FAQ)文档。
C-h p- 根据关键词展示可用的 Emacs 软件包(详情参见《软件包关键词搜索》章节)。
M-x list-packages- 展示所有外部软件包的列表(详情参见《软件包》章节)。
此外,在其他多种场景下, C-h 或 F1 也代表 “帮助” 功能。例如,在按下前缀键后再按这两个键,即可查看该前缀键后续可搭配的按键列表。(此场景下也可使用 ? 键替代。少数前缀键不支持通过 C-h 或 ? 查看后续按键 —— 因为这些按键被定义了其他功能,但所有前缀键均支持 F1 来触发帮助。)
目录
- 帮助摘要:所有帮助命令的简明列表
- 按键相关文档:查询 Emacs 中按键的功能
- 按命令或变量名查询帮助:查询命令、变量或函数名称的相关信息
- 关键词检索帮助(Apropos):查询与指定主题相关的内容
- 帮助模式命令:帮助模式与帮助缓冲区的特殊功能
- 软件包关键词搜索:通过关键词(主题)查找 Lisp 程序库
- 国际语言支持相关帮助:与国际化语言支持相关的帮助
- 其他帮助命令:各类杂项帮助命令
- 帮助文件:用于展示辅助帮助文件的命令
- 活动文本与工具提示帮助:活动文本的帮助信息与工具提示(“气泡帮助”)
12.1. 帮助摘要
以下是用于查阅内置文档的帮助命令汇总,其中大部分命令会在后续章节展开详细说明。
C-h a topics RET- 显示名称匹配指定主题的命令列表 (
apropos-command) 。详情参见《关键词检索》章节。 C-h b- 显示当前所有生效的按键绑定;展示顺序为:次要模式绑定 → 主模式绑定 → 全局绑定(对应函数
describe-bindings)。详情参见《其他帮助命令》章节。 C-h C-q- 切换显示一个窗口,该窗口用于展示常用命令及其对应的按键绑定。详情参见《其他帮助命令》章节。
C-h c key- 显示该按键序列所绑定命令的名称 (
describe-key-briefly) 。此处的 c 代表 “字符(character)”。若需查看该按键的完整信息,可使用C-h k命令。详情参见《按键相关文档》章节。 C-h d topics RET- 显示文档字符串内容匹配指定主题的命令与变量列表 (
apropos-documentation) 。详情参见《关键词检索》章节。 C-h e- 显示
*Messages*缓冲区的内容 (view-echo-area-messages) 。详情参见《其他帮助命令》章节。 C-h f 函数名 RET- 显示名为函数名的 Lisp 函数文档 (
describe-function) 。由于 Emacs 命令本质上也是 Lisp 函数,该命令同样适用于查询命令;你也可以使用C-h x命令查询。详情参见《按命令或变量名查询帮助》章节。 C-h h- 显示
HELLO文件,该文件包含多种字符集的示例文本。 C-h i- 启动 Info——GNU 官方文档阅读器(对应函数
info)。Emacs 手册可在 Info 中查阅。详情参见《其他帮助命令》章节。 C-h k key- 显示该按键序列所绑定命令的名称与完整文档 (
describe-key) 。详情参见《按键相关文档》章节。 C-h l- 显示你最近输入的 300 次按键记录 (
view-lossage) 。详情参见《其他帮助命令》章节。 C-h m- 显示当前主模式与已启用次要模式的文档说明 (
describe-mode) 。详情参见《其他帮助命令》章节。 C-h n- 显示 Emacs 近期更新内容的公告 (
view-emacs-news) 。详情参见《帮助文件》章节。 C-h o 符号- 显示名为符号的 Lisp 符号文档 (
describe-symbol) 。该命令可查询各类符号:函数、变量、以及显示样式(face)均适用。详情参见《按命令或变量名查询帮助》章节。 C-h p- 根据主题关键词查找软件包 (
inder-by-keyword) 。详情参见《软件包关键词检索》章节。查询结果会以软件包菜单缓冲区的形式列出。详情参见《Emacs Lisp 软件包》章节。 C-h P 软件包名 RET- 显示指定软件包名对应的软件包文档 (
describe-package) 。详情参见《软件包关键词检索》章节。 C-h r- 在 Info 中打开 Emacs 手册 (
info-emacs-manual) 。 C-h s- 显示当前语法表的内容 (
describe-syntax) 。详情参见《其他帮助命令》章节。语法表定义了各类字符的属性,例如哪些字符属于左分隔符、哪些字符属于单词组成部分等。详情参见《Emacs Lisp 参考手册》中的《语法表》章节。 C-h t- 启动 Emacs 交互式教程 (
help-with-tutorial) 。 C-h v var RET- 显示名为变量名的 Lisp 变量文档 (
describe-variable) 。详情参见《按命令或变量名查询帮助》章节。 C-h w command RET- 显示触发指定命令名对应命令的按键序列 (
where-is) 。详情参见《按键相关文档》章节。 C-h x command RET- 显示指定命令名对应命令的文档 (
describe-command) 。详情参见《按命令或变量名查询帮助》章节。 C-h C coding RET- 描述指定编码名对应的编码系统 (
describe-coding-system) 。详情参见《编码系统》章节。 C-h C RET- 描述当前正在使用的编码系统。
C-h F command RET- 启动 Info 并跳转到记录指定 Emacs 命令名文档的节点 (
Info-goto-emacs-command-node) 。详情参见《按命令或变量名查询帮助》章节。 C-h I method RET- 描述指定输入法名对应的输入法 (
describe-input-method) 。详情参见《选择输入法》章节。 C-h K key- 启动 Info 并跳转到记录指定按键序列文档的节点 (
Info-goto-emacs-key-command-node) 。详情参见《按键相关文档》章节。 C-h L language-env RET- 显示指定语言环境名所使用的字符集、编码系统与输入法信息 (
describe-language-environment) 。详情参见《语言环境》章节。 C-h S 符号 RET- 根据当前编辑文件对应的编程语言,显示该符号的 Info 文档 (
info-lookup-symbol) 。详情参见《其他帮助命令》章节。 C-h .- 若光标位于特殊文本区域内,显示该区域对应的帮助信息 (
display-local-help) 。(例如*Help*缓冲区中的超链接就属于这类特殊文本区域。)详情参见《活动文本与工具提示帮助》章节。若带前缀参数执行该命令(即C-u C-h .),且光标位于按钮(button)或控件(widget)之上,该命令会弹出一个新缓冲区,展示对应按钮 / 控件的详细描述。
12.2. 按键相关文档
获取按键序列相关信息的帮助命令为 C-h c (describe-key-briefly ,简要描述按键功能) 和 C-h k (describe-key) 。
按下 C-h c 后再输入目标按键 ,会在回显区显示该按键所绑定的命令名称。例如,执行 C-h c C-f 会显示命令 'forward-char'(向前移动字符)。
C-h k key 命令的作用与 C-h c key 类似,但会提供更详细的信息:它会打开一个帮助缓冲区,其中包含该命令的文档字符串,详细说明命令的具体功能。
C-h K key 命令则会跳转到 Emacs 手册中,对应该按键所绑定命令的说明章节。
C-h c 、 C-h k 和 C-h K 适用于所有类型的按键序列,包括功能键、菜单操作和鼠标事件( C-h c 会忽略鼠标移动事件除外)。例如,按下 C-h k 后,你可以从菜单栏中选择一个菜单项,查看该菜单项所执行命令的文档字符串。
C-h w command RET 会列出该命令所绑定的所有按键,结果会显示在回显区。如果提示该命令未绑定任何按键,则意味着你必须通过 M-x 来调用这个命令。 C-h w 对应的底层命令是 where-is 。
Emacs 的部分模式会用到各类按钮(参见《Emacs Lisp 参考手册》的 “按钮” 章节)和控件(参见《Emacs Widget 控件使用入门》),点击这些元素可以执行相应操作。若要查看这些按钮最终调用的函数,你可以将光标移到按钮上方,然后执行 button-describe 和 widget-describe 命令。
M-x which-key 是一个全局次要模式,专门用于帮助用户探索 Emacs 的按键映射表。当你输入了不完整的命令前缀时,它会以弹窗形式显示对应的按键绑定关系。
12.3. 按命令或变量名查询帮助
C-h x command RET (describe-command) 会在一个窗口中显示指定命令的文档说明。例如,执行
C-h x auto-fill-mode RET
会展示自动换行模式 (auto-fill-mode) 的相关文档。对于那些未绑定到任何按键的命令(这类命令通常需要通过 M-x 调用),这是获取其文档的常用方式。
C-h f function RET (describe-function) 用于显示 Lisp 函数的文档说明,该命令主要面向 Lisp 程序中调用的函数。比如,当你编写了表达式 (make-vector len) ,想要确认 make-vector 的用法是否正确时,只需输入 C-h f make-vector RET 即可查看文档。此外,由于 Emacs 中所有命令本质上都是 Lisp 函数,因此你也可以用这个命令查看任意命令的文档。
若直接按下 C-h f RET ,该命令会解析光标所在位置周围最内层的 Lisp 表达式,并展示其调用函数的文档 —— 前提是该函数名是已定义的合法 Lisp 函数(输入参数时,这个函数名会作为默认值显示)。例如,当光标位于文本 "(make-vector (car x)" 之后时,包含光标的最内层列表是以 "(make-vector" 开头的,此时按下 C-h f RET ,就会显示 make-vector 函数的文档。
C-h f 也可用于校验函数名的拼写是否正确。如果 C-h f 在迷你缓冲区的提示符中,将光标所在位置的函数名作为默认值显示,就说明这个名称是已定义的 Lisp 函数。若你并不需要查看该函数的文档,按下 C-g 即可取消 C-h f 命令的执行。
describe-function 命令展示的函数文档,不仅包含 函数的文档字符串和签名 ,还会附带一些辅助信息,比如函数类型、定义该函数的文件路径、函数是否已被标记为废弃。文档中被加粗强调的部分,通常可以通过点击或按下回车键,跳转到更多相关内容。
如果函数的类型是已知的,会通过 函数类型说明符 来标注(参见《Emacs Lisp 参考手册》中的 “类型说明符” 章节)。只有当 Lisp 程序手动声明了函数类型,或者编译器自动推导出了函数类型时,该标注才会显示。需要注意的是,函数类型推导功能仅在启用 原生编译 时生效(参见《Emacs Lisp 参考手册》中的 “原生编译” 章节)。
若你查询的是一个 自动加载函数 ,且其自动加载定义(参见《Emacs Lisp 参考手册》中的 “自动加载” 章节)中未提供文档字符串,那么 *Help* 帮助缓冲区中将不会显示任何文档内容。这种情况下,如果变量 help-enable-symbol-autoload 的值不为 nil,Emacs 会尝试加载该函数的定义文件,以查找是否存在对应的文档字符串。
使用 M-x shortdoc 命令,你可以获取与特定主题相关的函数概览。执行该命令后,它会提示你输入感兴趣的主题(例如 string ,即字符串),然后弹出一个缓冲区,列出所有与字符串处理相关的常用函数。
你还可以设置让 C-h f 弹出的 *Help* 缓冲区中,在展示函数和命令文档的同时附带 使用示例 。要实现这个功能,需在你的初始化文件中添加以下配置(参见《Emacs 初始化文件》章节):
(add-hook 'help-fns-describe-function-functions
#'shortdoc-help-fns-examples-function)
C-h v (describe-variable) 的功能与 C-h f 类似,区别在于它用于展示 Lisp 变量的文档,而非 Lisp 函数。如果光标所在位置或前方的符号是已定义的 Lisp 变量,该变量名会作为默认值填充到输入参数中。详情参见 “变量” 章节。
只要你安装了 Emacs 的源码文件,描述变量和函数的帮助缓冲区中,通常会包含指向对应源码的超链接(参见 “超链接与网页导航功能” 章节)。
若要在手册中查找某个命令的文档说明,可以使用 C-h F (Info-goto-emacs-command-node) 。该命令支持检索多种手册,而不仅限于 Emacs 官方手册,并能自动定位到对应的章节。
C-h o (describe-symbol) 的功能涵盖了 C-h f 和 C-h v ,它可以描述任意符号—— 无论是函数、变量还是面(face)。如果某个符号拥有多重定义(例如,既被定义为函数,又被定义为变量),该命令会依次展示它的所有文档说明。
C-h 4 s (elp-find-source) 会切换到一个新的缓冲区,打开当前帮助文档中所描述对象的 源码定义文件 。
如果用户选项 completions-detailed 的值不为 nil,部分命令在展示补全候选时会附带详细信息。例如,执行 C-h o TAB 键,补全列表中会包含每个符号文档字符串的第一行内容,同时标注该符号的类型(如函数、变量等)。具体附带的详细信息会根据所使用的命令有所不同。
12.4. 关键词检索帮助(Apropos)
apropos关键词检索 类命令可以解答这类问题:“有哪些用于文件操作的命令?”更准确地说,你需要输入一个 apropos pattern检索模式串 ,这个模式串可以是单个单词、一组用空格分隔的单词,或是一个正则表达式。
下述所有关键词检索命令,都会在迷你缓冲区中读取检索模式串,搜索匹配该模式的目标对象,并在新窗口中展示检索结果。
C-h a- 搜索命令 (
apropos-command) 。若带上前缀参数执行,则同时搜索非交互式函数 M-x apropos- 搜索函数与变量,可同时匹配交互式函数(即命令)与非交互式函数
M-x apropos-user-option- 搜索用户可自定义的变量。若带上前缀参数执行,则同时搜索不可自定义的变量
M-x apropos-variable- 搜索所有变量。若带上前缀参数执行,则仅搜索可自定义变量
M-x apropos-local-variable- 仅搜索缓冲区局部变量
M-x apropos-value- 搜索值匹配指定模式的变量。若带上前缀参数执行,则同时搜索定义匹配模式的函数、以及属性匹配模式的 Lisp 符号
M-x apropos-local-value- 仅搜索值匹配指定模式的缓冲区局部变量
C-h d- 搜索文档字符串匹配指定模式的函数与变量(对应底层函数 apropos-documentation)
最简单的检索模式串是 单个单词 ,任何名称中包含该单词的对象都会被匹配。例如,要查找所有文件相关的命令,输入 C-h a file RET 即可。执行后会列出所有名称含 'file' 的命令,比如 copy-file 、 find-file 等。每条命令都会附带简短描述,以及当前可调用它的快捷键列表。以 find-file 为例,检索结果会标注它的快捷键是 C-x C-f 。
默认情况下,展示检索结果的 apropos 缓冲区不会被自动选中。若要让 Emacs 自动选中该缓冲区,可将变量 help-window-select 设为任意非 nil 值。
对于 apropos 缓冲区中列出的函数定义、变量或符号属性,若想查看其详细信息,可使用 mouse-1 鼠标左键或 mouse-2 中键点击对应条目,或移动光标到条目后按下 RET 回车键。
当检索模式串包含 多个单词 时,目标对象的名称必须至少包含其中两个单词,才算匹配成功。例如,若想查找 “删除光标前方文本块” 的命令,可输入 C-h a kill back backward behind before RET 。实际存在的命令 kill-backward 会被匹配到;如果存在 kill-text-before 这类命令,也会被匹配到,因为它的名称包含了模式串中的两个单词。
若需要更高的检索灵活性,可直接输入 正则表达式 (参见《正则表达式语法》章节)。当模式串中包含正则表达式的特殊字符 ”^$*+?.\[“ 时,Emacs 会自动将其解析为正则表达式。
遵循 Emacs 命令的命名规范,以下这些单词在检索模式串中非常实用。通过在 C-h a 中使用它们,你也能逐步熟悉 Emacs 的命名习惯:
char(字符)、line(行)、word(单词)、sentence(句子)、paragraph(段落)、region(区域)、page(页)、sexp(S 表达式)、list(列表)、defun(函数定义)、rect(矩形区域)、buffer(缓冲区)、frame(框架)、window(窗口)、face(外观)、file(文件)、dir(目录)、register(寄存器)、mode(模式)、beginning(开头)、end(结尾)、forward(向前)、backward(向后)、next(下一个)、previous(上一个)、up(向上)、down(向下)、search(搜索)、goto(跳转至)、kill(剪切)、delete(删除)、mark(标记)、insert(插入)、yank(粘贴)、fill(填充)、indent(缩进)、case(大小写)、change(修改)、set(设置)、what(查询)、list(列出)、find(查找)、view(查看)、describe(描述)、default(默认)。
若变量 apropos-do-all 的值为非 nil,则大多数关键词检索命令的执行效果,等同于带前缀参数执行的效果。但存在一个例外:不带前缀参数执行 apropos-variable 时,无论 apropos-do-all 的值是什么,都会始终搜索所有变量。
默认情况下,除 apropos-documentation 外,所有关键词检索命令都会按 字母顺序 排列结果。若将变量 apropos-sort-by-scores 设为非 nil,这些命令会尝试评估结果的相关度,将最相关的结果排在前面。而 apropos-documentation 命令默认按 相关度 排序结果;若要改为按字母顺序排序,可将变量 apropos-documentation-sort-by-scores 设为 nil。
12.5. 帮助模式命令
帮助缓冲区的主模式为帮助模式。该模式包含查看模式的所有命令(参见《查看模式》相关内容);例如,按 SPC 空格键可向下滚动,按 DEL 删除键或 S-SPC 可向上滚动。同时它还提供了若干专属的特殊命令:
RET- 跳转到光标所在位置的交叉引用 (
help-follow) TAB- 将光标向前移动至下一个超链接 (
forward-button) S-TAB- 将光标向后移动至上一个超链接 (
backward-button) mouse-1mouse-2- 跳转到点击的超链接
np- 在帮助缓冲区中向前或向后翻页
C-c C-c- 显示光标所在位置符号的全部相关文档 (
help-follow-symbol) C-c C-fr- 向前翻阅帮助命令的操作历史 (
help-go-forward) C-c C-bl- 向后翻阅帮助命令的操作历史 (
help-go-back) s- 查看当前帮助主题的源码(若有源码可查) (
help-view-source) i- 在手册中检索当前主题 (
help-goto-info) I- 在 Emacs Lisp 手册中检索当前主题 (
help-goto-lispref-info) c- 自定义光标所在的变量或显示样式 (
help-customize)
在帮助缓冲区的文档中,函数名、变量名或显示样式名(参见文《本显示样式》相关内容)通常会显示为 underlined hyperlink带下划线的超链接 。若要查看其关联文档,可将光标移至对应位置并按 RET 回车 (help-follow) ,或用 mouse-1 鼠标左键 / mouse-2 中键点击该超链接。此操作会替换帮助缓冲区的原有内容;若要回溯操作记录,可按 C-c C-b 或 l (help-go-back) 。回溯过程中,可按 C-c C-f 或 r (help-go-forward) 向前恢复操作。
在帮助缓冲区中切换超链接时,按 TAB (forward-button) 可跳至下一个超链接,按 S-TAB 可返回上一个超链接。这些命令为循环执行;例如,在最后一个超链接处按制表符,光标会跳回第一个超链接。
默认情况下,帮助缓冲区中的许多超链接会被引号包裹。若将用户选项 help-clean-buttons 设为非空值,缓冲区中的这些引号会被自动移除。
部分帮助命令生成的帮助缓冲区(例如展示大量按键绑定的 C-h b 命令)会通过 ^L 字符将内容分块为多个页面。在这类缓冲区中,按 n (help-goto-next-page) 可跳至下一页开头,按 p (help-goto-previous-page) 可返回上一页开头。通过该方式可快速在帮助缓冲区的不同文档板块间导航。
帮助缓冲区中还可包含指向 Info 手册、源码定义和网页地址的超链接。前两种链接会在 Emacs 中直接打开,网页地址则会通过 browse-url 命令调用浏览器打开(参见超链接跳转相关内容)。
若要查看文档中任意符号的全部相关信息,可将光标移至该符号处并按 C-c C-c (help-follow-symbol) 。该操作会展示该符号的所有相关文档 —— 包括其作为变量、函数和、and/or 显示样式的相关说明。
12.6. 软件包关键词搜索
Emacs 中大多数可选功能都归类为软件包。Emacs 内置了数百个软件包,还可通过网络安装更多软件包(参见《Emacs Lisp 软件包》章节)。
为方便查找与特定主题相关的软件包,多数软件包会根据其功能关联一个或多个关键词。按下 C-h p (finder-by-keyword) 可调出软件包关键词列表,同时显示各关键词的含义说明。若要查看某一关键词对应的软件包列表,在该关键词所在行按下 RET 回车键即可;相关软件包列表会在「软件包菜单」缓冲区中展示(参见《软件包菜单缓冲区》章节)。
按下 C-h P (describe-package) 后,Emacs 会提示你输入软件包名称(参见《Emacs Lisp 软件包》章节),并在帮助缓冲区中展示该软件包的属性信息及所实现的功能。该缓冲区会以按钮形式列出与该软件包关联的所有关键词,使用鼠标左键或鼠标中键点击任意按钮,即可查看该关键词对应的其他软件包列表。
12.7. 国际语言支持相关帮助
若需查看特定语言环境的相关信息(参见《语言环境》章节),按下 C-h L (describe-language-environment) 即可。该操作会调出帮助缓冲区,展示此语言环境所支持的语言,同时列出其关联的字符集、编码体系、输入方法,以及该语言环境的示例文本。
命令 C-h h (view-hello-file) 会打开 etc/HELLO 文件,该文件通过展示多种语言的 “你好” 表达,直观演示各类字符集的使用。
命令 C-h I (describe-input-method) 用于说明输入方法 —— 可指定某一输入方法进行查看,默认情况下则展示当前正在使用的输入方法(参见《输入方法》章节)。
命令 C-h C (describe-coding-system) 用于解读编码体系 —— 可指定某一编码体系进行查看,默认情况下则展示当前正在使用的编码体系(参见《编码体系》章节)。
12.8. 其他帮助命令
C-h i (info) 可启动 Info 程序,该程序用于浏览结构化的文档文件。 C-h 4 i (info-other-window) 功能与之相同,区别在于会在另一个窗口中显示 Info 缓冲区。Emacs 的完整手册及 GNU 系统的其他众多手册均可在 Info 中查阅。进入 Info 后按下 h 键,即可运行 Info 的使用教程。
若为 C-h i 指定数字参数 n ,该命令会选中名为 ”*info*<n>“ 的 Info 缓冲区,此功能便于你同时浏览多本 Info 手册。若仅将 C-u 作为前缀参数, C-h i 会提示你输入文档文件名,你可借此浏览在 Info 顶层菜单中无入口的文件。
前文提及的帮助命令 C-h F functions RET 与 C-h K key ,会直接进入 Info 并跳转到对应函数或按键的说明文档。
在编辑程序时,若该编程语言有对应的 Info 版手册,你可使用 C-h S (info-lookup-symbol) ,在对应手册中查找某个符号(关键字、函数或变量)的相关条目。该命令的具体工作方式由当前主模式决定。
若遇到意外情况,且不确定自己按下了哪些按键,可使用 C-h l (view-lossage) 。 C-h l 会显示你最近输入的按键序列及其触发的命令。Emacs 默认保存最近的 300 次按键记录,你可通过 lossage-size 命令修改该数值。若发现记录中有不熟悉的命令,可使用 C-h k 或 C-h f 查询其功能。
如需查看近期的回显消息,可使用 C-h e (view-echo-area-messages),该命令会打开保存此类消息的 *Messages* 缓冲区。
Emacs 的每种主模式通常会重新定义部分按键,并对编辑的工作方式做出其他调整。 C-h m (describe-mode) 会显示当前主模式的说明文档,文档中通常会介绍该模式下修改后的命令、功能,以及对应的按键绑定。
C-h b (describe-bindings) 与 C-h s (describe-syntax) 可展示 Emacs 当前运行环境的其他相关信息。 C-h b 会显示当前所有生效的按键绑定列表,顺序依次为:当前次要模式的局部绑定、当前主模式定义的局部绑定,最后是全局绑定(参见《自定义按键绑定》章节)。 C-h s 会显示语法表的内容,并对每个字符的语法规则做出解释(参见《Emacs Lisp 参考手册》中的《语法表》章节)。
C-h C-q (help-quick-toggle) 可切换一个辅助缓冲区的显示与隐藏,该缓冲区展示了 Emacs 最常用的命令及其对应的按键绑定(也被称作 “速查手册”),其内容由 help-quick 命令生成。缓冲区中所有按键绑定均为可点击按钮,使用 mouse-1 鼠标左键或 mouse-2 中键点击,即可查看该按键序列所绑定命令的说明文档。
在按下某个前缀键后,再输入 C-h 、 ? 或 F1 (describe-prefix-bindings) ,即可查看该前缀键对应的所有子命令列表。(少数前缀键不支持全部上述按键 —— 这类前缀键为相关按键定义了专属绑定。ESC 就是其中之一: ESC C-h 与 ESC ? 实际对应的是 C-M-h (mark-defun) 和 M-? (xref-find-references) ,但 ESC F1 可正常生效。)
最后,执行 M-x describe-keymap 命令,程序会带补全功能提示你输入键盘映射表名称,确认后将显示该键盘映射表中所有的按键绑定。
12.9. 帮助文件
除内置文档和手册外,Emacs 还包含若干其他说明文件,内容涉及复制条款、版本说明、调试方法、问题反馈指引等。你可通过以下命令查看这些文件,除快捷键 C-h g 外,其余命令均为 C-h C-字符 的形式。
C-h C-c- 显示 Emacs 的复制与再分发规则 (
describe-copying) 。 C-h C-d- 显示 Emacs 调试相关帮助 (
view-emacs-debugging) 。 C-h C-e- 显示获取外部包的相关信息 (
view-external-packages) 。 C-h C-f- 显示 Emacs 常见问题解答列表 (
view-emacs-FAQ) 。 C-h g- 打开 GNU 项目的介绍页面 (
describe-gnu-project) 。 C-h C-m- 显示 Emacs 手册印刷版的订购信息 (
view-order-manuals) 。 C-h C-n- 显示版本更新日志,列出当前 Emacs 版本的新功能 (
view-emacs-news) 。 C-h C-o- 显示 Emacs 及其他 GNU 软件最新版本的订购与下载方式 (
describe-distribution) 。 C-h C-p- 显示已知的 Emacs 问题列表,部分问题会附带建议的解决办法 (
view-emacs-problems) 。 C-h C-t- 显示 Emacs 的待办事项列表 (
view-emacs-todo) 。 C-h C-w- 显示 GNU Emacs 无任何担保的完整详细说明 (
describe-no-warranty) 。
12.10. 活动文本与工具提示帮助
在 Emacs 中, active text活动文本 (即响应鼠标点击或 RET 回车键执行特定操作的文本)通常配有对应的帮助文本。这类文本包括 Emacs 缓冲区中的超链接,以及模式行的部分内容。在图形化显示界面中,或是部分支持鼠标追踪的文本终端中,将鼠标移至活动文本上方时,相关帮助文本会以 tooltip 工具提示 的形式显示,详见「工具提示」相关说明。
在不支持鼠标追踪的终端中,可键入快捷键 C-h . (display-local-help) ,显示光标所在位置的缓冲区活动文本的帮助文本,该内容会展示在回显区。若希望光标所在位置存在可用帮助文本时,自动显示对应的帮助信息,可将变量 help-at-pt-display-when-idle 的值设为 t。
13. 标记与区域(Mark and Region)
Emacs 与诸多其他应用程序一样,允许你选中缓冲区文本中的任意部分,并调用针对该 select text选中文本 执行操作的命令。在 Emacs 中,我们将选中的文本称为 region区域 ;其处理方式与其他程序中选中文本的处理方式十分相似,但也存在一些重要区别。
区域指的是 标记 与当前 光标位置 之间的缓冲区文本部分。你可以通过在某处设置标记(例如使用 C-SPC 命令),再将光标移动至希望作为区域结束位置的地方,来定义一个区域。(你也可以使用鼠标来定义区域。)
无论标记和光标在文本中哪个位置更靠前,区域始终覆盖两者之间的范围;每次移动光标,区域都会随之改变。
在文本的某个位置设置标记后,标记会 activates被激活 。当标记处于激活状态时,我们也称区域处于激活状态;Emacs 会通过 region face区域面 为区域内的文本添加高亮,以此标示出区域的范围(参见「自定义样式」相关内容)。
执行部分非移动类命令后(包括所有会修改缓冲区文本的命令),Emacs 会自动 deactivates取消标记的激活状态 ,区域的高亮效果也会随之消失。你也可以在任何时候键入 C-g ,手动取消标记的激活状态(参见「退出与终止操作」相关内容)。
许多命令会将其操作的文本范围限定在 激活的区域 内。例如, M-% 命令(用于替换匹配的文本)默认会作用于缓冲区中所有可访问的文本部分,但如果存在激活的区域,该命令将仅对该区域内的文本生效。
即便标记未被激活,它依然具有实用价值。例如,你可以通过标记环跳转到之前设置过的标记位置(参见「标记环」相关内容)。此外,部分命令即便针对未激活的区域,也能执行相应操作(例如 upcase-region 命令,用于将区域文本转为大写)。你也可以通过 C-x C-x 这类命令,重新激活区域。
上述行为是 Emacs 交互会话中的默认行为,被称为 临时标记模式 (Transient Mark mode)。关闭临时标记模式后,Emacs 会切换至另一种行为模式,在该模式下,区域通常不会被高亮显示(参见「关闭临时标记模式」相关内容)。
在一个缓冲区中设置标记,不会对其他缓冲区中的标记产生任何影响。当你回到一个存在激活标记的缓冲区时,标记会停留在之前的位置。当多个窗口显示同一个缓冲区时,各窗口的光标位置可以不同,因此对应的区域也会不同,但所有窗口会共享同一个标记位置(参见「多窗口」相关内容)。通常情况下,只有 选中的窗口 会为其对应的区域添加高亮;但如果将变量 highlight-nonselected-windows 的值设为非 nil,每个窗口都会为自身对应的区域添加高亮。
Emacs 中还有另一种区域类型:矩形区域(参见「矩形区域」相关内容)。
13.1. 设置标记
以下是若干用于设置标记的命令:
C-SPC- 在光标位置设置标记并将其激活 (
set-mark-command) 。 C-@- 与上一命令功能相同。
C-x C-x- 在光标位置设置标记并将其激活,随后将光标移至标记原位置 (
exchange-point-and-mark,交换光标与标记位置)。 Drag-mouse-1- 鼠标左键拖动。在拖动鼠标划过的文本范围两端分别设置光标与标记。
mouse-3- 鼠标右键点击。在当前光标位置设置标记,随后将光标移至鼠标点击处 (
mouse-save-then-kill) 。 - 按住 SHIFT 的光标移动键
- 若标记未激活,则在光标位置设置标记,再移动光标(详见「移位选择」相关内容)。
设置标记最常用的方式是按下 C-SPC (set-mark-command)5。该操作会在当前光标位置设置并激活标记,之后你可移动光标,标记将停留在原位置。若标记已存在于当前光标位置,该命令不会重复设置标记,仅激活已有的标记。
例如,若你希望将缓冲区中的部分文本转换为大写,可将光标移至目标文本的一端,按下 C-SPC ,再移动光标至目标文本的另一端,此时目标文本会被高亮显示。接着按下 C-x C-u (upcase-region ,将区域文本转为大写) ,该命令会将区域内的文本转换为大写,随后自动取消标记的激活状态。
只要标记处于激活状态,你可按下 C-g 取消其激活(详见「退出与终止操作」)。大多数对区域执行操作的命令都会自动取消标记激活,如上述示例中的 C-x C-u 。
标记不仅可用于定义待操作的区域,还可用于标记缓冲区中的某个位置(连续按下 C-SPC C-SPC ),后续可通过 C-u C-SPC 跳回该位置(详见「标记环」相关内容)。
命令 C-x C-x (exchange-point-and-mark) 用于交换光标与标记的位置。当你希望保留当前光标位置,仅调整区域的另一端(标记位置)时,该命令非常实用。若需要,再次按下 C-x C-x ,可将标记移至新位置,同时将光标恢复至原始位置。默认情况下,若标记处于未激活状态,该命令会先重新激活上一次设置的标记,确保区域保持高亮;但如果为该命令添加前缀参数执行,它会保持标记未激活、区域不高亮的状态,你可通过这种方式跳至标记位置,效果与 C-u C-SPC 类似。
你也可通过鼠标设置标记:按住鼠标左键( down-mouse-1 )并拖动划过一段文本,鼠标按下处会被设为标记,松开处则为光标位置。此外,点击鼠标右键( mouse-3 )会在当前光标位置设置标记,再将光标移至点击位置。有关这些鼠标命令的详细说明,参见「编辑相关的鼠标命令」。
最后,你还可在按下Shift 键的同时,按下部分光标移动命令(如 S-RIGHT 、 S-C-f 、 S-C-n 等)设置标记,这种方式被称为“shift-selection”移位选择。该操作会在移动光标前,于当前光标位置设置标记 —— 但仅当此前未通过移位选择或鼠标命令激活标记时生效。通过鼠标命令和移位选择设置的标记,与常规标记的行为略有不同:后续任何未按住 Shift 的光标移动命令,都会自动取消其激活状态(详见「移位选择」)。
许多插入文本的命令(如 C-y (yank ,粘贴) 会在插入文本的另一端设置标记,但不会激活该标记。这一设计能让你轻松返回该位置(详见「标记环」),当回显区显示 “Mark set(标记已设置)“ 时,即表示当前执行的命令完成了该操作。
在 X 窗口系统中,每当激活的区域发生变化,Emacs 会将区域内的文本保存至主选择区(primary selection),你可通过点击鼠标中键( mouse-2 ),将该文本插入其他 X 应用程序中(详见「与其他窗口应用程序的剪切和粘贴」)。
13.2. 标记文本对象的命令
以下命令可在单词、列表、段落、页面等文本对象的两端定位光标与标记:
M-@- 将标记设到下一个单词的末尾 (
mark-word) ,执行后光标位置不变。 C-M-@- 将标记设到下一个平衡表达式的末尾之后 (
mark-sexp) ,执行后光标位置不变。 M-h- 将光标移至当前段落开头,并将标记设到段落末尾 (
mark-paragraph) 。 C-M-h- 将光标移至当前函数定义开头,并将标记设到定义末尾 (
mark-defun) 。 C-x C-p- 将光标移至当前页面开头,并将标记设到页面末尾 (
mark-page) 。 C-x h- 将光标移至缓冲区开头,并将标记设到缓冲区末尾 (
mark-whole-buffer) 。
M-@ (mark-word) 会把标记定位到下一个单词的末尾(关于单词的定义详见「单词」相关内容)。重复执行该命令,会将标记逐个单词向后移动,从而扩展选中的区域。一个例外情况是:若标记处于激活状态且位于光标前方, M-@ 会将标记从当前位置逐个单词向前移动。
该命令也可接收数字参数 n ,指示将标记向后移动 n 个单词;若传入负参数 -n ,则将标记向前移动 n 个单词。
类似地, C-M-@ (mark-sexp) 会把标记定位到下一个平衡表达式的末尾(关于括号平衡表达式详见「带配对括号的表达式」相关内容)。重复执行该命令,会将区域扩展至后续的表达式;传入正、负数字参数时,会将标记按指定数量的表达式向前或向后移动。
上述列表中的其他命令会同时调整光标与标记的位置,以此在缓冲区中划定一个文本对象的范围: M-h (mark-paragraph) 用于标记段落(详见「段落」相关内容), C-M-h (mark-defun) 用于标记顶层函数定义(详见「按函数定义移动」相关内容), C-x C-p (mark-page) 用于标记页面(详见「页面」相关内容)。重复执行这些命令,作用同样是扩展区域至相邻的同类文本对象;数字参数也可指定标记需要移动过的文本对象数量。
C-x h (mark-whole-buffer) 会将光标置于缓冲区开头、标记置于缓冲区末尾,从而将整个缓冲区设为选中区域
13.3. 对区域执行操作
定义好区域后,可通过以下方式对其执行各类操作:
C-w删除区域内容(参见「文本的删除与移动」)。M-w将区域内容复制到删除环(参见「粘贴操作」)。C-x C-l或C-x C-u转换区域内容大小写(参见「大小写转换命令」)。C-u C-/撤销区域内的修改操作(参见「撤销」)。M-%替换区域内的文本(参见「查询替换」)。C-x TAB或C-M-\对区域内容进行缩进(参见「缩进」)。M-x fill-region将区域内容按文本格式进行填充排版(参见「文本填充」)。M-$检查区域内单词的拼写(参见「拼写检查与修正」)。M-x eval-region将区域内容作为 Lisp 代码执行(参见「Emacs Lisp 表达式的执行」)。C-x r s将区域内容保存到寄存器(参见「寄存器」)。- 将区域内容保存到缓冲区或文件中(参见「文本累积」)。
部分命令在标记未激活时执行默认操作,标记激活时则对区域进行操作。例如 M-$ (ispell-word) ,默认会检查光标所在单词的拼写,标记激活时则检查区域内文本的拼写(参见「拼写检查与修正」)。通常情况下,若区域为空(即标记与光标位置重合),这类命令会执行默认操作。若希望命令对空区域也执行区域操作,可将变量 use-empty-active-region 设为 t 。
如「文本擦除」章节所述, DEL (backward-delete-char ,向前删除字符) 和 Delete (delete-forward-char ,向后删除字符) 命令也遵循此规则:标记激活时,这两个命令会删除区域内的文本。(例外情况:若为命令指定非 1 的数字参数 n ,无论标记是否激活,命令都会删除 n 个字符。)若将变量 delete-active-region 设为 nil ,这两个命令在标记激活时将不再执行特殊操作;若将该变量值设为 kill ,这两个命令会将区域内容删除并保存到删除环,而非直接擦除(参见「文本的删除与移动」)。
另有部分命令始终对区域进行操作,无默认行为,这类命令的名称中通常包含 “region”(区域),例如 C-w (kill-region ,删除区域) 和 C-x C-u (upcase-region ,区域内容转大写) 。标记未激活时,这些命令会对 非激活区域 —— 即光标与上一次设置标记的位置之间的文本 —— 进行操作(参见「标记环」)。若要禁用此行为,可将变量 mark-even-if-inactive 设为 nil ,此后标记未激活时,执行这类命令会直接抛出错误。
默认情况下,即便标记处于激活状态,文本插入操作仍正常执行 —— 例如输入 a 会插入字符 a,随后取消标记的激活状态。次要模式「删除选中内容模式(Delete Selection mode)」会修改此行为:启用该模式后,标记激活时执行文本插入,会先删除区域内的文本,再插入新内容。不过可通过自定义选项 delete-selection-temporary-region 调整该行为,该选项默认值为 nil ;若将其设为 t , 仅临时激活的区域 会被新内容替换,临时激活区域包括通过鼠标拖动(参见「设置标记」)、移位选择(参见「移位选择」)设置的区域,以及临时标记模式(Transient Mark mode)禁用时通过 C-u C-x C-x 激活的区域。若将 delete-selection-temporary-region 设为 selection ,则仅鼠标拖动或移位选择激活的临时区域会被替换,通过 C-u C-x C-x 激活的临时区域不会被替换。键入 M-x delete-selection-mode 可切换 delete-selection-mode删除选中内容模式 的启用与禁用状态。
13.4. 标记环(Mark Ring)
每个缓冲区都会通过 mark ring标记环 记录标记的历史位置,设置标记的命令会将旧标记压入该标记环中。标记环的用途之一,就是记录你可能需要返回的位置。
C-SPC C-SPC- 设置标记并将其压入标记环,且不激活该标记。
C-u C-SPC- 将光标移至标记的上一位置,并从历史标记环中恢复标记位置。
当你希望用标记记录一个后续可能返回的位置时,命令 C-SPC C-SPC 会非常实用。该操作会将当前光标位置压入标记环,且不会激活标记(激活标记会让 Emacs 高亮显示对应区域)。这一操作实际是连续两次执行 C-SPC (set-mark-command) :第一次按下 C-SPC 用于设置标记,第二次则用于取消标记的激活状态。(若临时标记模式(Transient Mark mode)处于关闭状态, C-SPC C-SPC 会临时激活该模式,详见「关闭临时标记模式」相关内容。)
若要返回标记记录的位置,可带前缀参数执行 set-mark-command 命令: C-u C-SPC 。该操作会将光标移至标记的上一位置,若标记原本处于激活状态,则会将其取消激活。后续每一次按下 C-u C-SPC ,都会跳转到标记环中存储的更早一个位置,通过这种方式遍历的位置不会从环中丢失,而是会被移至标记环的末尾。
若将变量 set-mark-command-repeat-pop 设为非 nil 值,那么在按下 C-u C-SPC 后,可直接按下 C-SPC 替代 C-u C-SPC ,实现标记环的循环遍历。该变量的默认值为 nil 。
每个缓冲区都有独立的标记环 ,所有编辑命令均使用当前缓冲区的标记环,因此 C-u C-SPC 的跳转操作始终在当前缓冲区中进行,不会跳至其他缓冲区。
变量 mark-ring-max 用于指定标记环中可保存的最大记录数,默认值为 16 条。当标记环中的记录达到该上限,且有新标记被压入时,最早存入的那条记录会被丢弃。重复按下 C-u C-SPC ,会在当前标记环中存储的所有位置间循环跳转。
若你需要反复返回同一个位置,标记环的操作方式可能不够便捷。这种情况下,你可将该位置记录到寄存器中,方便后续快速调取(详见「在寄存器中保存位置」相关内容)。
13.5. 全局标记环(Global Mark Ring)
除了每个缓冲区各自拥有的普通标记环外,Emacs 还配有一个 global mark ring 全局标记环。若在上一次设置标记后切换过缓冲区,那么每次你设置新标记时,该标记会被同时记录在当前缓冲区的标记环与全局标记环中。因此,全局标记环会记录你曾操作过的缓冲区序列,且为每个缓冲区留存一处标记设置的位置。全局标记环的最大记录数由变量 global-mark-ring-max 控制,默认值为 16 条。
需注意, 仅当通过命令新建标记时 ,该标记才会被记录到全局标记环中。若只是激活已存在的标记(例如在已有标记的位置按下 C-SPC ,详见「设置标记」章节),并不会将该标记压入全局标记环。
命令 C-x C-SPC (pop-global-mark) 会跳转到全局标记环中最新记录对应的缓冲区与位置,同时会对全局标记环进行轮循 —— 连续按下 C-x C-SPC ,会依次跳转到更早记录的缓冲区及对应标记位置。
13.6. 移位选择(Shift Selection)
在按下光标移动命令的同时按住 Shift 键,会先设置标记,再移动光标,使区域覆盖从光标原始位置到新位置的范围。该功能被称为 shift-selection 移位选择 ,其文本选中方式与其他编辑器的操作逻辑相近。
通过移位选择设置的标记,行为与前文所述的常规标记略有不同。其一,除了常规的标记取消激活方式(如修改缓冲区文本、按下 C-g )外, 任何未按住 Shift 键的光标移动命令 ,都会触发标记的取消激活;其二,后续再次按下的移位光标移动命令,不会重新设置标记。因此,连续执行一系列移位光标移动命令,可对选中区域进行连续调整。
仅当移位后的光标移动键 未绑定至其他独立命令 时,移位选择功能才会生效(参见「自定义配置」相关内容)。例如,若将 S-C-f 绑定至其他命令,按下 S-C-f 时会执行该绑定命令,而非触发 C-f (forward-char) 对应的移位选择操作。
通过鼠标命令设置的标记,与移位选择设置的标记行为 完全一致 (参见「设置标记」相关内容)。例如,通过鼠标拖动划定区域后,可继续使用移位光标移动命令扩展该区域。无论以哪种方式设置标记,只要按下未按住 Shift 键的光标移动命令,都会取消标记的激活状态。
若要关闭移位选择功能,可将变量 shift-select-mode 的值设为 nil ,此操作不会禁用通过鼠标命令设置标记的功能。若将该变量值设为 permanent ,未经过移位转换的光标移动键将不会触发标记的取消激活;例如,此前命令划定的区域可通过移位选择继续扩展,未按住 Shift 键的光标移动键也能对移位选择划定的区域进行扩展。
13.7. 禁用临时标记模式(Disabling Transient Mark Mode)
标记与区域的默认行为 —— 设置标记时会激活标记并高亮显示区域 —— 由 临时标记模式 (Transient Mark mode) 实现。该模式为次要模式,在交互会话中默认启用,可通过 M-x transient-mark-mode 命令切换开关,也可通过「Options选项」菜单中的「Highlight Active Region 高亮激活区域」菜单项进行操作。关闭该模式后,Emacs 会切换为另一套操作方式:
执行
C-SPC、C-x C-x等命令设置标记时, 不会高亮显示区域 。因此无法通过视觉判断标记的位置,只能手动记忆。解决这一问题的常用方法是,设置标记后尽快使用,避免忘记其位置;也可通过
C-x C-x命令查看标记位置,该命令会交换光标与标记的位置(参见「设置标记」章节)。- 部分原本在标记激活时会对区域执行操作的命令,将不再具备该行为。例如默认情况下,若标记处于激活状态,
M-%(qurey-replace) 会在区域内执行替换操作;而关闭临时标记模式(Transient Mark mode)后,该命令会始终从光标位置开始,执行至缓冲区末尾。这类存在行为差异的命令,会在其自身的文档说明中明确标注。
关闭临时标记模式(Transient Mark mode)后,可通过 C-SPC C-SPC 或 C-u C-x C-x 临时激活 该模式。
C-SPC C-SPC- 在光标位置设置标记(与普通的
C-SPC操作一致),并一次性启用临时标记模式(Transient Mark mode),直至标记被取消激活为止。(这并非独立命令,而是连续执行两次C-SPC命令的效果。) C-u C-x C-x- 交换光标与标记的位置,激活标记并临时启用临时标记模式(Transient Mark mode),直至标记下一次被取消激活。(这是带前缀参数执行
C-x C-x命令exchange-point-and-mark的效果。)
上述命令在设置 或者 激活标记的同时,仅会在 标记激活期间 临时启用临时标记模式(Transient Mark mode)。使用该操作的一个常见原因是:关闭临时标记模式后,部分命令会对整个缓冲区而非区域执行操作,而临时启用该模式,就能让这些命令重新对区域生效。
通过鼠标(参见「设置标记」章节)或移位选择(参见「移位选择」章节)划定区域时,Emacs 也会以同样的方式 临时激活临时标记模式(Transient Mark mode) ,并高亮显示该区域。
14. 剪切与移动文本(Killing and Moving Text)
在 Emacs 中, killing 指擦除文本并将其复制到 kill ring 删除环中, yanking 指将删除环中的文本重新插入到缓冲区中。(部分应用程序将类似操作称为「cutting剪切」和「pasting粘贴」。)之所以称其为删除环,是因为可以将其想象成一组按环形排列的文本块,你能以循环的方式访问其中的内容,详见「删除环」相关章节。
killing删除与yanking粘贴是 Emacs 中移动或复制文本最常用的方式,该方式的灵活性极高,因为 Emacs 提供了针对多种不同语法单元的删除命令。
14.1. 删除与剪切
大多数从缓冲区中清除文本的命令,会将清除的文本保存至删除环中(参见「删除环」)。这类命令被称为 kill commands删除命令 ,其名称中通常包含 “kill” 一词(例如 kill-line )。删除环会存储近期多次执行删除操作的文本,而非仅保留最后一次的内容,因此删除操作的安全性很高:你无需过度担心丢失之前删除的文本。 删除环为 所有缓冲区共享 ,因此在一个缓冲区中删除的文本,可粘贴至另一个缓冲区中。
当你使用 C-/ (undo 撤销) 来撤销某一删除命令时(参见「撤销」),被删除的文本会恢复至缓冲区,但不会从删除环中移除。
在图形化显示界面中,执行删除操作的同时,还会将文本复制到系统剪贴板,详见「图形化界面中的『剪切 - 粘贴』操作』。
仅清除文本但 不将其保存至删除环 的命令,被称为 delete commands 擦除命令 ,其名称中通常包含 “delete” 一词。这类命令包括 C-d (delete-char ,向前擦除字符) 和 DEL (delete-backward-char ,向后擦除字符) —— 二者均一次仅擦除一个字符,以及所有仅用于擦除空格或换行符的命令。通常,若某一命令会清除大量有效文本,其执行的是删除(kill)操作而非擦除操作。
你也可通过鼠标执行删除与粘贴操作,详见「图形化界面中的『剪切 - 粘贴』操作」。
14.1.1. 删除操作
delete 擦除指清除文本且 不将其保存至删除环 的操作。Emacs 中的擦除命令,大多仅用于清除单个字符或空白字符。
DELBACKSPACE- 删除前一个字符;若区域处于激活状态,则删除区域内的文本 (
delete-backward-char) 。 Delete- 删除后一个字符;若区域处于激活状态,则删除区域内的文本 (
delete-forward-char) 。 C-d- 删除后一个字符 (
delete-char) 。 M-\- 删除光标位置前后的空格与制表符 (
delete-horizontal-space) 。 M-x just-one-space- 删除光标位置前后的空格与制表符,仅保留一个空格。
M-SPC- 以灵活的方式删除光标位置前后的空格与制表符 (
cycle-spacing) 。 C-x C-o- 删除当前行前后的空行 (
delete-blank-lines) 。 M-^- 删除两行之间的换行符及换行符后的所有缩进,将两行合并为一行 (
delete-indentation) 。
前文已介绍过 "basic deletion commands基础擦除命令" DEL (delete-backward-char) 、 Delete (delete-forward-char) 与 C-d (delete-char) ,详见「文本擦除」章节。为这些命令指定数字参数时,会按参数值删除对应数量的字符;若省略参数或参数为 1,当区域处于激活状态时, DEL 与 Delete 命令会删除区域内的所有文本,详见「对区域进行操作」章节。
其余擦除命令均仅用于清除空白字符(空格、制表符、换行符)。
M-\ (delete-horizontal-space) 会删除光标位置前后所有的空格与制表符;若为该命令添加前缀参数,则仅删除光标位置之前的空格与制表符。
M-x just-one-space 会删除光标位置前后的制表符与空格,且无论原位置有多少个空格(即便无空格),均在光标前保留一个空格。为该命令指定正数字参数 n 时,会在光标前保留 n 个空格;若参数为负数字 -n ,则除了删除空格与制表符外,还会删除换行符,并在光标前保留 -n 个空格。
cycle-spacing (M-SPC) 可看作是功能更灵活的 just-one-space 命令。若连续重复执行该命令,它会按照 cycle-spacing-actions 变量定义的规则,循环执行不同的空白字符清理操作。默认规则为:第一次执行等同于 just-one-space 命令,第二次执行等同于 delete-horizontal-space 命令(删除光标前后所有空白字符),第三次执行恢复光标位置原本的空白字符布局,后续按此规则循环。若为该命令添加前缀参数,所有循环操作都会沿用该参数值。用户可通过自定义 cycle-spacing-actions 变量添加更多清理规则,具体可参考该变量的文档说明。
C-x C-o (delete-blank-lines) 会删除当前行之后的所有空行;若当前行本身为空行,则同时删除当前行之前的所有空行(仅保留当前行这一个空行);若缓冲区中仅有这一个空行,则直接删除该行。
M-^ (delete-indentation) 会删除换行符及其周围的所有空格,将当前行与上一行合并为一行,通常仅在合并处保留一个空格,详见「M-^ 命令说明」。
delete-duplicate-lines 命令会在区域内查找重复的行,并删除每行的多余副本,仅保留一份。默认保留每组重复行中的第一行;若为该命令添加前缀参数 C-u ,则保留每组重复行中的最后一行;若添加前缀参数 C-u C-u ,则仅查找相邻的重复行(该模式执行效率更高,适用于已排序的文本);若添加前缀参数 C-u C-u C-u ,则会保留重复的空行不做删除。
14.1.2. 按行剪切
C-k- 剪切行尾剩余内容,或剪切一行或多行内容 (
kill-line) 。 C-S-backspace- 一次性剪切整行内容 (
kill-whole-line) 。
最简单的剪切命令是 C-k (kill-line) 。若在行尾使用该命令,它会剪切行尾的换行符,将下一行合并至当前行(因此空行会被彻底删除)。若不在行尾, C-k 会剪切从光标位置到行尾的所有文本;若光标原本就在行首,执行后该行会变为空行。
判断适用上述哪种情况时,会忽略行尾的空格和制表符。只要光标位于行中最后一个非空白字符之后,即可确定 C-k 会剪切换行符。若要剪切一整行非空内容,需将光标移至行首,连续按两次 C-k 。
此处的「line行」指 逻辑文本行 ,而非屏幕显示行(详见「折行」相关说明)。
当给 C-k 传入正数值参数 n 时,它会剪切 n 行内容及其后续的换行符(光标之前的当前行文本不会被剪切)。若传入负数值参数 -n ,它会剪切当前行之前的 n 行内容,以及光标之前的当前行文本。若给 C-k 传入参数 0,则仅剪切光标之前的当前行文本。
若变量 kill-whole-line 的值为非空(non-nil),在行首执行 C-k 会剪切包含后续换行符在内的整行内容。该变量的默认值为空(nil)。
C-S-backspace (kill-whole-line) 会剪切包含换行符在内的整行内容,与光标在该行中的位置无关。注意,许多文本终端会限制输入 C-S-backspace 这个按键组合。
14.1.3. 其他剪切命令
C-w- 剪切区域内的文本 (
kill-region) 。 M-w- 将区域内的文本复制到剪切环 (
kill-ring-save) 。 M-d- 剪切下一个单词 (
kill-word) ,详见「单词」相关说明。 M-DEL- 向前剪切一个单词 (
backward-kill-word) 。 C-x DEL- 向前剪切至句子开头 (
backward-kill-sentence) ,详见「句子」相关说明。 M-k- 剪切至句子末尾 (
kill-sentence) 。 C-M-k- 剪切后续的匹配表达式 (
kill-sexp) ,详见「括号匹配表达式」相关说明。 M-z char- 剪切至指定字符的下一次出现位置(含该字符) (
zap-to-char) 。 M-x zap-up-to-char char- 剪切至指定字符的下一次出现位置(不含该字符)。
最常用的剪切命令之一是 C-w (kill-region) ,该命令会剪切区域内的所有文本(详见「标记与区域」相关说明)。与之对应, M-w (kill-ring-save) 会将区域内的文本复制到剪切环,而不会从缓冲区中删除该文本。若按下 C-w 或 M-w 时标记处于未激活状态,命令会作用于光标位置与上一次设置标记的位置之间的文本(详见「区域操作」相关说明)。
Emacs 还提供了剪切特定语法单元的命令:可通过 M-DEL 和 M-d 剪切单词(详见「单词」);通过 C-M-k 剪切括号匹配表达式(详见「括号匹配表达式」);通过 C-x DEL 和 M-k 剪切句子(详见「句子」)。
M-z (zap-to-char) 命令将剪切与搜索结合:输入一个字符后,命令会从光标位置开始,剪切至该字符在缓冲区中下一次出现的位置(包含该字符本身)。传入数字参数表示重复执行次数;传入负数值参数则表示 向前搜索 并剪切光标之前的内容。该命令会保留此前输入过的字符历史,可通过 M-p / M-n 按键调取,此功能在需要通过复杂输入法输入目标字符时尤为实用。与之类似的 zap-up-to-char 命令,同样剪切至指定字符的下一次出现位置,但 不包含该字符本身 ,数字参数的作用同样为重复执行次数。
14.1.4. 剪切相关选项
部分专用缓冲区包含 read-only text 只读文本,这类文本无法修改,因此也无法被剪切。 kill commands 剪切命令在只读缓冲区中会以特殊方式工作:光标会遍历目标文本并将其复制到剪切环,却不会从缓冲区中实际删除该文本。默认情况下,执行此操作时编辑器还会发出提示音并显示错误信息。但如果将变量 kill-read-only-ok 设为非空值,编辑器仅会在回显区打印一条提示信息,说明文本未被删除的原因。
在将剪切内容存入剪切环(kill ring)之前,你可以通过 kill-transform-function 函数对目标字符串进行转换。该函数会接收待剪切的字符串作为参数,返回值为你希望存入剪切环的字符串;若返回 nil ,则该字符串不会被存入剪切环。例如,若你希望永远不将纯空白字符串存入杀环,可使用以下配置:
(setq kill-transform-function (lambda (string) (and (not (string-blank-p string)) string)))
若将变量 kill-do-not-save-duplicates 设为非空值,连续执行的多次相同剪切操作,最终仅会在剪切环中生成一条条目,不会产生重复内容。
若启用次要模式 kill-ring-deindent-mode ,存入剪切环的文本会自动减少缩进量,减少的幅度与被保存文本第一行的缩进量一致。也就是说,若被保存文本的第一行有n列的缩进,该模式会从每一行的缩进中均移除n列的空白。
14.2. 粘贴(Yanking)
Yanking 粘贴指重新插入此前被剪切的文本。移动或复制文本的常规方式为:先将文本剪切,再在其他位置将其粘贴。
C-y- 将最近一次剪切的内容粘贴到缓冲区的光标位置 (
yank) 。 M-y- 将刚粘贴的文本替换为更早一次剪切的文本块 (
yank-pop) ,或支持从过往所有剪切的文本块列表中选择粘贴内容,详见「粘贴更早的剪切内容」。 C-M-w- 让后续执行的命令(若为剪切命令)将内容追加至前一次的剪切结果中(追加下一次剪切命令),详见「追加剪切内容」。
基础的粘贴命令为 C-y (yank) ,该命令会插入最近一次剪切的内容,光标停留在插入文本的末尾;同时会在插入文本的开头位置设置标记(标记处于未激活状态),若需要跳转到该位置,可使用 C-u C-SPC 快捷键实现(详见「标记环」)。
若使用纯前缀参数执行该命令 C-u C-y ,光标会停留在插入文本的前方,并将标记设置在插入文本的末尾。使用其他任意前缀参数时,可指定粘贴更早一次的剪切内容:例如 C-u 4 C-y ,会重新插入倒数第四次剪切的内容,详见「粘贴更早的剪切内容」。
在图形化界面及支持该功能的文本模式界面中, C-y 会首先检查:其他应用程序是否在 Emacs 最后一次剪切操作之后,向系统剪贴板写入了新的文本。若检测到新内容,C-y会优先插入系统剪贴板中的文本。因此,Emacs 会将其他应用中执行的剪贴板「cut剪切」或「copy复制」操作,视作 Emacs 自身的剪切操作来处理;区别在于,这些外部操作的内容 不会被记录在 Emacs 的kill ring剪切环中 。相关细节详见「图形化界面中的 “剪切与粘贴” 操作」。
14.2.1. 剪切环(Kill Ring)
kill ring 剪切环是此前被剪切的文本块构成的列表。Emacs 中 仅有一个剪切环 ,由所有缓冲区共享,因此你可在一个缓冲区中剪切文本,再在另一个缓冲区中粘贴,这也是将文本从一个缓冲区移至另一缓冲区的常规方式。(此外还有多种实现方法:例如可将文本存入寄存器,详见「寄存器」章节;也可参考「文本累加」章节,了解其他文本移动方式。)
剪切环中的最大条目数由变量 kill-ring-max 控制,其默认值为 120。当剪切环达到该数量上限后,若执行新的剪切操作,Emacs 会删除剪切环中最旧的条目,为新内容腾出空间。
剪切环的实际内容存储在名为 kill-ring 的变量中,你可通过快捷键 C-h v kill-ring 查看剪切环的全部内容。
14.2.2. 粘贴之前的剪切内容
正如「粘贴」章节所述,你可以为快捷键 C-y 添加数字参数,来粘贴并非最近一次的删除文本。如果你能记清想要调用删除环中的哪一项内容,这个方法会非常实用。若记不清,可使用 M-y (yank-pop) 命令遍历所有删除记录,或从中选中某条更早的删除内容。
如果上一个执行的命令是粘贴类命令, M-y 会将当前已粘贴的文本,替换为删除环中更早一条的删除内容。因此,若要恢复倒数第二次的删除文本,需先执行 C-y 粘贴最后一次的删除内容,再执行 M-y 将其替换为前一次的删除内容。该操作 仅在执行C-y或其他M-y命令后有效 (若在其他命令后调用 M-y ,执行逻辑会有所不同,详见下文)。
你可以通过 最后粘贴指针 来理解 M-y 的这一运行模式:该指针始终指向删除环中的某一条目。每次执行删除操作时,最后粘贴指针会移动到删除环头部新生成的条目处。 C-y 会粘贴该指针指向的条目;在 C-y 或其他 M-y 之后执行 M-y ,会将最后粘贴指针移动到上一条目,同时缓冲区中的文本会同步替换为该条目内容。连续执行多次 M-y ,可将指针移动到删除环中的任意条目,从而将任意删除内容调入缓冲区。当指针到达删除环末尾时,再次执行 M-y 会循环回到第一条目。
M-y 仅会移动删除环中的最后粘贴指针, 不会改变删除环中条目的顺序 —— 删除环始终保持「头部为最近一次删除内容,尾部为仍保留的最早一次删除内容」的顺序。
在 C-y 或 M-y 之后执行 M-y 时,也可为其添加数字参数,该参数用于指定最后粘贴指针需要向前移动的条目数。负数字参数会让指针向删除环头部移动;若指针已在头部,会循环到尾部并继续向前移动。
当你找到目标文本并将其调入缓冲区后,即可停止执行 M-y ,此时最后粘贴的文本会保留在缓冲区中。该文本仅是删除环条目的一份副本,因此在缓冲区中编辑它, 不会改变删除环中原有的内容 。只要未执行新的删除操作,最后粘贴指针会始终停留在删除环的当前位置,重复执行 C-y 会粘贴同一条历史删除内容的另一份副本。
为 C-y 添加数字参数执行时,最后粘贴指针也会同步指向此次粘贴的条目。
你也可在非粘贴类命令后调用 M-y ,此时 M-y 会在迷你缓冲区中弹出提示,让你选择某条历史删除内容。你可使用 迷你缓冲区历史命令 (参见「迷你缓冲区历史」)遍历或搜索删除环中的条目,直至找到想要重新插入的内容;也可使用补全命令(参见「补全命令」),对删除环条目列表进行补全匹配,或弹出 *Completions* (补全)缓冲区,从候选条目中选择目标内容。选中删除环条目后,你还可在迷你缓冲区中对其进行编辑。最后按下 RET (回车)退出迷你缓冲区,选中的删除环条目文本就会插入到缓冲区中。与在粘贴命令后执行 M-y 的情况相同,最后粘贴指针会指向此次刚粘贴的文本(无论该文本是某条原始历史删除内容,还是你编辑后再插入的删除环条目 —— 若为后者,编辑后的条目会被添加到删除环的头部)。因此,这种情况下执行 C-y ,也会粘贴刚插入的文本的另一份副本。
在非粘贴类命令后,使用纯前缀参数 (C-u M-y) 调用 M-y 时,该命令会将光标定位在插入文本的前方,并在文本末尾设置标记,与 C-y 的默认行为一致。
14.2.3. 追加剪切内容(Appending Kills)
默认情况下,每一个删除命令都会向删除环中新增一条独立条目。但 连续执行的两个或多个删除命令 ,会将其删除的文本合并为删除环中的单一条目,如此一来,单次执行 C-y 即可将所有文本作为一个整体粘贴回来,与删除前的原貌一致。
因此,若你希望将一段文本作为整体粘贴,无需用单个命令一次性删除全部内容;你可以逐行、逐个单词地连续执行删除操作,直至删完目标文本,后续仍能一次性将其全部恢复。
从光标处向前删除的命令,会将删除的文本追加到上一次删除内容的末尾;从光标处向后删除的命令,会将删除的文本添加到上一次删除内容的开头。通过这种方式,无论你混合执行多少次向前、向后的删除命令,所有被删除的文本都会按原有顺序合并为删除环中的单一条目,不会发生内容错乱。为删除命令添加数字参数,并不会打断这种删除内容的追加序列。例如,假设缓冲区中有如下文本:
This is a line ∗of sample text.
其中 ∗ 为光标位置。若你依次执行 M-d (删除后一个单词)、 M-DEL (删除前一个单词)、 M-d 、 M-DEL ,交替进行向前、向后删除,最终删除环中会新增单一条目a line of sample,而缓冲区中剩余文本为 "This is text." (注意is与text之间的两个空格,可使用 M-SPC 或 M-q 整理)。
删除上述相同文本的另一种方式:先执行 M-b M-b 将光标向前移动两个单词,再执行 C-u M-d 向前一次性删除四个单词。这种操作在缓冲区和删除环中产生的结果,与前一种交替删除的方式完全一致。即便执行 M-f M-f 将光标后移两个单词,再执行 C-u M-DEL 向后一次性删除四个单词,最终结果也毫无差别 —— 删除环中该条目内的文本,始终与删除前其在缓冲区中的原有顺序保持一致。
若某一删除命令与上一次删除命令之间,插入了 其他非删除命令 (仅添加数字参数除外),则该命令会在删除环中新建一条独立条目。但你可通过提前执行 C-M-w (append-next-kill 追加下一次删除) ,强制让该删除命令与上一次的删除内容合并。 C-M-w 的作用是:告知其后紧跟的命令(若为删除命令),将此次删除的内容视作上一次删除序列的一部分。与常规的追加规则一致,若该删除命令为向前删除,则将文本追加到上一次删除内容的末尾;若为向后删除,则将文本添加到上一次删除内容的开头。通过这种方式,你可以删除缓冲区中多处不连续的文本,并将其全部累积为一个整体,后续在某一位置一次性粘贴回来。
执行 M-w (kill-ring-save 保存到删除环,即复制) 后,紧跟的删除命令不会将删除的文本,追加到 M-w 复制到删除环中的内容之后。
14.3. 图形界面中的 “复制 - 粘贴” 操作
在大多数图形化桌面环境中,你可通过名为 clipboard剪贴板 的系统功能,在不同应用程序间传输数据(通常为文本)。在 X 窗口系统中,还有另外两种类似的功能可用: 主选择区 和 次选择区 。当 Emacs 在图形化显示界面中运行时,其删除和粘贴命令会与这些系统功能集成,因此你能轻松在 Emacs 与其他图形化应用之间传输文本。
默认情况下,Emacs 将 UTF-8 用作程序间文本传输的编码系统。若你发现粘贴的文本与预期不符,可通过键入 C-x RET x 或 C-x RET X 指定其他编码系统;也可通过自定义 x-select-request-type 变量,请求使用不同的数据类型。详见《进程间通信的编码系统》章节。
14.3.1. 使用剪贴板
clipboard 剪贴板是绝大多数图形化应用程序用于 剪切粘贴 的系统功能。当系统存在剪贴板时,Emacs 的删除和粘贴命令会直接调用该功能。
当你通过 C-w (king-region) 这类命令删除文本,或通过 M-w (kill-ring-save) 这类命令将文本复制至删除环时, 该文本也会同时存入系统剪贴板 。
Emacs 的删除命令向剪贴板写入文本时,剪贴板中原有的内容默认会被覆盖。你可手动配置 Emacs,将剪贴板原有内容保存至删除环,避免旧数据丢失:若将变量 save-interprogram-paste-before-kill 设为数字值,当剪贴板原有内容的字符数小于该数字时,会自动将其复制到删除环;若将该变量设为非 nil 的其他值,则无论内容大小,都会始终将剪贴板原有内容复制到删除环 —— 但此设置可能因剪贴板内容过大,导致 Emacs 内存占用过高。
C-y (yank) 这类粘贴命令同样会调用剪贴板。若 剪贴板的归属权属于其他应用程序 (即你在其他应用中执行剪切 / 复制操作的时间,晚于在 Emacs 中执行最后一次删除命令的时间),Emacs 会优先从 系统剪贴板 粘贴内容,而非从自身的 kill rang删除环中读取。
默认情况下,使用 M-y (yank-pop) 遍历 Emacs 删除环的操作, 不会修改系统剪贴板的内容 ;若将变量 yank-pop-change-selection 设为t,则执行 M-y 时,会将当前遍历到的粘贴内容同步保存至系统剪贴板。
若要禁止 Emacs 的 kill 删除 、粘贴命令访问系统剪贴板,将变量 select-enable-clipboard 设为 nil 即可。
程序可向剪贴板存入纯文本以外的内容,例如网页浏览器中对图片执行「复制图片」操作后,图片数据会被存入剪贴板。在支持该功能的平台上,你可通过 Emacs 的 yank-media 命令粘贴这类对象 —— 仅在支持该特性的编辑模式下可用 (详见《Emacs Lisp 参考手册》中的「粘贴媒体内容」章节)。
许多 X 窗口桌面环境支持 clipboard manager剪贴板管理器功能 :若 Emacs 是当前剪贴板数据的归属程序,且系统中正在运行剪贴板管理器,那么退出 Emacs 时,Emacs 会将剪贴板数据传输至剪贴板管理器,避免数据丢失。部分场景下,该传输过程可能导致 Emacs 退出卡顿;若要禁止此行为,将变量 x-select-enable-clipboard-manager 设为 nil 即可。
由于包含 空字节(NUL) 的字符串在通过剪贴板传输时,通常会被截断,因此 Emacs 会在将这类字符串传输至系统剪贴板前,自动将空字节替换为转义字符“\0”。
在Emacs 24 版本之前,其删除和粘贴命令默认调用的是主选择区(详见「与其他窗口应用的剪切粘贴」小节),而非系统剪贴板。若你希望恢复旧版行为,需同时完成以下配置:将 select-enable-clipboard 设为 nil 、 select-enable-primary 设为 t 、 mouse-drag-copy-region 设为 t 。配置后,你可通过以下命令显式操作系统剪贴板:
clipboard-kill-region:删除指定区域文本,并将其保存至剪贴板;clipboard-kill-ring-save:将指定区域文本复制至 Emacs 删除环,同时保存至剪贴板;clipboard-yank:在光标位置粘贴系统剪贴板中的内容。
14.3.2. 与其他窗口应用程序的复制 - 粘贴
在 X 窗口系统、PGTK 图形工具包及 Haiku 系统中,存在 primary selection主选择区 功能,其中会保存 X 应用中最后一次选中的文本(通常通过鼠标拖动选中)。通常情况下,在其他 X 应用中单击 mouse-2 鼠标中键,即可将该文本插入对应位置。主选择区与剪贴板相互独立,其内容的留存性更低 —— 每次用鼠标选中新文本时,主选择区的内容就会被覆盖,而剪贴板的内容仅会被显式的剪切或复制命令覆盖。
在 X 窗口系统中,只要 Emacs 的 区域处于激活 状态(参见《标记与区域》),区域内的文本就会自动保存到主选择区。无论该区域是通过鼠标拖动、点击创建(参见《编辑相关的鼠标命令》),还是通过键盘命令创建(例如按下 C-SPC 设置标记后移动光标),此规则均适用。
若将变量 select-active-regions 设为 only ,Emacs 仅会将 临时激活的区域 (即通过鼠标或移位选择创建的区域,参见《移位选择》)保存至主选择区;若将该变量设为 nil ,Emacs 则完全不会将激活区域的内容保存到主选择区。
要将主选择区的内容插入 Emacs 缓冲区,在目标位置单击 mouse-2 (mouse-yank-primary) 即可(参见《编辑相关的鼠标命令》)。若已启用 select-enable-primary 变量(参见《使用剪贴板》),也可使用 Emacs 常规的粘贴命令 C-y 来插入该文本。
默认情况下,即便在其他程序中选中了文本,Emacs 的区域仍会保持激活状态,这与 X 窗口系统的常规行为不符。若希望当其他程序向主选择区存入数据后,Emacs 自动取消区域激活,启用全局次要模式 lost-selection-mode 即可。
MS-Windows微软视窗系统本身不提供主选择区功能,但 Emacs 会通过内部 存储选中的文本 ,在单个 Emacs 会话中模拟该功能。因此,所有与主选择区相关的功能和命令,在视窗系统中对 同一 Emacs 会话内 的剪切粘贴操作均有效,与在 X 窗口系统中的表现一致;但跨 Emacs 会话,或与其他应用程序之间的剪切粘贴,该模拟功能则无法生效。
14.3.3. 次要选择(Secondary Selection)
除主选择区外,X 窗口系统还提供了另一项功能相似的机制,即 secondary selection次选择区 。如今鲜有 X 应用程序会用到次选择区,但你可以通过以下 Emacs 命令对其进行操作:
M-Drag-mouse-1拖动鼠标左键设置次选择区,选区的一端为按下鼠标键的位置,另一端为松开鼠标键的位置(对应命令
mouse-set-secondary)。拖动过程中,选中的文本会以次选择区面版的样式高亮显示。若将鼠标拖至窗口顶部或底部外侧,窗口会自动滚动,该行为与mouse-set-region命令一致(参见《编辑相关的鼠标命令》)。该命令不会修改Emacs 的 kill ring删除环。
M-mouse-1单击鼠标左键- 设置次选择区的一个端点 (
mouse-start-secondary) ;可配合M-mouse-2单击鼠标右键设定另一个端点,完成选区的创建。执行该命令新建次选择区时,会取消当前已存在的任意次选择区。 M-mouse-3单击鼠标右键- 设置次选择区 (
mouse-secondary-save-then-kill) ,选区的一端为单击该按键的位置,另一端为之前通过M-mouse-1单击鼠标左键指定的位置。该操作会同时将选中的文本存入 Emacs 的删除环。若在同一位置再次执行M-mouse-3单击鼠标右键,则会删除刚通过次选择区选中的文本。 M-mouse-2单击鼠标中键- 将次选择区的内容插入到单击位置,且光标会定位在粘贴文本的末尾 (
mouse-yank-secondary) 。
对 M-mouse-1 单击鼠标左键执行双击或三击操作时,会按单词、按行进行选区匹配,行为与普通的鼠标左键双击 / 三击基本一致。
若变量 mouse-yank-at-point 设为非 nil 值,则 M-mouse-2 单击鼠标中键会在当前光标位置粘贴内容,此时鼠标单击的具体位置、甚至单击的是框架中的哪个窗口,均不影响粘贴结果(参见《编辑相关的鼠标命令》)。该用户选项同样会作用于交互式搜索:若其值为非 nil,在框架内任意位置通过鼠标执行的粘贴操作,都会将对应文本添加至搜索字符串中。
14.4. 文本累积(Accumulating Text)
通常我们通过 killing删除 + yanking粘贴 的方式复制或移动文本,但如需将某段文本复制到多个位置,或把多处分散的文本复制到同一位置,还有其他更便捷的方法。本节将介绍把分散的文本片段累积到缓冲区或文件中的相关命令。
M-x append-to-buffer- 将选中的区域文本追加至指定缓冲区的内容中。
M-x prepend-to-buffer- 将选中的区域文本前置插入至指定缓冲区的内容中。
M-x copy-to-buffer- 将选中的区域文本复制到指定缓冲区,清空该缓冲区原有的所有内容。
M-x insert-buffer- 将指定缓冲区的全部内容,插入到当前缓冲区的光标位置。
M-x append-to-file- 将选中的区域文本追加至指定文件的末尾。
若要将文本累积到缓冲区中,可使用 M-x append-to-buffer 。执行该命令时,会先提示你输入缓冲区名称,随后将选中区域的文本副本插入至该指定缓冲区;若指定的缓冲区不存在,该命令会自动创建。文本的插入位置为 目标缓冲区的当前光标处 :若该缓冲区此前被用于编辑,复制的文本会从光标当前位置开始,插入到缓冲区原有文本的中间位置。
执行后,目标缓冲区的光标会停留在复制文本的末尾,因此连续执行 M-x append-to-buffer 时,文本会按照 复制的先后顺序 累积到指定缓冲区中。严格来说,该命令并非始终将文本追加到缓冲区原有内容的末尾 —— 仅当目标缓冲区的光标处于末尾位置时,才会实现「追加」效果。但如果仅使用该命令修改某一缓冲区,光标会始终保持在缓冲区末尾,也就始终实现追加。
M-x prepend-to-buffer 的功能与 M-x append-to-buffer 基本一致,唯一区别是:执行后目标缓冲区的光标会停留在复制文本的前方,因此连续执行该命令时,文本会按与复制顺序相反的方式添加。 M-x copy-to-buffer 与之类似,但会先 清空目标缓冲区的所有原有内容 ,最终该缓冲区中仅保留本次新复制的文本。
使用 C-x x i (insert-buffer) 命令,可从其他缓冲区中调取累积的文本。执行该命令时会提示输入缓冲区名称,随后将该缓冲区的全部文本副本插入到 当前缓冲区的光标位置 ,且光标会停留在插入文本的开头;同时会将插入文本的末尾位置添加到 mark ring标记环 中,且不会激活标记。关于缓冲区的基础信息,可参见《使用多个缓冲区》章节。
除了将文本累积到缓冲区,也可使用 M-x append-to-file 直接将文本追加到文件中。执行该命令时会提示输入文件名,随后将选中区域的文本添加到指定文件的末尾,且修改会 立即同步到磁盘上的文件 。
注意: append-to-file 该命令仅适用于 未在 Emacs 中打开 的文件。若对 Emacs 中正在编辑的文件执行此命令,会在 Emacs 后台修改文件内容,可能导致部分编辑操作的内容丢失。
另一种文本移动的方法是将文本存储到寄存器中,相关操作可参见《寄存器》章节。
14.5. 矩形操作(Rectangles)
Rectangle 矩形操作命令作用于文本的 矩形区域 :即某几行范围内、指定两列之间的所有字符。Emacs 提供了多种矩形操作命令,可实现矩形区域的删除、粘贴、清空、填充空白或指定文本,或直接删除矩形区域等功能。矩形命令适用于多列格式的文本编辑,也可将普通文本转换为多列格式,或反向转换。
要为命令指定操作的矩形区域,需在矩形的 一个对角 设置标记,将光标置于 对角的另一端 ,这样定义的矩形区域被称为 region-rectangle区域矩形 。若光标与标记处于同一列,该区域矩形为空;若处于同一行,该区域矩形的高度为一行。
区域矩形的控制方式与普通区域基本一致,但需注意:同一组光标与标记的位置组合,会根据调用命令的不同,被解析为 普通区域 或 矩形区域 。
也可通过鼠标标记矩形区域: C-M-mouse-1 按住并拖动鼠标左键,从矩形的一个对角拖至另一个对角即可。
矩形操作核心快捷键
C-x r k- 删除区域矩形内的文本,并将其内容保存为 the last killed rectangle最近一次删除的矩形 (
kill-rectangle) 。 C-x r M-w- 将区域矩形内的文本保存为最近一次删除的矩形,不删除原文本 (
copy-rectangle-as-kill) 。 C-x r d- 直接删除区域矩形内的文本 (
delete-rectangle) 。 C-x r y- 粘贴最近一次删除的矩形,其左上角对齐当前光标位置 (
yank-rectangle) 。 C-x r o- 插入空格填充到当前区域矩形,将原矩形内的内容向右推移 (
open-rectangle) 。 C-x r N- 在区域矩形的左侧边缘插入行号,将原矩形内的内容向右推移 (
rectangle-number-lines) 。 C-x r c- 将区域矩形内的所有内容替换为空格,实现清空效果 (
clear-rectangle) 。 M-x delete-whitespace-rectangle- 删除指定矩形区域内每行的空白字符,从矩形左边缘对应的列开始执行。
C-x r t 字符串 RET- 将矩形区域内的每行内容替换为指定字符串 (
string-rectangle) 。 M-x string-insert-rectangle RET 字符串 RET- 在矩形区域的每行插入指定字符串。
C-x SPC- 切换矩形标记模式 (
rectangle-mark-mode) 。该模式激活时,区域矩形会高亮显示,可调整其大小,且标准的删除、粘贴命令会直接作用于该矩形区域。
矩形操作的分类与核心逻辑
矩形操作主要分为两类: erase擦除 / insert插入矩形的命令 ,以及 make blank rectangles创建空白矩形的命令 。
一、擦除矩形区域的两种方式
C-x r d(delete-rectangle) :直接删除矩形内的文本,不保存;C-x r k(kill-rectangle) :删除文本并将其保存为last killed rectanle最近一次删除的矩形。
两种方式的擦除效果一致:均会删除矩形内每行的指定文本,若该行擦除位置后还有其他文本,后续文本会向左移动填补空白。
注意:删除矩形并非普通意义上的「删除」—— 矩形内容不会存入 删除环 ,而是保存在一个专门的区域,且该区域仅记录 最近一次删除的矩形 。这是因为矩形粘贴与普通线性文本的粘贴逻辑差异极大,需使用专用的粘贴命令,且矩形操作不支持 粘贴循环 功能。
C-x r M-w (copy-rectangle-as-kill) 是矩形操作的「复制」命令,等同于普通文本的 M-w :仅将矩形内容记录为最近一次删除的矩形,不会从缓冲区中删除原文本。
二、矩形粘贴
执行 C-x r y (yank-rectangle) 可粘贴最近一次删除的矩形:矩形的第一行插入在光标位置,第二行插入在光标正下方同一列的位置,后续行依次向下排列,受影响的行数由保存的矩形高度决定。
示例:将两个单列列表合并为双列列表,可将其中一个单列列表作为矩形删除,再将其粘贴至另一个列表的右侧。
此外,还可通过 C-x r r r 寄存器和 C-x r i r 寄存器,将矩形内容存入寄存器或从寄存器中调取,详见《将矩形保存至寄存器》章节。
三、创建空白矩形
有两个命令可创建空白矩形:
C-x r c(clear-rectangle) :将现有区域矩形内的文本替换为空格,保留矩形区域的占位;C-x r o(open-rectangle) :直接插入一个空白的矩形区域,原区域内容向右推移。
四、其他常用矩形命令
delete-whitespace-rectangle:删除矩形区域内每行的水平空白字符,以矩形 左边缘的列 为起始位置,矩形的右边缘对该命令无影响;C-x r N(rectangle-number-lines) :在区域矩形的左侧边缘插入行号,默认从数字 1 开始(对应矩形的第一行)。若添加前缀参数,命令会提示输入起始行号,以及行号的格式化字符串(详见《Emacs Lisp 参考手册》中的《格式化字符串》章节);C-x r t(string-rectangle) :将矩形区域内的每行内容替换为指定字符串,字符串的宽度无需与矩形宽度一致:若字符串更短,矩形右侧的文本会向左移动;若字符串更长,矩形右侧的文本会向右移动;string-insert-rectangle:与string-rectangle类似,但不会替换原内容,仅在矩形区域的每行插入指定字符串,原文本向右推移。
矩形标记模式(Rectangle Mark Mode)
C-x SPC (rectangle-mark-mode) 用于切换区域的高亮类型(必要时会先激活普通区域):模式启用时,高亮显示 区域矩形 ,调整区域大小的命令(如 C-f 、 C-n 等)会 按矩形方式 调整,且删除、粘贴命令会直接作用于该矩形区域(详见《删除与移动文本》章节)。该模式仅在 区域处于激活状态 时有效。
区域矩形仅在 标记激活 时生效。尤其当 临时标记模式(Transient Mark mode) 关闭时(详见《关闭临时标记模式》),除了按下 C-x SPC ,还需手动激活标记。
与普通区域不同,区域矩形的对角可延伸至 缓冲区末尾之外 ,也可定位在光标通常无法进入的空白区域内(例如 TAB 制表符的中间位置)。
当区域处于激活状态且开启矩形标记模式时,执行 C-x C-x 会调用 rectangle-exchange-point-and-mark 命令,该命令会在区域矩形的 四个对角之间循环切换 光标与标记的位置。若要在对标记文本执行操作前调整区域矩形的尺寸,该命令会非常实用。
14.6. CUA 键绑定(CUA Bindings)
执行命令 M-x cua-mode 可配置一套与 通用用户访问(CUA) 系统兼容的键位绑定,该系统被广泛应用于其他各类应用程序中。
启用 CUA 模式后,按键 C-x 、 C-c 、 C-v 和 C-z 将分别执行剪切(kill删除)、复制、粘贴(yank)和撤销命令。 仅当区域处于激活状态时 , C-x 和 C-c 才会执行剪切、复制操作;若区域未激活,这两个按键仍会作为 prefix keys前缀键 生效,因此 C-x C-c 这类标准 Emacs 命令依然可以正常使用。需注意,这意味着变量 mark-even-if-inactive 对 C-x 和 C-c 不产生任何作用(参见《对区域执行操作》章节)。
若标记处于激活状态,想要输入 C-x C-f 这类 Emacs 标准命令,可采用以下两种方法:一是按住 Shift 键同时按下前缀键,例如 S-C-x C-f ;二是快速连续按下两次前缀键,例如 C-x C-x C-f 。
若希望保留下文所述 CUA 模式的其他功能,仅禁用其对 Emacs 标准键位绑定的覆盖,可将变量 cua-enable-cua-keys 设为 nil 。
CUA 模式默认会激活 删除选区模式 (参见《编辑相关的鼠标命令》章节),此时输入的文本会替换激活的区域。若想使用 CUA 模式但关闭该行为,将变量 cua-delete-selection 设为 nil 即可。
CUA 模式对矩形操作提供了增强支持,包括 矩形区域高亮显示 功能:按下 C-RET 即可开始标记矩形,通过移动命令扩展矩形范围,再使用 C-x 或 C-c 对其执行剪切或复制操作。按下 RET 可将光标移动至矩形的下一个(顺时针方向)角点,方便向任意方向扩展矩形。在该模式下,输入的普通文本会插入到矩形每一行的左侧或右侧(与光标所处的一侧保持一致)。
即便不激活 CUA 模式,也可通过调用 cua-rectangle-mark-mode 命令,使用这套矩形操作增强功能。此外还有标准的 rectangle-mark-mode 命令可供使用,详见《矩形操作》章节。
在 CUA 模式下,可为剪切、复制、粘贴命令添加 单个数字的前缀参数 ,轻松将文本和矩形存入或调取自寄存器。例如, C-1 C-c 会将区域内容复制到寄存器 1 中, C-2 C-v 会粘贴寄存器 2 中的内容。
CUA 模式还提供 全局标记 功能,便于在不同缓冲区之间移动和复制文本:按下 C-S-SPC 可切换全局标记的开启与关闭状态。当全局标记开启时,所有被剪切或复制的文本会自动插入到全局标记位置,输入的文本也会插入至全局标记处,而非当前光标位置。
示例:若要将多个缓冲区中的单词复制到某一缓冲区的单词列表中,可先在目标缓冲区中设置全局标记,然后导航至每个需要添加的单词处,标记该单词(例如使用 S-M-f ),通过 C-c 或 M-w 将其复制到列表中,最后按下 RET 在目标列表的该单词后插入换行符即可。
15. 寄存器(Registers)
Emacs registers寄存器 是可用于保存文本、矩形、位置及其他内容的独立存储区域,方便后续调用。文本或矩形一旦存入寄存器,可单次或多次复制到缓冲区中;位置一旦存入寄存器,也可单次或多次跳转到该位置。
每个寄存器均有一个 a single character单字符名称 ,下文统一用 r 表示;该字符可以是字母(如“a”)或数字(如“1”), 大小写敏感 ,因此寄存器“a”与寄存器“A”并非同一个。也可使用非字母数字字符命名寄存器,例如通过 C-q C-d 的方式将 “C-d” 设为寄存器名。
一个寄存器可存储的内容类型包括:位置、文本片段、矩形、数字、窗口或框架配置、缓冲区名或文件名,但 同一时间仅能存储一种内容 。存入寄存器的内容会一直保留,直至向该寄存器存入其他内容。若要查看寄存器 r 中存储的内容,可使用 M-x view-register 命令:
M-x view-register RET r- 显示寄存器
r中存储内容的描述信息。
所有需要指定寄存器的命令,默认情况下会经过短暂延迟后,弹出一个 preview window预览窗口 ,列出当前已存在的寄存器(若有)及其对应值。寄存器的提示相关行为(包括预览功能)可通过自定义变量 register-use-preview 实现,该变量可设置为以下值:
traditional- 默认值,Emacs 表现与 29 版本之前的所有版本一致:经过指定延迟后显示已有寄存器的预览,直接输入寄存器名即可覆盖该寄存器的原有值。预览的延迟时长由可自定义变量
register-preview-delay指定(单位为秒);将该变量设为nil则禁用预览功能(但在 Emacs 提示输入寄存器时,仍可通过按下C-h或F1显式调出预览窗口)。 t启用更灵活的寄存器预览功能。Emacs 提示输入寄存器时会 立即弹出预览窗口 (
register-preview-delay不再生效),且预览窗口支持导航:可通过C-n、C-p(或上下方向键)在预览窗口的寄存器间切换。在此模式下,若要覆盖已有寄存器的值,需通过导航选中该寄存器或直接输入其名称后,按下RET确认。此外,预览窗口显示的寄存器会根据调出预览的命令进行过滤:例如
insert-register命令调出的预览,仅显示可插入缓冲区的寄存器内容,隐藏存储窗口配置、位置及其他不可插入内容的寄存器。insist- 行为与
t一致,额外支持直接 再次按下寄存器名对应的按键 退出迷你缓冲区,无需按RET确认。 nil- 行为接近
traditional,但预览窗口会 无延迟弹出 ,且寄存器列表会根据命令进行过滤。 never- 行为与
nil一致,且 直接禁用预览功能 。
Bookmarks书签 用于记录文件及其对应的位置,方便再次打开该文件时快速跳转到对应位置。书签的设计思路与寄存器相似,因此也在本章介绍。
15.1. 在寄存器中保存位置
C-x r SPC r- 将光标位置与当前缓冲区记录至寄存器
r中 (point-to-register) 。 C-x r j r- 跳转到寄存器
r中保存的缓冲区及对应光标位置 (jump-to-register) 。
按下 C-x r SPC (point-to-register) ,随后输入字符 r ,即可将当前的 光标位置 和 所属缓冲区 一同保存至寄存器 r 。该寄存器会一直保留此信息,直至向其中存入其他内容。
执行 C-x r j r 命令,会切换至寄存器 r 中记录的缓冲区,设置标记点,并将光标移动到该寄存器保存的位置(若光标已处于记录的位置,或连续调用该命令时,不会重复设置标记点)。寄存器内的内容不会因该操作发生改变,因此你可以无限次跳转到这个保存的位置。
若使用 C-x r j 跳转到已保存的位置,但该位置所属的缓冲区已被关闭,Emacs 会尝试通过重新打开原文件来重建该缓冲区。当然,此功能仅对原本关联了文件的缓冲区生效。
15.2. 在寄存器中保存文本
若你需要多次插入同一段文本的副本,从删除环中粘贴会十分不便 —— 因为后续每一次的删除操作,都会让该文本条目在删除环中不断后移。此时可将文本存入寄存器,后续再从寄存器中调取使用,这是更优的替代方案。
C-x r s r- 将选中的区域文本复制到寄存器
r中 (copy-to-register) 。 C-x r i r- 从寄存器
r中插入文本至缓冲区 (insert-register) 。 M-x append-to-register RET r将选中的区域文本追加到寄存器
r中已存储的文本末尾。当寄存器
r中存储的是文本时,你可使用C-x r +(increment-register) 向该寄存器追加内容。请注意,若寄存器r中存储的是数字,C-x r +命令的执行行为会有所不同,详见《在寄存器中存储数字》章节。M-x prepend-to-register RET r- 将选中的区域文本添加到寄存器
r中已存储的文本开头。
执行 C-x r s r 会将区域内的文本副本存入名为 r 的寄存器。若标记处于未激活状态,Emacs 会先将标记重新激活在其上次被设置的位置,该命令执行完毕后标记会被取消激活,相关说明详见《标记与区域》章节。带前缀参数执行该命令( C-u C-x r s r )时,会将文本复制到寄存器 r 的同时,从缓冲区中删除该区域的文本;你可将此操作理解为将区域文本移动到寄存器中。
执行 M-x append-to-register RET r ,会将区域文本的副本追加至名为 r 的寄存器中已存储的文本末尾。若带前缀参数执行该命令,文本追加完成后,缓冲区中的对应区域会被删除。 prepend-to-register 命令的功能与之类似,区别仅在于该命令会将区域文本添加到寄存器内文本的开头,而非末尾。
当你使用 append-to-register 和 prepend-to-register 命令收集文本时,可能需要用分隔符将各段收集的文本区分开。这种情况下,你可 配置一个寄存器分隔符 register-separator ,并将分隔符文本存入该寄存器。例如,若要在文本收集过程中使用两个换行符作为分隔符,可进行如下设置:
(setq register-separator ?+) (set-register register-separator "\n\n")
执行 C-x r i r 会将寄存器 r 中的文本插入到缓冲区中。默认情况下,光标会停留在插入文本的末尾,标记会被设置在插入文本的开头,且标记不会被激活。若带前缀参数执行该命令,光标会被置于插入文本的开头,标记则会设置在文本末尾。
15.3. 在寄存器中保存矩形
15.4. 在寄存器中保存窗口与框架配置
你可将选中框架的窗口配置保存至寄存器,甚至能将所有框架中所有窗口的配置一并保存,后续再恢复该配置。关于窗口配置的相关信息,详见《窗口操作便捷功能》章节。
C-x r w r- 将选中框架的窗口状态保存至寄存器
r(window-configuration-to-register) 。 C-x r f r- 将所有框架的状态(包括其下所有窗口,亦称为 框架集 )保存至寄存器
r(frameset-to-register) 。
可使用 C-x r j r 恢复窗口或框架配置,该命令与恢复光标位置的命令为同一个。恢复框架配置时,所有未包含在该配置中的现有框架会变为不可见状态;若你希望直接删除这些框架,可执行 C-u C-x r j r 。
15.5. 在寄存器中存储数字
Emacs 提供了专门命令,可将数字存入寄存器、将寄存器中的数字以十进制形式插入缓冲区,还能对寄存器内的数字执行自增操作。这些命令在编写键盘宏时会十分实用(详见《键盘宏》章节)。
C-u number C-x r n r- 将指定数字存入寄存器
r(number-to-register) 。 C-u number C-x r + r- 若寄存器
r中存储的是数字,将该寄存器内的数字与指定数字相加(即完成自增指定数值的操作)。请注意,若寄存器r中存储的是文本,C-x r +(increment-register) 的执行行为会有所不同,详见《将文本保存到寄存器》章节。 C-x r i r- 将寄存器
r中的数字插入至缓冲区中。
C-x r i 是通用命令,既可插入寄存器中的数字,也可插入寄存器内存储的其他任意类型内容。执行 C-x r + 时若未指定数字参数,会将寄存器内的数值自增 1;执行 C-x r n 时若未指定数字参数,会将0存入目标寄存器。
15.6. 在寄存器中存储文件与缓冲区名称
若你需要频繁打开某些文件,可将其文件名存入寄存器,后续访问会更为便捷。将文件名存入寄存器 r 的 Lisp 代码如下:
(set-register r '(file . name))
例如,执行代码:
(set-register ?z '(file . "/gd/gnu/emacs/19.0/src/ChangeLog"))
即可将示例中的文件名存入寄存器 "z"。
要打开寄存器 r 中存储的文件名对应的文件,键入 C-x r j r 即可(该命令与跳转到保存的光标位置、恢复框架配置的命令为同一个)。
同理,若你需要频繁切换至某些缓冲区,也可将其缓冲区名存入寄存器。例如,若你经常打开 *Messages* 缓冲区,可执行以下代码将该缓冲区名存入寄存器 m :
(set-register ?m '(buffer . "*Messages*"))
要切换至寄存器 r 中存储的缓冲区名对应的缓冲区,键入 C-x r j r 即可。
15.8. 书签(Bookmarks)
Bookmarks 书签的作用与寄存器有相似之处,均可记录供跳转的位置;但与寄存器不同的是,书签支持长命名,且会自动永久保存,跨 Emacs 会话生效。书签的典型用法,是记录你在不同文件中阅读到的位置。
C-x r m RET- 为当前打开的文件,在光标位置创建书签(使用文件名作为默认书签名称)。
C-x r m 书签名称 RET- 在光标位置创建指定名称的书签 (
bookmark-set) 。 C-x r M 书签名称 RET- 功能与
C-x r m一致,但不会覆盖已存在的同名书签。 C-x r b 书签名称 RET- 跳转到指定名称的书签位置 (
bookmark-jump) 。 C-x r l- 列出所有已创建的书签 (
list-bookmarks) 。 M-x bookmark-save- 将当前所有书签的配置保存至默认书签文件。
要记录当前打开文件中的光标位置,可执行 C-x r m 命令,该命令会以当前文件名作为默认书签名称创建书签。若为每个书签按其指向的文件命名,后续即可通过 C-x r b (bookmark-jump) 便捷地重新打开对应文件,同时直接跳转到书签记录的位置。
在图形化显示界面中,执行 C-x r m 创建书签时,除记录位置外,还会在书签对应行的左侧页边距(参见《窗口页边距》章节)显示一个特殊图标,标识该位置存在书签。该功能可通过用户选项 bookmark-fringe-mark 控制:将其设为 nil 即可关闭页边距书签标识,默认值为 bookmark-mark (即用于标识书签的位图图标)。后续通过 C-x r b 跳转到该书签时,页边距的书签标识会重新显示。
C-x r M (bookmark-set-no-overwrite) 命令的功能与 C-x r m 基本一致,区别在于:若指定的书签名称已存在,该命令会抛出错误提示,而非直接覆盖原有书签。
键入 C-x r l (list-bookmarks) 可在独立缓冲区中列出所有书签;切换至该书签缓冲区后,你可对书签定义进行编辑,或为书签添加注释。在书签缓冲区中键入 C-h m ,可查看其专属编辑命令的更多说明。
当你关闭 Emacs 时,若书签配置有过修改,Emacs 会自动保存书签;你也可在任意时刻执行 M-x bookmark-save 命令手动保存。书签默认保存至文件 ~/.emacs.d/bookmarks (为兼容旧版 Emacs,若存在文件 ~/.emacs.bmk ,则会优先使用该文件)。书签相关命令会自动加载默认书签文件,正是通过这种 自动保存与加载 的机制,实现书签的跨 Emacs 会话永久保存。
若将变量 bookmark-save-flag 设为1,则每次执行书签创建命令后,Emacs 都会自动保存书签配置;即便 Emacs 意外崩溃,也不会丢失书签数据。该变量若设为数字值,代表每完成指定次数的书签修改后,自动执行一次保存操作;若将其设为 nil ,则 Emacs 仅会在你显式执行 M-x bookmark-save 时,才保存书签配置。
变量 bookmark-default-file 用于指定书签的默认保存文件。
若将变量 bookmark-use-annotations 设为 t ,创建书签时会弹出提示,让你为书签添加注释;当书签存在注释时,后续跳转到该书签位置,注释会自动在独立窗口中显示。
书签记录位置时,会同时保存位置周边的上下文内容,因此即便文件内容有轻微修改, bookmark-jump 仍能准确定位到书签位置。变量 bookmark-search-size 用于指定书签位置两侧需要记录的上下文字符数(注意:对于打开的加密文件,无论该变量如何设置,书签文件中都不会保存任何上下文内容)。
以下是书签的一些附加操作命令:
M-x bookmark-load RET 文件名 RET- 加载指定文件中的书签配置列表。除默认书签文件外,你可通过该命令与
bookmark-write命令,管理其他书签配置文件。 M-x bookmark-write RET 文件名 RET- 将当前所有书签配置保存至指定文件。
M-x bookmark-delete RET 书签名称 RET- 删除指定名称的书签。
M-x bookmark-insert-location RET 书签名称 RET- 在缓冲区中插入指定书签指向的文件名。
M-x bookmark-insert RET 书签名称 RET- 在缓冲区中插入指定书签指向文件的全部内容。
16. 显示控制
由于大缓冲区的内容无法在窗口中完整显示,Emacs 只能展示其中一部分内容。本章将介绍用于指定文本显示区域、设置文本显示方式的相关命令与变量。
- 滚动操作
- 居中显示(Recentering)
- 自动滚动
- 水平滚动
- 窄化显示(Narrowing)
- 查看模式(View Mode)
- 跟随模式(Follow Mode)
- 文本外观(Text Faces)
- 外观颜色设置
- 标准样式(Standard Faces)
- 图标(Icons)
- 文本缩放(Text Scale)
- 字体锁定模式(Font Lock mode)
- 交互式高亮(Interactive Highlighting)
- 窗口边缘(Window Fringes)
- 边界显示(Displaying Boundaries)
- 无用空白字符(Useless Whitespace)
- 选择性显示(Selective Display)
- 模式行可选功能
- 文本显示方式
- 光标显示(Displaying the Cursor)
- 行截断(Line Truncation)
- 视觉行模式(Visual Line Mode)
- 显示定制(Customization of Display)
16.1. 滚动操作
若窗口尺寸不足以显示缓冲区中的全部文本,只会展示其中一部分。 Scrolling commands 滚动命令可更改窗口中显示的缓冲区内容范围。
向前滚动 或 向上滚动 会推进窗口中显示的缓冲区内容;换言之,是让缓冲区文本相对窗口向上移动。向后滚动 或 向下滚动 会显示缓冲区中更靠前的内容,同时让文本相对窗口向下移动。
在 Emacs 中,“scrolling up向上滚动”或“scrolling down向下滚动” 指代 文本在窗口中的移动方向 ,而非窗口相对文本的移动方向。该术语在 “向上滚动”“向下滚动” 的现代含义普及前便已被 Emacs 采用,因此会出现一个看似反常的结果:按 PageDown 键,在 Emacs 的定义中属于 向上滚动 。
窗口中显示的缓冲区内容始终包含 光标位置 。若将光标移至窗口可视区域的下方或上方,Emacs 会自动执行滚动,让光标回到可视区域内(参见「自动滚动」)。你也可以通过以下命令手动执行滚动:
基础滚动命令
C-v/PageDown/next- 向前滚动近一整屏 (
scroll-up-command) M-v/PageUp/prior- 向后滚动 (
scroll-down-command)
C-v (scroll-up-command) 会向前滚动近整个窗口高度的内容,效果为将窗口底部的两行文本移至顶部,后续拼接此前未显示的内容。若光标原本在滚出窗口顶部的文本区域,滚动后光标会停在窗口新的首行。 PageDown (或 next )键与 C-v 功能完全等效。
M-v (scroll-down-command) 的向后滚动逻辑与之类似, PageUp (或 prior )键与 M-v 功能完全等效。
这些滚动命令执行后保留的 重叠行数 由变量 next-screen-context-lines 控制,其默认值为 2 。你可以为这些命令添加数字前缀参数 n ,实现滚动 n 行的效果;此时 Emacs 会尽量保持光标位置不变,让文本与光标同步上下移动。带负参数的 C-v 等效于 M-v ,反之亦然。
默认情况下,若窗口已到达缓冲区开头或末尾、无法继续滚动,这些命令会触发错误(通过蜂鸣或屏幕闪烁提示)。若将变量 scroll-error-top-bottom 设为 t ,命令会将光标移至当前可滚动的最远位置;若光标已处于该位置,才会触发错误。
保持光标屏幕位置
部分用户希望滚动命令能让光标停在 屏幕固定位置 ,这样滚动回同一屏时,光标可便捷地回到原位置。可通过变量 scroll-preserve-screen-position 启用该功能:
- 若值为
t:当滚动命令将光标移出窗口时,Emacs 会调整光标位置,让光标在屏幕上的位置保持不变(而非移至窗口首行 / 末行); - 若值为其他非nil值:即便滚动命令未将光标移出窗口,Emacs 也会按上述规则调整光标位置。
该变量对本节介绍的所有滚动命令、鼠标滚轮滚动均生效(参见「编辑用鼠标命令」);通常,所有带有非nil属性 scroll-command 的命令都会受其影响(参见《Emacs Lisp 参考手册》中的「属性列表」)。当 isearch-allow-scroll 为非nil时,该属性还会让 Emacs 在调用此类滚动命令时,不退出增量搜索模式(参见「不退出增量搜索」)。
优化高速滚动性能
当长按 C-v 、 M-v 等键触发 键盘自动重复 时,Emacs 有时无法跟上快速的滚动请求,会出现显示不更新、长时间无法响应输入的情况。可将变量 fast-but-imprecise-scrolling 设为非nil值,缓解该卡顿问题:该设置会让滚动命令跳过对滚过区域未做字体高亮的文本的着色(参见「字体锁定模式」),直接默认使用默认字体样式。
注意:若当前使用的字体并非统一尺寸,即便单次滚动(非自动重复),该设置也可能导致 Emacs 滚动到缓冲区的轻微错误位置。
作为 fast-but-imprecise-scrolling 的替代方案,你也可以启用 即时锁定延迟字体 高亮(参见「字体锁定模式」):将变量 jit-lock-defer-time 自定义为一个较小的正数(如 0.25,若打字速度快,可设为 0.1)。该设置能让长按 C-v 时的滚动更流畅,但滚动到缓冲区新区域时,窗口内容会短暂处于未着色状态。
第三种优化方案是设置变量 redisplay-skip-fontification-on-input :若值为非nil,当有未处理的输入时,Emacs 会跳过部分字体高亮操作。通常,若有未处理的输入,Emacs 本就会完全跳过重绘,因此该设置基本不影响显示,却能通过避免不必要的着色让滚动更顺滑。
其他滚动命令
M-x scroll-up/M-x scroll-down:与scroll-up-command/scroll-down-command功能类似,但 不遵循scroll-error-top-bottom变量的设置。Emacs 24 版本前,这两个命令是默认的上下滚动命令。M-x scroll-up-line/M-x scroll-down-line:让当前窗口逐行上下滚动。
若你打算常用上述命令,可为其绑定快捷键(参见「在初始化文件中重新绑定按键」)。
图形化界面滚动
在图形化显示界面中,你也可以通过 滚动条 实现窗口滚动(参见「滚动条」)。
16.2. 居中显示(Recentering)
C-l- 滚动选中的窗口,使当前行成为文本的正中间行;连续重复执行该命令时,会按循环顺序依次将当前行设为顶行、底行,依此类推。该命令也可能会重新刷新屏幕 (
recenter-top-bottom) 。 C-M-S-l- 滚动另一个窗口;效果等同于对另一个窗口执行
C-l命令。 M-x recenter- 滚动选中的窗口,使当前行成为文本的正中间行,也可能会重新刷新屏幕。
C-M-l- 通过启发式滚动,将有效信息显示在屏幕中 (
reposition-window) 。
C-l (recenter-top-bottom) 命令用于为选中的窗口重定中心,通过滚动让当前屏幕行恰好处于窗口正中间,或尽可能接近正中间的位置。
连续两次按下 C-l (C-l C-l),会滚动窗口使光标位于屏幕最顶行;第三次按下 C-l ,会让光标位于屏幕最底行。每一次连续按下的 C-l ,都会在这三个位置间循环切换。
你可以通过自定义列表变量 recenter-positions 来修改循环顺序。该列表的每个元素可以是符号 top(顶部)、middle(中间)、bottom(底部),也可以是数字:整数表示将当前行移动到指定的屏幕行号,0.0 到 1.0 之间的浮点数则表示将当前行定位在窗口顶部起的指定百分比位置。默认的列表值为 (middle top bottom) ,即上述的循环顺序。此外,若将变量 scroll-margin 设为非零值 n , C-l 命令会始终让光标与窗口顶部、底部保持至少 n 行的屏幕间距(参见「自动滚动」章节)。
你也可以为 C-l 命令添加前缀参数:单纯的前缀参数 C-u C-l 仅将光标所在行重定到窗口中心;正数值参数 n 会将光标所在行移至窗口顶部向下数第 n 行的位置;参数 0 会将光标所在行移至窗口顶行;负数值参数 −n 会将光标所在行移至窗口底部向上数第 n 行的位置。当指定了参数时, C-l 命令不会清空屏幕,也不会在不同屏幕位置间循环。
若变量 recenter-redisplay 设为非 nil 值,每次执行 C-l 命令都会清空并重新刷新屏幕;其特殊值 tty (默认值)表示仅在文本终端窗口中执行该刷新操作。当屏幕因任何原因出现显示错乱时,重新刷新屏幕会非常有用(参见「屏幕显示错乱」章节)。
更基础的命令 M-x recenter 与 recenter-top-bottom 功能类似,但不会在不同屏幕位置间循环。
C-M-l (reposition-window) 会通过启发式方式滚动当前窗口,旨在将有效信息展示在屏幕中。例如,在 Lisp 文件中,该命令会尽可能将当前整个函数定义(defun)完整显示在屏幕内。
16.3. 自动滚动
当光标移出文本的可视区域时,Emacs 会执行 automatic scrolling 自动滚动。默认情况下,自动滚动会将光标垂直居中在窗口中,不过有多种方式可以修改这一行为。
若将变量 scroll-conservatively 设为较小的数值 n ,当光标仅小幅移出屏幕(不超过n行)时,Emacs 会仅滚动必要的距离,将光标拉回屏幕可视范围;若此操作仍无法让光标显示,Emacs 会滚动至恰好让光标在窗口中居中的位置。若将 scroll-conservatively 设为较大的数值(大于 100),无论光标移出多远,自动滚动都不会让光标居中;Emacs 始终仅滚动必要距离让光标进入可视范围,光标最终会出现在窗口顶部或底部,具体取决于滚动方向。 scroll-conservatively 的默认值为 0 ,即始终让光标在窗口中居中。需要注意的是,在迷你缓冲区窗口中,默认的滚动行为始终为保守模式,因为变量 scroll-minibuffer-conservatively 的默认值为非空,且该变量的优先级高于 scroll-conservatively 。
控制自动滚动的另一种方式是自定义变量 scroll-step ,其值决定了光标移出屏幕时,自动滚动的行数。若滚动该行数后仍无法让光标回到可视范围,则会将光标居中显示。 scroll-step 的默认值为 0,这一设置(在默认情况下)会让滚动后光标始终保持居中。
第三种控制自动滚动的方式是自定义变量 scroll-up-aggressively 和 scroll-down-aggressively ,这两个变量可直接指定滚动后光标的垂直位置。 scroll-up-aggressively 的值可以是 nil (默认值),也可以是 0 到 1 之间的浮点数 f 。若设为浮点数,当光标超出窗口下边缘(即向前滚动)时,Emacs 会滚动窗口,使光标到窗口下边缘的距离为窗口高度的 f 倍。因此, f 值越大,滚动的激进程度越高,会有更多新文本进入可视范围。默认值 nil 等效于 0.5。
同理,当光标超出窗口上边缘(即向后滚动)时,会使用变量 scroll-down-aggressively 。其值指定了滚动后光标与窗口上边缘的距离比例,与 scroll-up-aggressively 一致,值越大,滚动的激进程度越高。
请注意,变量 scroll-conservatively 、 scroll-step 以及 scroll-up-aggressively / scroll-down-aggressively 对自动滚动的控制方式相互矛盾,因此你应仅选择其中一种方式来自定义自动滚动。若同时自定义了多个变量,其优先级顺序为: scroll-conservatively 最高,其次是 scroll-step ,最后是 scroll-up-aggressively / scroll-down-aggressively 。
变量 scroll-margin 会限制光标能靠近窗口顶部或底部的距离(即便激进滚动设置的比例 f ,超出了窗口上下边距之间的区域比例)。其值为屏幕行数,当光标与窗口顶部或底部的距离小于该行数时,Emacs 会执行自动滚动。 scroll-margin 的默认值为 0。默认情况下,有效边距的大小被限制为窗口高度的四分之一,不过可通过自定义变量 ~maximum-scroll-margin ,将该限制提高至窗口高度的一半(也可降低至 0)。
16.4. 水平滚动
Horizontal scrolling 水平滚动指将窗口内的所有行向侧面偏移,使左边缘附近的部分文本不显示。当窗口中的文本被水平滚动时,文本行将 truncated被截断 而非折行显示(参见《行截断》相关内容)。若窗口显示的是被截断的文本,每当光标移出屏幕的左边缘或右边缘时,Emacs 会执行自动水平滚动。默认情况下,窗口内的所有行会一起进行水平滚动;但如果将变量 auto-hscroll-mode 设为特殊值 current-line ,则仅显示光标的那一行会被滚动。若要完全关闭自动水平滚动,将变量 auto-hscroll-mode 设为 nil 即可。注意,当自动水平滚动关闭后,若光标移出屏幕边缘,光标会消失以作提示(在文本终端中,光标则会停留在边缘位置)。
变量 hscroll-margin 用于控制光标距离窗口左右边缘多近时触发自动水平滚动,其单位为列。例如,若该值设为 5,当光标移动到距离某一边缘 5 列范围内时,会触发水平滚动并远离该边缘。
变量 hscroll-step 决定了光标过近边缘时,窗口的滚动列数。默认值 0 表示将光标在窗口中水平居中;正整数值表示滚动的具体列数;0 到 1 之间的浮点数则表示按窗口宽度的该比例进行滚动。
你也可以通过以下命令执行显式水平滚动:
C-x <- 向左滚动当前窗口中的文本 (
scroll-left) 。 C-x >- 向右滚动 (
scroll-right) 。
C-x < (scroll-left) 会将选中窗口的文本向左滚动窗口的完整宽度减两列的距离(换言之,窗口内的文本相对窗口向左移动)。若带数字参数 n ,则向左滚动 n 列。
当文本被向左滚动后,若光标移出窗口左边缘,光标会固定在窗口左边缘,直至光标移回文本的显示区域。此行为与 auto-hscroll-mode 的当前设置无关 —— 对于向左滚动的文本,该变量仅影响窗口右边缘处的行为。
C-x > (scroll-right) 的向右滚动行为与之类似。当窗口恢复正常显示(每行均从窗口左边缘开始)后,无法再继续向右滚动,此时执行该操作无任何效果。这意味着你无需为 C-x > 精确计算参数,任何足够大的参数都能恢复正常显示。
若通过上述命令对窗口进行水平滚动,该操作会为自动水平滚动设置一个下限。自动水平滚动仍会继续作用于窗口,但向右滚动的距离绝不会超过你此前通过 scroll-left 命令设置的距离。当 auto-hscroll-mode 设为 current-line 时,除显示光标的行外,其他所有行都会按该最小距离滚动。
在图形化显示界面中,若开启可选的 horizontal-scroll-bar-mode (水平滚动条模式),你可通过水平滚动条对窗口进行水平滚动,参见《滚动条》相关内容
16.5. 窄化显示(Narrowing)
Narrowing 内容窄化指将操作焦点限定在缓冲区的某一部分,使其余部分暂时无法访问。仍可操作的这部分区域被称为 accessible portion可访问区域 。取消窄化、让整个缓冲区恢复可访问状态的操作,称为 widening内容展宽 。缓冲区当前生效的窄化范围,被称为缓冲区的 restriction访问限制 。
通过内容窄化隐藏无关内容,能让你更专注地处理单个子程序或段落;同时,它也可用于限制替换命令或重复执行的键盘宏的作用范围。
窄化相关核心命令
C-x n n- 窄化至光标与标记之间的区域 (
narrow-to-region) 。 C-x n w- 展宽缓冲区,恢复全部内容的可访问性 (
widen) 。 C-x n p- 窄化至光标所在的当前页面 (
narrow-to-page) 。 C-x n d- 窄化至光标所在的当前函数定义块 (
narrow-to-defun) 。
当缓冲区被窄化到某一部分后,该部分会呈现为缓冲区的全部内容:你无法看到其余部分,无法将光标移至该区域外(移动命令无法超出可访问区域),也无法对不可访问部分进行任何修改。但这些内容并未被删除,若你保存文件,所有不可访问的文本都会被一并保存。只要窄化状态生效,模式行中会显示Narrow(窄化)字样,提示当前的窄化状态。
核心的窄化命令为 C-x n n (narrow-to-region) ,执行该命令后,缓冲区的访问限制会被设定为当前选中的区域,仅该区域内的文本可访问,区域之前和之后的所有文本均变为不可访问,光标与标记的位置不会发生改变。
此外,可使用 C-x n p (narrow-to-page) 窄化至光标所在的当前页面(关于页面的定义,参见 “页面” 相关章节); C-x n d (narrow-to-defun) 可窄化至包含光标在内的当前函数定义块(参见 “顶层定义 或 函数定义块“ 相关章节)。
取消窄化的唯一方式是执行 C-x n w (widen) 进行内容展宽,该操作会清除缓冲区的访问限制,恢复全部文本的可访问性。
你可通过 C-x = 命令查看当前窄化的具体范围信息(参见 “光标位置信息” 相关章节)。
由于对于不了解窄化功能的用户而言,该操作极易造成操作困惑,因此 narrow-to-region 默认是 禁用命令 。首次尝试执行该命令时,Emacs 会弹出确认提示,并提供启用该命令的选项;若你选择启用,后续执行该命令将不再需要确认(关于禁用命令的相关说明,参见 “禁用命令” 相关章节)。
16.6. 查看模式(View Mode)
M-x view-mode 查看模式是一种次要模式,可按整屏顺序浏览缓冲区内容。该模式提供了便捷的缓冲区滚动命令,但不支持对内容进行修改。除常规的 Emacs 光标移动命令外,可按下 SPC (空格) 向下滚动一整屏,按下 S-SPC 或 DEL 向上滚动,按下s 启动增量搜索。
按下 q (View-quit) 会关闭查看模式,并切回开启该模式前的缓冲区及光标位置。按下 e (View-exit) 会关闭查看模式,同时保留当前的缓冲区及光标位置。
执行 M-x view-buffer会 提示选择一个已存在的 Emacs 缓冲区,切换至该缓冲区并启用查看模式。执行 M-x view-file 会提示选择一个文件,打开该文件并同时启用查看模式。
16.7. 跟随模式(Follow Mode)
Follow mode 跟随模式是一种次要模式,可让两个显示同一缓冲区的窗口,如同一个纵向的超大虚拟窗口般同步滚动。使用跟随模式的操作方法为:先打开一个仅含单个窗口的框架,通过 C-x 3 将其拆分为两个左右并排的窗口,再执行 M-x follow-mode 即可启用。启用后,你可在任意一个窗口中编辑缓冲区、滚动页面,另一个窗口会自动同步跟随操作。
在跟随模式下,若将光标从一个窗口的可视区域移出,移入另一个窗口的可视区域时,系统会自动选中该窗口 —— 这一逻辑同样将两个窗口视作同一个大窗口的不同部分。
再次执行 M-x follow-mode ,即可关闭跟随模式。
16.8. 文本外观(Text Faces)
Emacs 可以用多种不同样式显示文本,这些样式称为 外观 (face)。
每个外观可以指定多种外观属性,例如字体、高度、粗细、倾斜度、前景色与背景色,以及下划线或上划线。大多数主模式会通过字体锁定模式(Font Lock mode)自动为文本分配外观。关于这些外观如何分配的更多信息,参见字体锁定模式。
要查看当前已定义的外观及其显示效果,输入 M-x list-faces-display 。如果带上前缀参数,该命令会提示输入一个正则表达式,并只显示名称与该正则表达式匹配的外观(参见正则表达式语法)。
同一个外观在不同 框架(frame) 中可能显示不同。例如,某些文本终端不支持所有外观属性,尤其是字体、高度和宽度,还有些终端只支持有限的颜色范围。此外,为了可读性,大多数 Emacs 外观都被定义为:在浅色和深色框架背景下使用不同属性。默认情况下,Emacs 会根据每个框架当前的背景色,自动选择使用哪一组外观属性。不过,你可以通过给变量 frame-background-mode 赋予一个非 nil 的值来覆盖这一行为:
- 值为
dark会让 Emacs 把所有框架当作 深色背景 处理; - 值为
light则当作 浅色背景 处理。
你可以自定义外观以修改其属性,并将这些自定义设置保存到未来的 Emacs 会话中。详情参见自定义外观。
default (默认)外观是显示文本的基础外观,它的所有属性都已完整定义。它的背景色也会被用作框架的背景色。参见外观颜色。
另一个特殊外观是 cursor (光标)外观。在图形界面下,该外观的背景色用于绘制文本光标;此外观的其他属性均无效。光标下方文本的前景色取自底层文本的背景色。在文本终端中,文本光标的样式由终端决定,而非 cursor 外观。
你还可以使用 X 资源来指定任意特定外观的属性。参见 X 资源。
Emacs 可以显示 变宽字体 ,但部分 Emacs 命令(尤其是缩进相关命令)不会考虑字符的显示宽度变化。因此,我们建议 不要对大多数外观使用变宽字体 ,特别是由字体锁定模式分配的外观。
16.9. 外观颜色设置
外观可设置不同的前景色和背景色。为外观指定颜色时(例如自定义面版的场景,参见「自定义外观」),可使用 color name 颜色名称或 RGB triplet RGB 三元组两种方式。
16.9.1. 颜色名称
颜色名称为 pre-defined name预定义名称,例如深橙色('dark orange')、中海绿色('medium sea green')。键入命令 M-x list-colors-display 可查看颜色名称列表;通过自定义变量 list-colors-sort ,可控制颜色的显示顺序。在图形化显示器中执行该命令,会显示 Emacs 支持的所有颜色名称(均为 X11 标准颜色名称,定义于 X 系统的 rgb.txt 文件);在文本终端中执行该命令,仅会显示可在该类终端上正常显示的少量颜色子集。即便在文本终端中,Emacs 仍能识别所有 X11 颜色名称;若为某一面版指定了 X11 颜色名称对应的颜色,终端会自动匹配最相近的颜色进行显示。
16.9.2. RGB 三元组(RGB Triplets)
RGB 三元组为 "#RRGGBB" 格式的字符串,其中红、绿、蓝三原色的分量各由一个十六进制数表示,取值范围为00(亮度为 0)至FF(最大亮度)。每个颜色分量也可使用 1 位、3 位或 4 位十六进制数表示,因此红色可写作 #F00 、 #fff000000 或 #ffff00000000 , 注意各分量的位数必须保持一致 。十六进制数中的 A-F 字符,大小写均可。
执行 M-x list-colors-display 命令时,也会同时显示每个命名颜色对应的 RGB 三元组,例如中海绿色(medium sea green)对应的 RGB 三元组为 #3CB371 。
你可通过命令 M-x set-face-foreground 和 M-x set-face-background ,分别修改面版的前景色和背景色。执行该类命令时,迷你缓冲区会提示你输入面版名称和颜色(支持补全),确认后即可为该面版应用指定颜色。这两个命令的效果会作用于所有 Emacs 窗口,但与使用自定义缓冲区或 X 资源的方式不同,其设置不会在后续的 Emacs 会话中保留。你也可通过窗口参数,为指定的单个窗口设置前景色和背景色,参见「框架参数」章节。
16.10. 标准外观(Standard Faces)
以下是用于指定文本显示样式的标准样式,可将其应用于特定文本以实现对应的显示效果。
default- (默认样式)该样式适用于未指定任何样式的普通文本,其背景色会作为 Emacs 窗口的背景色。
bold- (粗体样式)使用默认字体的粗体变体。
italic- (斜体样式)使用默认字体的斜体变体。
bold-italic- (粗斜体样式)使用默认字体的粗斜体变体。
underline- (下划线样式)为文本添加下划线样式。
fixed-pitch- (等宽样式)强制使用 fixed-width 等宽字体。可根据需求自定义该样式,替换为其他等宽字体,但不可将其设为 variable-width 变宽字体。
fixed-pitch-serif- (衬线等宽样式)与等宽样式功能一致,区别在于该样式使用 serifs 带衬线的字体,视觉上更接近传统打字机的字体样式。
variable-pitch- (变宽样式)强制使用变宽(即比例)字体,其字体大小与默认样式(通常为等宽字体)的字体大小保持一致。
variable-pitch-text- (文本变宽样式)继承自变宽样式,字体尺寸略大于变宽样式。与等宽字体同高度的比例字体,视觉上通常会显得更小,可读性也会随之降低。因此在显示大篇幅文本时,该样式是比变宽样式(尺寸略小)更优的选择。
shadow- (浅显样式)用于让文本比周围的普通文本更不显眼,通常通过使用灰色调,与黑色或白色的默认前景色形成对比来实现该效果。
以下是一组不完整的样式列表,这类样式用于为特定目的临时高亮文本的特定部分(许多其他模式也会为此定义专属样式表)。
highlight- (高亮样式)在多种场景下用于文本高亮,例如鼠标光标移至超链接上时的文本高亮。
isearch- (增量搜索高亮样式)用于高亮增量搜索的当前匹配结果(参见增量搜索)。
query-replace- (查询替换高亮样式)用于高亮查询替换的当前匹配结果(参见替换命令)。
lazy-highlight- (延迟高亮样式)用于高亮增量搜索和查询替换中的非当前匹配结果(延迟匹配项)。
region- (选区样式)用于显示激活的文本选区(参见《标记与选区》)。当 Emacs 编译时启用 GTK+ 支持时,该样式的颜色会继承当前的 GTK+ 主题。
secondary-selection- (二级选区样式)用于显示 X 系统的二级选区(参见二级选区)。
trailing-whitespace- (行尾空白样式)当变量
show-trailing-whitespace非空时,用于高亮行尾的多余空格和制表符(参见无用空白字符)。 escape-glyph- (转义符号样式)用于显示控制字符和转义序列(参见文本的显示方式)。
homoglyph- (形近字符样式)用于显示形近字符,即外观相似但实际并非所代表的字符(参见文本的显示方式)。
nobreak-space- (不换行空格样式)用于显示不换行空格字符(参见文本的显示方式)。
nobreak-hyphen- (不换行连字符样式)用于显示不换行连字符(参见文本的显示方式)。
以下样式用于控制 Emacs 框架各组成部分的显示样式:
mode-line(模式行样式)是模式行的基础样式,同时也适用于标题行,且在未使用工具包菜单时适用于菜单栏。默认情况下,在图形化显示器中会为其添加阴影效果以实现凸起视觉感;在文本终端中,其显示样式为默认样式的反色。
模式行实际使用的
mode-line-active和mode-line-inactive均继承自该样式。mode-line-active- (激活模式行样式)与
mode-line样式一致,专用于当前选中窗口的模式行。该样式继承自mode-line,因此对模式行样式的修改会作用于所有窗口的模式行。 mode-line-inactive- (非激活模式行样式) 与
mode-line一致,专用于非选中窗口的模式行(当变量mode-line-in-non-selected-windows非空时生效)。该样式继承自mode-line,因此对模式行样式的修改会作用于所有窗口的模式行。 mode-line-highlight- (模式行高亮样式) 与
highlight样式功能一致,专用于模式行上的鼠标敏感文本区域。鼠标指针悬停在该类文本区域时,通常会弹出工具提示(参见工具提示)。 mode-line-buffer-id- (模式行缓冲区标识样式)用于模式行中显示缓冲区标识的部分。
header-line- (标题行样式) 与
mode-line样式功能类似,专用于窗口的标题行;标题行显示在窗口顶部,模式行则显示在窗口底部。大多数窗口无标题行,仅部分特殊模式(如 info mode信息模式)会生成标题行。 header-line-highlight- (标题行高亮样式)与高亮样式、模式行高亮样式功能一致,专用于标题行上的鼠标敏感文本区域。该样式为独立样式,因标题行样式的自定义设置可能与高亮样式不兼容。
tab-line- (标签行样式)与
mode-line样式功能类似,专用于窗口的标签行;标签行显示在窗口顶部,以标签形式展示窗口的各个缓冲区(参见窗口标签行)。 vertical-border- (垂直边框样式)在文本终端中,用于显示窗口之间的垂直分隔线。
minibuffer-prompt- (迷你缓冲区提示符样式) 用于显示迷你缓冲区中的提示符字符串。默认情况下,Emacs 会自动将该样式添加至变量
minibuffer-prompt-properties的取值中;该变量是一个文本属性列表(参见《Emacs Lisp 参考手册》中的文本属性),用于控制提示符文本的显示(进入迷你缓冲区时该变量生效)。 fringe- (边距样式)在图形化显示器中,用于显示窗口左右两侧的边距区域(边距是 Emacs 窗口中,文本区域与窗口左右边框之间的窄条区域)(参见窗口边距)。
cursor- (光标样式)该样式的
:background属性用于指定文本光标的颜色(参见光标的显示)。 tooltip- (工具提示样式)用于显示工具提示的文本。默认情况下,若 Emacs 编译时启用 GTK+ 支持,工具提示会由 GTK+ 渲染,该样式将失效(参见工具提示)。
mouse- (鼠标指针样式)用于指定鼠标指针的颜色。
以下外观样式同样用于控制 Emacs 窗口各组成部分的显示样式,但仅在文本终端中,或Emacs 基于 X 系统编译且未启用工具包支持时生效。其他场景下,对应窗口组件的显示样式由系统全局设置决定。
scroll-bar- (滚动条样式)用于指定滚动条的视觉样式(参见滚动条)。
tool-bar- (工具栏样式)用于指定工具栏图标的颜色(参见工具栏)。
tab-bar- (标签栏样式)用于指定标签栏图标的颜色(参见标签栏)。
menu- (菜单样式)用于指定 Emacs 菜单的颜色和字体(参见菜单栏)。
tty-menu-enabled-face- (终端可用菜单项样式)在文本模式终端中,用于显示可用的菜单项。
tty-menu-disabled-face- (终端禁用菜单项样式)在文本模式终端中,用于显示禁用的菜单项。
tty-menu-selected-face- (终端选中菜单项样式)在文本模式终端中,用于显示待选中的菜单项(单击鼠标或按下
RET回车键即可选中该菜单项)
16.11. 图标(Icons)
Emacs 会在部分场景下显示可点击的按钮(或其他信息类图标),你可自定义这些图标的显示样式。
此处的核心自定义项为用户选项 icon-preference (图标偏好)。通过该选项,你可以向 Emacs 设定自己对图标的整体显示偏好。该选项的取值为 图标类型列表 ,Emacs 会选用列表中首个受支持的图标类型。目前支持的图标类型包括:
image- 使用图像作为图标显示
emoji- 使用彩色表情符号作为图标显示
symbol- 使用单色符号作为图标显示
text- 使用简单文本作为图标显示
此外,你可通过命令 M-x customize-icon 对单个图标进行个性化设置,主题也可进一步修改所有图标的显示样式。
若需快速查看某一图标的说明信息,可使用命令 M-x describe-icon 。
16.12. 文本缩放(Text Scale)
要增大当前缓冲区中默认面版的字体大小,可按下 C-x C-+ 或 C-x C-= ;要减小字体大小,按下 C-x C-- ;恢复默认(全局)字体大小则按下 C-x C-0 。这些快捷键均绑定至同一个命令 text-scale-adjust ,该命令会根据最后按下的按键判断执行的操作,并通过修改默认面版的高度属性相应调整字体大小。
大多数面版并未显式设置 :height 属性,因此会继承默认面版的高度,这类面版的字体大小也会随上述命令同步缩放。
对于默认面版之外、显式设置了 :height 属性的面版,其字体大小不会受上述命令影响; header-line 标题行面版为特例:即便显式设置了 :height 属性,该面版仍会随命令同步缩放。
同理,当鼠标指针置于缓冲区文本区域时,按住 Ctrl 键滚动鼠标滚轮,也会根据滚动方向增大或减小相关面版的字体大小。
执行上述缩放命令时,后续的调节按键可省略前置的 C-x 和修饰键直接重复按下。例如, C-x C-= C-= C-= 与 C-x C-= = = 两种操作,均可将面版高度增大三级。每级缩放会将文本高度乘以1.2的系数,若需修改该系数,可自定义变量 text-scale-mode-step 。为 text-scale-adjust 命令传入数字参数0,效果与按下 C-x C-0= 一致,均可恢复默认的文本高度。
若要全局修改所有区域的字体大小,可按下C-x C-M-+、C-x C-M-=、C-x C-M– 或C-x C-M-0,也可同时按住Ctrl 键和 Meta 键滚动鼠标滚轮。若希望全局修改字体大小时,Emacs 窗口也随之自动调整尺寸,可自定义变量 global-text-scale-adjust-resizes-frames (参见简易自定义界面)。
命令 text-scale-increase 和 text-scale-decrease ,分别实现与 C-x C-+ 、 C-x C-- 完全相同的功能,仅调整当前缓冲区的字体大小。相比绑定 text-scale-adjust ,直接绑定这两个命令可能会更便捷。
命令 text-scale-set 可根据其前缀参数,将当前缓冲区的字体大小缩放至指定的绝对级别。
当当前文本的缩放比例非 1 时,上述所有缩放命令会自动启用次要模式 text-scale-mode ;缩放比例恢复为 1 时,该模式则会自动关闭。
命令 text-scale-pinch 支持通过触控板的捏合手势调整文本缩放比例:将两根手指放在触控板上相互靠近或远离,命令会根据手指间的距离变化放大或缩小文本。该功能仅在部分搭载兼容硬件的系统上可用。
命令 mouse-wheel-text-scale 也可调整文本缩放比例,该命令通常在按住 Ctrl 键滚动鼠标滚轮时触发,向下滚动滚轮会放大文本,向上滚动滚轮则会缩小文本。
16.13. 字体锁定模式(Font Lock mode)
字体锁定模式是一种次要模式,仅作用于单个缓冲区,该模式会为缓冲区中的文本分配对应面版(即进行字体着色)。每个缓冲区的主模式会告知字体锁定模式需要对哪些文本进行着色;例如,编程语言相关主模式会为注释、字符串、函数名等语法相关结构进行字体着色。
支持字体锁定模式的主模式会默认启用该功能。在当前缓冲区中切换其开关状态,可执行命令 M-x font-lock-mode ;传入正数值参数会强制启用字体锁定模式,传入负数或 0 参数则会关闭该模式。
执行命令 M-x global-font-lock-mode 可在所有缓冲区中统一切换字体锁定模式的开关。若要让该设置在后续的 Emacs 会话中生效,可自定义变量 global-font-lock-mode (参见简易自定义界面),或在初始化文件中添加以下代码行:
(global-font-lock-mode 0)
若你已关闭全局字体锁定模式,仍可通过将函数 font-lock-mode 添加至模式钩子,为特定主模式单独启用字体锁定功能(参见钩子)。例如,为 C 语言文件编辑启用该模式,可添加以下配置:
(add-hook 'c-mode-hook 'font-lock-mode)
字体锁定模式会使用多个特定命名的面版完成着色工作,包括 font-lock-string-face (字符串着色面版)、 font-lock-comment-face (注释着色面版)等。查看所有相关面版的最简方式为执行 M-x customize-group RET font-lock-faces RET ,随后可在该自定义缓冲区中修改这些面版的显示样式(参见自定义面版)。
对超大缓冲区进行字体着色会消耗较多时间。为避免打开文件时出现长时间卡顿,Emacs 初始状态下仅会对缓冲区的可见区域进行着色。当你滚动浏览缓冲区时,新进入可见区域的文本会在显示的同时完成着色;这种字体锁定方式被称为 即时锁定 (Just-In-Time (or JIT))。你可通过自定义 jit-lock自定义组 中的相关变量,控制即时锁定的行为,包括设置让 Emacs 在空闲时执行着色操作(参见自定义特定项)。
主模式用于判断缓冲区中哪些文本需要着色、以及为其分配何种面版的依据,来自多种不同的文本分析方式:
- 基于正则表达式,检索关键字及其他文本模式(参见正则表达式搜索);
- 基于内置的语法表,识别文本中语法上彼此独立的部分(参见《Emacs Lisp 参考手册》中的《语法表》章节);
- 通过专用类库(如 tree-sitter 类库,参见《Emacs Lisp 参考手册》中的程序源代码解析章节)或外部程序,调用完整解析器生成的语法树进行分析。
- 传统字体锁定
- 基于解析器的字体锁定
16.13.1. 传统字体锁定
提供字体锁定信息的「Traditional传统」方法,基于正则表达式搜索,以及借助 Emacs 内置语法表开展的句法分析。本小节将介绍,针对采用这些传统方法的主模式,字体锁定功能的使用与自定义方式。
对于支持该特性的主模式,你可通过自定义变量 font-lock-maximum-decoration ,控制字体锁定模式的着色精细程度。该变量的取值可为数字(数字 1 代表最低程度的着色,部分模式支持最高至 3 级的着色程度);也可为 t ,表示「尽可能高的着色程度」(默认值)。若要让该自定义设置对某一文件缓冲区生效,需在打开文件前完成 font-lock-maximum-decoration 的配置;若已打开文件后才修改该变量,需关闭该缓冲区,重新打开文件使设置生效。
你也可为特定主模式单独指定不同的着色等级。例如,要将 C/C++ 模式的着色等级设为 1,其他模式使用默认等级,可将该变量设为以下值:
'((c-mode . 1) (c++-mode . 1)))
注释与字符串的着色(或称「句法性」着色),依赖对缓冲区文本句法结构的分析。为提升处理速度,包括 Lisp 模式在内的部分模式遵循一项特殊约定: 最左侧列中的左括号或左大括号,始终表示函数定义(defun)的起始位置,因此永远处于任意字符串或注释之外 。因此,若左括号或左大括号出现在字符串或注释内部,请勿将其放在最左侧列。详情参见「左边界约定」相关内容。
绝大多数模式都已预置字体锁定的高亮匹配规则,但若你需要为额外的内容模式添加着色,可使用函数 font-lock-add-keywords ,为特定模式自定义高亮匹配规则。例如,要在 C 语言的注释中高亮显示”FIXME:“字样,可使用以下代码:
(add-hook 'c-mode-hook
(lambda ()
(font-lock-add-keywords nil
'(("\\<\\(FIXME\\):" 1
font-lock-warning-face t)))))
若要从字体锁定的高亮匹配规则中移除关键词,可使用函数 font-lock-remove-keywords ,详情参见《Emacs Lisp 参考手册》中的「基于搜索的着色」章节。此外,你也可通过自定义 font-lock-ignore 选项,选择性禁用部分关键词的高亮效果,详情参见《Emacs Lisp 参考手册》中的「自定义关键词」章节。
16.13.2. 基于解析器的字体锁定
若你的 Emacs 编译时集成了 tree-sitter 库,便可利用该库对缓冲区文本的解析结果实现文本着色。这一方式通常比上一小节所述的「传统方法」更快、更精准,因为 tree-sitter 库为其所支持的编程语言及其他格式文本,提供了功能完善的专用解析器。基于 tree-sitter 库开发的主模式均命名为 foo-ts-mode 格式,其中的 '-ts-' 后缀即表示该模式启用了此解析库。本小节将介绍基于 tree-sitter 库的字体锁定功能相关用法。
对于基于 tree-sitter 的主模式,你可通过自定义变量 treesit-font-lock-level ,控制其字体锁定模式的着色精细程度。该变量的取值为 1 至 4 之间的整数,各等级对应的着色范围如下:
- 等级 1
- 仅对注释,以及函数定义中的函数名进行着色。
- 等级 2
- 在等级 1 基础上,新增对关键字、字符串和数据类型的着色。
- 等级 3
- 默认着色等级;在等级 2 基础上,新增对赋值语句、数字等内容的着色。
- 等级 4
- 最高着色等级;在等级 3 基础上,对所有可着色内容进行标注,包括运算符、分隔符、方括号、其他标点符号、函数调用中的函数名、属性查找表达式、变量等。
上述各类句法范畴的具体判定标准,由对应主模式及 tree-sitter 为该模式所属语言提供的解析器语法共同决定。但总体而言,这些范畴均遵循该主模式所支持的编程语言或文件格式的通用规范。变量 treesit-font-lock-feature-list 的缓冲区局部值,存储了基于 tree-sitter 的主模式所支持的所有着色特性,其中每个子列表对应一个着色等级,展示该等级所包含的具体着色特性。
通过 M-x customize-variable 命令修改 treesit-font-lock-level 的取值后(参见「自定义特定项」),该设置会立即在当前 Emacs 会话的所有已打开缓冲区中生效,且后续打开的文件也会沿用此设置。
16.14. 交互式高亮(Interactive Highlighting)
Highlight Changes mode 变更高亮模式是一种次要模式,会为缓冲区中 最近修改过的内容 应用不同的文本外观,实现高亮标记。启用或关闭该模式可执行命令 M-x highlight-changes-mode 。
Hi Lock 模式是一款次要模式,可高亮显示缓冲区中与 你指定的正则表达式 相匹配的文本。例如,可通过该模式高亮程序源文件中某个变量的所有引用、某程序大量输出结果中的特定内容,或是一篇文章里的指定名称。启用或关闭 Hi Lock 模式执行命令 M-x hi-lock-mode ;若要为所有缓冲区全局启用该模式,可执行 M-x global-hi-lock-mode ,或在 .emacs 配置文件中添加代码 (global-hi-lock-mode 1) 。
Hi Lock 模式的工作逻辑与字体锁定模式类似(参见字体锁定模式相关内容),区别在于前者需由用户显式指定要高亮的正则表达式。可通过以下命令对该模式的高亮规则进行控制(下述以 C-x w 开头的快捷键已被弃用,官方推荐使用全局的 M-s h 系列快捷键,这些旧快捷键将在未来的 Emacs 版本中移除)。
M-s h r 正则表达式 回车 外观 回车C-x w h regexp RET face RET使用指定的文本外观,高亮所有匹配该正则表达式的文本 (
highlight-regexp) 。该高亮效果会在缓冲区存续期间一直保留。例如,要使用默认高亮外观(黄色背景)标记所有 “whim” 单词,可输入M-s h r whim RET RET。任意文本外观均可用于高亮,Hi Lock 模式自身提供了多款专属外观,且已预加载至默认外观列表中;在命令提示选择外观时,可通过M-n和M-p循环切换该列表中的外观。若为该命令添加数字前缀参数,仅会对正则表达式中的对应子表达式匹配内容进行高亮。若将选项
hi-lock-auto-select-face设为非 nil 值,该命令(及其他所有需要选择外观的 Hi Lock 命令)会自动从默认外观列表中选取下一个外观,无需手动交互选择。该命令可多次执行,为不同正则表达式配置不同的高亮方式。
M-s h u regexp RETC-x w r regexp RET- 取消指定正则表达式对应的高亮效果 (
unhighlight-regexp) 。若通过菜单调用该命令,可从列表中选择要取消的正则表达式;若通过键盘调用,需在迷你缓冲区中操作 —— 迷你缓冲区会默认显示最近添加的高亮正则表达式,按M-n可切换至更早添加的表达式,按M-p可切换至更新的表达式(也可手动输入表达式,支持补全功能)。当要取消的表达式出现在迷你缓冲区时,按RET回车即可退出迷你缓冲区并取消对应高亮。 M-s h l regexp RET face RETC-x w l regexp RET face RET- 使用指定的文本外观, 高亮所有包含该正则表达式匹配内容的整行文本 (
highlight-lines-matching-regexp) 。 M-s h p phrase RET face RETC-x w p 短语 回车 外观 回车- 使用指定的文本外观,高亮匹配该短语的内容 (
highlight-phrase) 。该短语可是任意正则表达式,且命令会自动将短语中的 空格 匹配为任意空白字符,将短语中 首字母小写的字符 设为大小写不敏感匹配。 M-s h .C-x w .- 使用下一个可用的高亮外观,高亮 光标附近的符号 (
highlight-symbol-at-point) 。 M-s h wC-x w b将当前所有的「高亮正则表达式 / 外观」匹配对,插入到 光标所在的缓冲区位置 ,并为其添加注释分隔符,避免该内容干扰程序运行 (
hi-lock-write-interactive-patterns) 。若执行
M-x hi-lock-find-patterns,或在 Hi Lock 模式启用状态下打开某文件(该操作会自动调用hi-lock-find-patterns),程序会从文件的注释中 提取合适的高亮匹配 对并生效。M-s h fC-x w i从当前缓冲区的注释中提取「正则表达式 / 外观」匹配对 (
hi-lock-find-patterns) 。借助该功能,你可先通过highlight-regexp交互式添加高亮匹配对,再通过hi-lock-write-interactive-patterns将其保存到文件中,对其进行编辑(例如为正则表达式中不同括号匹配的部分配置不同高亮外观),最后通过该命令让 Hi Lock 模式根据编辑后的匹配实现高亮。变量
hi-lock-file-patterns-policy用于控制:在打开文件时,Hi Lock 模式是否 自动提取并应用 文件中包含的高亮匹配对。其取值可为:nil(从不自动高亮)、ask(询问用户,为默认值)、或一个函数。若设为函数,hi-lock-find-patterns会将提取到的匹配对作为参数调用该函数;若函数返回非 nil 值,则应用这些匹配对。注意,若直接调用hi-lock-find-patterns,无论该变量取何值,都会对提取到的匹配对执行高亮。此外,若当前主模式的符号存在于列表
hi-lock-exclude-modes中,调用hi-lock-find-patterns时将 不执行任何操作 。
16.15. 窗口边缘(Window Fringes)
在图形化显示界面中,每个 Emacs 窗口的左右两侧默认都会显示窄版的 fringes窗口边缘 区域。该区域用于显示各类符号,以此标注窗口中文本的相关状态信息。你可执行命令 M-x fringe-mode ,切换窗口边缘的显示状态或修改其宽度;该命令会作用于所有框架,若仅需修改当前选中框架的窗口边缘,可使用 M-x set-fringe-style 。通过自定义变量 fringe-mode ,可将你对窗口边缘的设置永久保存。
窗口边缘最常用的功能,是标记 continuation line折行文本 (参见《折行》相关内容):当一行文本被拆分为多个 screen lines 屏幕行显示时,除首个屏幕行外,其余所有屏幕行对应的左侧边缘会显示弯曲箭头,表明此位置并非文本的实际起始处;除最后一个屏幕行外,其余所有屏幕行对应的右侧边缘也会显示弯曲箭头,表明此位置并非文本的实际结束处。若文本的显示方向为从右到左(参见《双向编辑》相关内容),窗口边缘中弯曲箭头的含义会相应互换。
窗口边缘也会用于标记 line truncation行截断 状态(参见行《截断》相关内容):当某行文本因水平滚动而有部分内容超出可视范围时,边缘会显示短水平箭头,箭头方向即为未显示内容的所在方向。用鼠标点击该箭头,窗口会沿箭头方向水平滚动,展示隐藏的文本内容。
此外,窗口边缘还可标注其他文本状态,例如缓冲区边界(参见《显示边界》相关内容)、窗口末尾附近的未使用行(参见 indicate-empty-lines 相关配置),以及正在调试的程序当前的执行位置(参见 Emacs 下运行调试器相关内容)。
当某行文本的宽度与窗口宽度完全一致,且光标处于该行末尾时,窗口边缘区域也会被用于绘制光标。若要关闭该功能,可将变量 overflow-newline-into-fringe 设为 nil ,此时 Emacs 会对宽度与窗口完全一致的文本行执行折行或截断处理。
若你通过自定义 fringe-mode ,隐藏了窗口单侧或双侧的边缘区域,原本显示在边缘处的各类状态标记功能将无法使用。但 折行与行截断的标记 是例外情况:当窗口边缘不可用时,Emacs 会使用每行最左侧和最右侧的字符位,通过特殊 ASCII 字符标记折行与行截断状态(参见折行、行截断相关内容)。这一处理会减少每行可用于显示文本的宽度,因为用于标记折行和截断的字符位会被专门预留,无法再显示普通文本。由于缓冲区中的文本可能包含双向显示内容,即同时存在从左到右和从右到左的段落(参见双向编辑相关内容),因此即便是仅隐藏单侧的窗口边缘,Emacs 仍会在窗口两侧各预留一个字符位,用于标注折行和截断状态 —— 这是因为在从右到左的段落中,这类标记会显示在窗口的对侧位置。
16.16. 边界显示(Displaying Boundaries)
Emacs 可显示 fill-column填充列 位置的标记(参见显式填充命令相关内容)。填充列标记是一项实用功能,在编程模式及其派生模式中尤为常用(参见主模式相关内容),用于标记一个特定列的位置,该列对程序源代码的格式化排版具有特殊意义。此功能的生效前提是,缓冲区使用 fixed-pitch font等宽字体 —— 即除全角字符外,所有字符在显示时宽度一致;若缓冲区使用 variable-pitch fonts变宽字体,不同行的填充列标记可能出现对不齐的情况。
要启用填充列标记的显示功能,可使用次要模式 display-fill-column-indicator-mode 和 global-display-fill-column-indicator-mode ,二者分别用于在当前缓冲区本地启用、在所有缓冲区全局启用该标记。
你也可通过设置两个缓冲区局部变量 display-fill-column-indicator 和 display-fill-column-indicator-character ,来启用填充列标记并控制标记所使用的字符。注意,只有当两个变量均设为非 nil 值时,填充列标记才会显示(启用上述次要模式会自动为这两个变量赋值)。
可通过以下 2 个缓冲区局部变量和 1 个文本外观,对该模式进行自定义配置:
display-fill-column-indicator-column指定填充列标记的显示列号。该变量可设为正整数(表示具体列号),也可设为特殊值t(表示使用变量
fill-column的取值作为标记列号);设为其他任意值则会禁用填充列标记,该变量默认值为
t。display-fill-column-indicator-character指定用于填充列标记的字符。该字符可为任意有效字符,若字体支持,也可使用 Unicode 字符;变量设为
nil则禁用填充列标记。当通过
display-fill-column-indicator-mode或global-display-fill-column-indicator-mode启用该模式时,若此变量为非 nil 值,将使用该变量指定的字符作为标记;若为 =nil=,Emacs 会默认使用字符U+2502(细竖线绘制符),若该字符无法显示,则回退使用竖线符号 "|"。fill-column-indicator- 指定用于显示填充列标记的文本外观。该外观默认继承
shadow外观的属性,但不包含背景色;若要修改填充列标记的颜色,仅需设置该外观的前景色即可。
在图形化显示界面中,Emacs 还可在 窗口边缘 区域标记缓冲区的边界。启用该功能后,缓冲区的首行和末行会在边缘区域显示角度图标,同时可搭配上下箭头图标,标识当前窗口是否可进行滚动操作。
缓冲区局部变量 indicate-buffer-boundaries ,用于控制缓冲区边界和窗口滚动状态在边缘区域的显示方式:
- 若变量值设为
left或right,则角度图标和箭头图标会一同显示在左侧边缘或右侧边缘区域; - 若变量值设为 关联列表 (参见《Emacs Lisp 参考手册》中的关联列表相关内容),列表中的每个元素均为 (标记类型。显示位置) 的形式,用于指定单个标记的显示位置。其中,标记类型可为 top(顶部)、bottom(底部)、up(向上)、down(向下),或特殊值 t(为列表中未指定的标记设置默认显示位置);显示位置可为 left(左侧)、right(右侧),或 nil(不显示该标记)。
例如,设置值为 ((top . left) (t . right)) ,表示将顶部角度图标显示在左侧边缘,底部角度图标和上下箭头图标均显示在右侧边缘;若仅需在左侧边缘显示角度图标、不显示箭头图标,可将变量值设为 ((top . left) (bottom . left)) 。
16.17. 无用空白字符(Useless Whitespace)
编辑时很容易在 行尾 无意间留下多余的空格,或在 缓冲区末尾 留下空行。多数情况下,这些尾随空白字符不会产生实际影响,但有时会造成不必要的麻烦。
将缓冲区局部变量 show-trailing-whitespace 设为 t,即可让行尾的尾随空白字符变为可见状态,Emacs 会通过 trailing-whitespace 文本外观对其进行标记显示。
该功能在 光标位于含空白字符的行尾 时会暂时失效。严格来说,此时的空白字符仍属于尾随空白,但在输入新文本的过程中,对其进行特殊标记会影响视觉体验,而光标所处的位置本身就足以提示用户此处存在空白字符。
执行命令 M-x delete-trailing-whitespace 可删除所有尾随空白字符,该命令会清除缓冲区中 每行行尾 的所有多余空格,以及 缓冲区末尾 的所有空行;若要保留缓冲区末尾的空行,可将变量 delete-trailing-lines 设为 nil 。若当前区域处于激活状态,该命令仅会删除该区域内每行行尾的多余空格。
在图形化显示界面中,Emacs 可在 左侧窗口边缘 通过小图标标记出窗口末尾的未使用行(参见窗口边缘相关内容)。该图标会显示在所有不对应缓冲区文本的屏幕行处,因此缓冲区末尾的空行会因 缺少该图标 而变得醒目。启用该功能需将缓冲区局部变量 indicate-empty-lines 设为非 nil 值;若要为所有新建缓冲区启用该功能,可设置该变量的默认值,例如添加配置 (setq-default indicate-empty-lines t) 。
空白字符模式 是一款缓冲区局部次要模式,可通过 特殊文本外观标记或特殊符号替代显示 的方式,让缓冲区中的各类空白字符可视化。执行 M-x whitespace-mode 可切换该模式的开启与关闭。需可视化的空白字符类型由列表变量 whitespace-style 决定,执行 M-x whitespace-toggle-options 可在当前缓冲区中单独开启或关闭该列表中的任意配置项。以下是该变量的部分可选配置项(完整列表参见该变量的官方文档):
face- 启用所有基于特殊文本外观的空白字符可视化功能。该配置项具有特殊含义:若列表中缺少此项,则除space-mark、tab-mark和newline-mark外,其余所有可视化功能均失效。
trailing- 高亮显示行尾的尾随空白字符。
tabs- 高亮显示制表符。
spaces- 高亮显示普通空格和不换行空格字符。
lines- 高亮显示超过 80 列的长行,可通过自定义变量
whitespace-line-column修改列数限制。 newline- 高亮显示换行符。
missing-newline-at-eof- 若缓冲区末尾未以换行符结尾,则高亮显示最后一个字符。
empty- 高亮显示缓冲区开头和 / 或末尾的空行。
big-indent- 高亮显示过深的缩进。默认情况下,连续至少 4 个制表符或 32 个普通空格组成的缩进会被高亮,可通过自定义正则表达式
whitespace-big-indent-regexp修改该判定规则。 space-mark- 使用特殊符号替代显示普通空格和不换行空格。
tab-mark- 使用特殊符号替代显示制表符。
newline-mark- 使用特殊符号替代显示换行符。
全局空白字符模式 是一款全局次要模式,可让所有缓冲区中的空白字符实现可视化,执行 M-x global-whitespace-toggle-options 可单独切换该模式下的各项功能。
16.18. 选择性显示(Selective Display)
Emacs 支持隐藏 缩进量超过指定列数 的行,你可利用该功能快速概览程序的部分内容结构。
在当前缓冲区中,带数字参数 n 执行快捷键 C-x $ (set-selective-display) ,即可让缩进量不少于 n 列的行从屏幕中隐藏。若某条可见行后紧跟一行或多行隐藏行,该行末尾会显示三个点(…),作为隐藏内容的标识。
快捷键 C-n 和 C-p 在跳转行时,会直接跳过隐藏的行,如同这些行不存在一般。
被隐藏的行仍会保留在缓冲区中,且绝大多数编辑命令会照常识别这些行,因此光标位置可能会出现在隐藏文本的中间。出现这种情况时,光标会显示在 前一可见行的末尾、三个点之后 ;若光标位于可见行的末尾(换行符之前),则会显示在 三个点之前 。
不带任何参数执行 C-x $ ,即可恢复显示所有被隐藏的行。
若将变量 selective-display-ellipses 设为 nil ,那么紧跟隐藏行的可见行末尾将 不再显示三个点 ,此时缓冲区中无任何视觉标识提示隐藏行的存在。该变量被设置后会自动成为缓冲区局部变量。
你也可使用 《大纲模式》 实现缓冲区文本的部分隐藏,这是另一种文本隐藏的实现方式。
16.19. 模式行可选功能
模式行中的缓冲区百分比 pos 表示窗口顶部之上的内容占整个缓冲区的比例。你可执行命令 M-x size-indication-mode 开启大小指示模式,额外显示缓冲区的字符规模,该数值会紧跟在缓冲区百分比后展示,格式如下:
pos of size
其中 size 是缓冲区字符数的 人性化可读表示 ,会使用缩写单位:k代表 10^3、M代表 10^6、G代表 10^9,依此类推。
开启行号模式后,光标所在的当前行号会显示在模式行中,可通过命令 M-x line-number-mode 开关该模式(默认处于开启状态)。行号会显示在缓冲区百分比 pos 之后,以字母"L"标识行号属性。
同理,执行 M-x column-number-mode 开启列号模式,即可在模式行显示光标当前的列号,列号以字母 'C' 标识。若同时开启行号和列号模式,行号和列号会以 括号包裹 的形式显示(行号在前、列号在后),而不是分别标注 'L'和 'C',例如显示为:'(561,2)'。有关次要模式的详细介绍及上述命令的使用方法,参见「次要模式」相关内容。
列号模式下,显示的列号以窗口左边界为0 起始位计数;若希望列号从 1 开始计数,可将变量 column-number-indicator-zero-based 设为 nil 。
若你对缓冲区进行了窄化操作(参见「窄化」相关内容),模式行显示的行号为相对窄化后可访问区域的行号,因此该数值不能作为 goto-line 命令的参数( what-line 命令可显示相对于整个文件的绝对行号)。你可使用 goto-line-relative 命令,将光标移至窄化后缓冲区可访问区域的指定相对行号位置。
若缓冲区体积过大(字符数超过变量 line-number-display-limit 的取值),Emacs 将不会计算行号(因计算过程会大幅卡顿),模式行也不会显示行号;若要取消该限制,将 line-number-display-limit 设为 nil 即可。
若缓冲区中的行过长,行号计算也会变慢。因此,当光标附近行的平均字符宽度超过变量 line-number-display-limit-width 的取值时,Emacs 同样不会显示行号,该变量的默认值为 200 个字符。
Emacs 支持在所有窗口的模式行中可选显示系统时间和负载,执行命令 M-x display-time 或自定义选项 display-time-mode 即可启用该功能。模式行中新增的相关信息格式如下:
hh:mmPM l.ll
其中 hh 为小时、 mm 为分钟,其后始终跟随 AM (上午)或 PM (下午); l.ll 为系统最近几分钟的平均负载值,即系统中处于运行或就绪状态(等待可用处理器)的进程数量(若操作系统不支持,部分字段可能不会显示)。若偏好 24 小时制时间显示,将变量 display-time-24hr-format 设为 t 即可。
若你存在未读邮件,模式行的系统负载值后会显示单词'Mail'。在图形化显示界面中,可通过自定义变量 display-time-use-mail-icon ,使用图标替代Mail文字,节省模式行的显示空间;也可自定义 display-time-mail-face 文本外观,让邮件提示标识更醒目。你可通过 display-time-mail-file 指定待检查的邮件文件,或设置 display-time-mail-directory 指定收件目录(该目录下所有非空的普通文件均会被视为新收到的邮件)。
在笔记本电脑上运行 Emacs 时,执行命令 display-battery-mode 或自定义变量 display-battery-mode ,即可在模式行中 显示电池电量 。变量 battery-mode-line-format 决定了电池电量的显示格式,具体的模式行提示信息因操作系统而异,通常会以百分比形式展示当前电池电量占总电量的比例。变量 battery-update-functions中 的函数会在模式行更新后运行,可基于电池状态触发自定义操作。
在图形化显示界面中,模式行默认以 3D 方框样式绘制;若你不喜欢该效果,可通过自定义 mode-line 文本外观,将其 box 属性设为 nil 来关闭 3D 效果,详情参见「自定义文本外观」相关内容。
默认情况下,未选中窗口的模式行会使用 mode-line-inactive 外观显示,仅选中窗口的模式行使用 mode-line 外观,该设计可清晰区分当前选中的窗口。当迷你缓冲区被选中时,由于其无模式行,触发迷你缓冲区的原窗口会继续使用 mode-line 外观显示模式行,因此常规的迷你缓冲区操作不会改变任何窗口的模式行显示样式。
若要关闭 mode-line-inactive 外观的使用,将变量 mode-line-in-non-selected-windows 设为 nil 即可,此后所有窗口的模式行均会使用 mode-line 外观显示。
你可通过自定义变量 eol-mnemonic-unix 、 eol-mnemonic-dos 、 eol-mnemonic-mac 和 eol-mnemonic-undecided ,为每种换行格式设置自定义的字符串,实现模式行中换行格式标识的个性化显示。
16.20. 文本显示方式
绝大多数字符为 printing character可打印字符 :这类字符出现在缓冲区中时,会直接按字面形式显示在屏幕上。可打印字符包括 ASCII 数字、字母、标点符号,以及众多非 ASCII 字符。
ASCII 字符集中包含 非打印control characters控制字符 ,其中有两个字符会以特殊形式显示:换行符(Unicode 编码点 U+000A)的显示方式为另起一行,制表符(U+0009)则会显示为连续空格,直至下一个制表位(默认每 8 列一个制表位)。每个制表符对应的空格数由缓冲区局部变量 tab-width 控制,该变量的取值必须是 1 到 1000 之间的整数(包含边界值)。注意,缓冲区中制表符的显示方式,与制表键( TAB )作为命令的定义无任何关联。
其余 ASCII 控制字符(编码小于 U+0020,即八进制 40、十进制 32),会以脱字符 '^' 后跟对应非控制形式字符的样式显示,并应用 escape-glyph 文本外观。例如,'contraol-A' 控制字符 A(U+0001)会显示为'^A'。
编码为 U+0080(八进制 200)至 U+009F(八进制 237)的原始字节,会以八进制转义序列的形式显示,同样应用escape-glyph外观。例如,编码 U+0098(八进制 230)会显示为'\230'。若将缓冲区局部变量 ctl-arrow 设为 nil ,所有 ASCII 控制字符都会改为以八进制转义序列显示,而非脱字符转义序列(你也可设置让原始字节以十六进制形式显示,参见变量display-raw-bytes-as-hex)。
部分非 ASCII 字符的视觉外观,与 ASCII 空格或连字符(减号)完全相同。若这类字符在你不知情的情况下被插入缓冲区(例如通过粘贴操作),可能会引发问题 —— 比如源代码编译器通常不会将非 ASCII 空格识别为空白字符。为解决该问题,Emacs 会对这类字符做特殊显示处理:U+00A0 不换行空格,以及 Unicode 水平空格类的其他字符,会应用 nobreak-space 外观显示;U+00AD 软连字符、U+2010 连字符、U+2011 非断连字符,会应用 nobreak-hyphen 外观显示。若要关闭该特殊显示功能,将变量 nobreak-char-display 设为 nil 即可。若该变量设为非 nil 且非 t 的取值,Emacs 会将这类字符显示为 高亮反斜杠 后跟普通空格或连字符的形式。
你可通过显示表自定义任意特定编码字符的显示方式,详情参见《Emacs Lisp 参考手册》中的「显示表」章节。
在图形化显示界面中,部分字符可能在 Emacs 可用的所有字体中均无对应字形。这类 glyphless characters无字形字符 默认会显示为包含其十六进制编码的方框。同理,在文本终端中,无法通过终端编码显示的字符(参见「终端输入输出的编码系统」),默认会显示为问号。你可通过自定义变量 glyphless-char-display-control ,控制无字形字符的显示方式;也可自定义 glyphless-char 文本外观,让这类字符在屏幕上更醒目。具体细节参见《Emacs Lisp 参考手册》中的「无字形字符的显示」章节。
启用 glyphless-display-mode 次要模式,可切换当前缓冲区中无字形字符的显示样式 —— 切换后,无字形字符会显示为方框,内部标注其字符名称的缩写。
Emacs 会自动检测当前显示设备是否支持显示弯引号 ( ‘ 和 ’ ) 。默认情况下,若检测到设备支持,Emacs 会将消息和帮助文本中的 ASCII 引号 (‘ ` ’ 和 ‘ ' ’) 自动转换为这类弯引号。你可通过自定义用户选项 text-quoting-style ,调整或禁止该转换行为,详情参见《Emacs Lisp 参考手册》中的「文档中的按键」章节。
若检测到弯引号 ‘ 、 ’ 、 “ 和 ” 的显示效果与 ASCII 字符完全一致,这些弯引号会应用 homoglyph 外观显示;若检测到设备不支持显示某类弯引号,该弯引号会替换为对应的 ASCII 近似字符 ` 、 ' 、 和 " ,并同样应用 homoglyph 外观。
16.21. 光标显示(Displaying the Cursor)
在文本终端中,光标的显示样式由终端本身控制,Emacs 基本无法干预。部分终端提供两种光标样式可选:常规可见的静态光标,以及高亮显示的闪烁光标。Emacs 默认使用高亮 闪烁光标 ,并在启动或恢复 Emacs 时自动切换至该样式;若 Emacs 启动或恢复时,变量 visible-cursor 的值为 nil ,则会使用常规静态光标。
在图形化显示界面中,文本光标的多项属性均可自定义。若要修改光标颜色,可调整cursor文本外观的: background (背景色)属性(参见自定义文本外观相关内容)。(该外观的其他属性均无实际效果,光标覆盖区域的文本会使用框架的背景色绘制。)若要修改光标形状,可自定义缓冲区局部变量 cursor-type ,其可选值包括:
box:方块光标(默认样式)(box . size):方块光标(当遮罩图像的宽或高超过 size 像素时,光标变为空心方块)hollow:空心方块光标bar:竖线光标(bar . n):宽度为 n 像素的竖线光标hbar:横线光标(hbar . n):高度为 n 像素的横线光标nil:隐藏光标
默认情况下,若 Emacs 在 10 次光标闪烁期间未接收到任何输入,光标会停止闪烁;任意输入事件都会重置闪烁计数。你可通过自定义变量 blink-cursor-blinks 控制闪烁次数,该变量的取值为无输入时的最大闪烁次数;将其设为 0 或负数,光标会一直闪烁。若要彻底关闭光标闪烁,可将变量 blink-cursor-mode 设为 nil(参见简易自定义界面),或在初始化文件中添加以下配置行:
(blink-cursor-mode 0)
此外,你也可通过自定义列表变量 blink-cursor-alist ,修改光标熄灭时的显示样式。该列表的每个元素均为 (on-type . off-type) 的形式,含义为:若光标闪烁亮起时为 on-type 亮灯样式(可为上述任意光标类型),则熄灭时显示为 off-type 熄灯样式。
部分字符(如制表符)为 宽字符 ,光标默认定位在这类字符上时,仍会以默认字符宽度绘制。若要让光标自动拉伸以覆盖整个宽字符,可将变量 x-stretch-cursor 设为非 nil 值。
默认情况下, 未选中窗口 中的光标会显示为不闪烁的空心方块(若为竖线光标,则显示为更细的竖线)。若要隐藏未选中窗口中的光标,可将变量 cursor-in-non-selected-windows 设为 nil 。
若要让光标辨识度更高,你可启用行高亮模式(HL Line mode)—— 这是一款次要模式,会高亮显示光标所在的整行文本。执行 M-x hl-line-mode 可在当前缓冲区中开关该模式,执行 M-x global-hl-line-mode 可全局开关该模式。
16.22. 行截断(Line Truncation)
作为折行显示的替代方案(参见折行相关内容),Emacs 可通过 truncation行截断 方式显示长行。行截断指的是,超出屏幕或窗口宽度的字符会被直接隐藏,不再显示。在图形化显示界面中,窗口边缘会显示一个小直箭头,标记行的左侧或右侧存在截断内容;在文本终端中,会在最右侧或最左侧列显示 ‘$’ 符号作为截断标识。
水平滚动会自动触发行截断(参见水平滚动相关内容)。你也可通过快捷键 C-x x t (toggle-truncate-lines) ,为特定缓冲区手动启用行截断功能,该命令的实现原理是修改缓冲区局部变量 truncate-lines 的取值:当该变量为非 nil 值时,长行将被截断显示;当该变量为 nil 值时,长行将折行显示为多个屏幕行。无论通过何种方式设置 truncate-lines ,该变量都会自动成为当前缓冲区的局部变量;在未手动设置前,该变量会使用默认值 nil (即折行显示)。
由于行截断与自动换行(下一节将介绍)功能互斥,执行 toggle-truncate-lines 命令启用行截断时,会同时禁用自动换行功能。
当拆分后的窗口宽度过窄时,Emacs 可能会自动启用行截断。相关控制变量为 truncate-partial-width-windows ,详情参见窗口拆分相关内容。
16.23. 视觉行模式(Visual Line Mode)
作为普通折行显示的另一种替代方案(参见折行相关内容),Emacs 支持使用 word wrap自动换行 功能。开启该功能后,较长的逻辑行会被拆分为两个或多个screen lines屏幕行(也称作「visual lines视觉行」),这一点与普通折行显示类似;不同的是,Emacs 会尝试 在窗口右边缘附近的单词边界处进行换行 (若文本显示方向为从右到左,则在窗口左边缘的单词边界处换行)。这种换行方式不会在单词中间拆分文本,能让内容更易阅读。
自动换行功能由视觉行模式开启,该模式是一款可选的次要模式。在当前缓冲区中执行命令 M-x visual-line-mode 即可开启视觉行模式,重复执行该命令则关闭此模式;也可通过菜单栏操作:在「Options选项」菜单中选择「'Line Wrapping in this Buffer'本缓冲区的换行方式」子菜单,再点击「'Word Wrap (Visual Line mode)'自动换行(视觉行模式)」选项。视觉行模式开启后,模式行的模式显示区域会出现字符串wrap作为标识。执行命令 M-x global-visual-line-mode ,可对所有缓冲区全局切换视觉行模式的开启与关闭。
由于自动换行与上一节介绍的行截断功能互斥,开启视觉行模式的同时会自动禁用行截断功能。
在视觉行模式下,部分编辑命令的操作对象会从 logical lines逻辑行 变为 screen lines视觉行 :
C-a(beginning-of-visual-line):将光标移至当前视觉行的行首C-e(end-of-visual-line):将光标移至当前视觉行的行尾C-k(kill-visual-line):删除光标至当前视觉行尾的文本
若要按 逻辑行 进行光标移动,可使用命令 M-x next-logical-line 和 M-x previous-logical-line ,二者分别将光标移至下一个和上一个逻辑行,且操作效果不受视觉行模式开启状态的影响。若你频繁使用这两个命令,可为其绑定快捷键,具体方法参见「在初始化文件中重新绑定按键」相关内容。
默认情况下,自动换行产生的视觉行 不会在窗口边缘显示标识 。视觉行模式常被用于编辑包含大量长逻辑行的文件,若为每个折行的视觉行都添加边缘标识,会造成视觉干扰;你可通过自定义变量 visual-line-fringe-indicators ,修改这一默认设置。
Emacs 默认仅在空格、制表符这类空白字符后进行换行,不会在「全角空格」等空白字符后折行。Emacs 提供了 word-wrap-whitespace-mode 次要模式,开启后可在当前模式下启用自定义规则的自动换行,并通过用户选项 word-wrap-whitespace-characters 设置允许触发换行的字符;该模式也提供全局版本 global-word-wrap-whitespace-mode ,可对所有缓冲区生效。
当文本中混合了中日韩(CJK)字符与拉丁字符时,仅在空白字符后换行会导致排版效果异常(原因是中日韩字符无需通过空白字符分隔单词)。此时可自定义选项 word-wrap-by-category ,让 Emacs 允许在所有属于 '|' 字符类的字符后换行(参见《Emacs Lisp 参考手册》中的「字符类」章节),该设置能更好地支持中日韩字符的排版。此外,若通过 Emacs 的自定义界面设置该变量,程序会自动加载 kinsoku.el 文件;该文件加载后,Emacs 会 遵循禁则排版规则 进行换行,即属于 '>' 字符类的字符(如 U+FF0C 全角逗号)不会出现在行首,属于 '<' 字符类的字符(如 U+300A 左双角括号)不会出现在行尾。
你可通过命令 char-category-set 和 category-set-mnemonics 查看某个字符所属的字符类;也可将光标移至目标字符处,执行 C-u C-x = ,在弹出的信息报告中查看「category(字符类)」部分的内容。若要为字符添加字符类属性,可使用命令 modify-category-entry 。
16.24. 显示定制(Customization of Display)
本节介绍用于控制 Emacs 界面各类外观细节的变量,初级用户可跳过本节内容。
若希望 Emacs 为缓冲区中的每一行显示行号,可自定义缓冲区局部变量 display-line-numbers (该变量默认值为 nil )。其支持多种取值,对应不同的行号显示模式:
t- 在每一行显示缓冲区文本的 non-continuation screen line非折行屏幕行 前,显示 绝对行号 。若为折行屏幕行,或整行屏幕行仅显示展示字符串 / 覆盖字符串,则不会为该行编号。
relative- 在显示缓冲区文本的非折行屏幕行前,显示 相对行号 。行号以光标所在行为基准,距离当前行越远,行号数值向上下两侧依次递增。
visual- 让 Emacs 按 视觉效果统计行号 —— 仅对实际显示在界面上的行计数(忽略文本隐藏部分的所有行),折行后占用多个屏幕行的逻辑行,会为每个屏幕行依次编号。显示的行号为相对行号,规则与上述
relative取值一致。该模式适用于大纲模式等支持文本折叠的模式(参见大纲模式),也适合需要按屏幕行精确移动光标的场景。 其他值- 任何非 nil 的其他取值,均按
t模式处理。
执行命令 M-x display-line-numbers-mode ,可便捷开启行号显示功能,该模式也提供全局版本 global-display-line-numbers-mode 。用户选项 display-line-numbers-type 用于指定上述两个模式启用时,采用的具体行号显示子模式。
注意,即便全局开启 display-line-numbers-mode , 迷你缓冲区和工具提示 区域也不会显示行号。
当 Emacs 显示相对行号时,可自定义光标所在行的行号显示规则。默认情况下,即便其他行均为相对行号,光标所在行仍会显示 绝对行号 。若将变量 display-line-numbers-current-absolute 设为 nil ,光标所在行的显示行号会变为0。若你无需关注当前行的绝对行号,且希望为大缓冲区的文本预留更多水平显示空间,该设置会非常实用。
在经过窄化的缓冲区中(参见窄化),行号默认从窄化区域的起始位置开始计数。若将变量 display-line-numbers-widen 设为非 nil 值,行号会忽略所有窄化操作,从缓冲区的第一个字符开始计数。
若变量 display-line-numbers-offset 的取值非 0,该数值会被叠加到每个绝对行号上,且行号始终从缓冲区起始位置开始计数(效果等同于 display-line-numbers-widen 设为非 nil)。若该变量设为 0,或行号非绝对行号模式,则此变量不产生任何效果。
在选择性显示模式(参见选择性显示)及其他会隐藏大量行的模式中(如大纲模式、Org 模式),你可自定义变量 display-line-numbers-width-start 和 display-line-numbers-grow-only ,或将 display-line-numbers-width 设为足够大的数值,避免行号预留显示区域出现偶尔的计算错误。
行号会通过专用的 line-number 文本外观显示,光标所在行的行号则使用 line-number-current-line 外观显示,你可通过该设置让当前行号呈现独特样式,便于快速定位光标所在行。此外,还可使用 line-number-major-tick 和 line-number-minor-tick 外观,高亮显示行号为特定数字倍数的行,分别自定义变量 display-line-numbers-major-tick 和 display-line-numbers-minor-tick 即可设置对应的倍数。
若将变量 visible-bell 设为非 nil 值,当 Emacs 原本需要发出提示音时,会改为让整个屏幕闪烁。若你的终端不支持屏幕闪烁功能,该变量将无效。
变量 echo-keystrokes 用于控制多字符按键的回显功能,其取值为触发回显所需的暂停秒数;若设为 0,则表示完全禁用回显。该取值仅在存在可回显内容时生效(参见回显区)。
若变量 echo-keystrokes-help 设为非 nil 值(默认值),根据 echo-keystrokes 规则回显的多字符按键序列,会附带一段简短的帮助文本 —— 按下对应按键可调用 describe-prefix-bindings 命令(参见其他帮助命令),查看已输入前缀对应的所有命令列表。相关的帮助功能可参见 which-key。
在图形化显示界面中,当 Emacs 处于忙碌状态时,鼠标指针会变为沙漏样式。若要禁用该功能,将变量 display-hourglass 设为 nil 即可。变量 hourglass-delay 用于设置 Emacs 忙碌多久后显示沙漏指针,单位为秒,默认值为 1。
当鼠标指针位于 Emacs 框架内时,每次输入字符插入文本时,Emacs 会将鼠标指针隐藏,避免其遮挡文本(准确来说,仅在输入自插入字符时会隐藏指针,参见插入文本)。移动鼠标指针后,其会重新显示。若要禁用该功能,将变量 make-pointer-invisible 设为 nil 即可。
在图形化显示界面中,变量 underline-minimum-offset 用于设置带下划线文本的基线与下划线之间的最小距离,单位为像素,默认值为 1。增大该数值,可提升部分字体下带下划线文本的可读性(但 Emacs 绝不会将下划线绘制到当前行区域之外)。变量 x-underline-at-descent-line 用于控制下划线的绘制位置,默认值为 nil ,表示在字体的基线位置绘制;若设为 t ,则在字体的下沿线位置绘制。(若为带下划线的文本设置了非默认的行间距,参见《Emacs Lisp 参考手册》中的行高章节,Emacs 会将下划线绘制到额外行间距的下方。)
变量 overline-margin 用于设置文本上方上划线的垂直位置(包含上划线自身的高度),单位为像素,默认值为 2。
在部分文本终端中,粗体与反显模式同时使用时,会导致文本难以辨认。调用函数 tty-suppress-bold-inverse-default-colors 并传入非 nil 参数,可在该场景下屏蔽粗体的显示效果。
原始字节默认以八进制格式显示,例如十进制值为 128 的字节会显示为 \200 。若要改为十六进制格式显示(如 \x80 ),将变量 display-raw-bytes-as-hex 设为 t 即可。从包含 Emacs 会话的终端复制文本,或终端的 escape-glyph 外观与默认外观一致时,解析原始字节需格外注意。例如,Emacs 默认会将十进制值 128 的字节,显示为与字符 \ 、 2 、 0 、 0 完全相同的样式;十六进制显示的问题会更严重 —— 原始字节 128 后跟字符 7 会显示为 \x807 ,而 Emacs Lisp 会将其解析为单个字符 U+0807 撒玛利亚字母 IT;对应的八进制显示 \2007 则不会出现该混淆问题,因为八进制转义序列最多包含三位数字。
17. 搜索与替换
与其他编辑器相同,Emacs 提供了字符串查找命令,也支持将指定字符串替换为其他字符串;同时还提供了同类功能的命令,可基于匹配模式而非固定字符串进行查找和替换。
你也可通过 xref 工具在多个文件中执行搜索替换操作(参见《通过标识符进行搜索与替换》),或使用 Dired 中的 A 命令(参见《文件操作》),亦可调用 grep 程序完成该操作(参见《在 Emacs 中使用 Grep 搜索》)。
17.1. 增量搜索(Incremental Search)
Emacs 中的核心搜索命令为 incremental增量搜索 :键入搜索字符串的第一个字符后,搜索即刻开始。在输入搜索字符串的过程中,Emacs 会实时展示当前已键入内容的匹配位置。当键入的字符足以定位到目标位置时,即可停止输入。根据后续的操作需求,你可选择是否通过回车键显式终止本次搜索。
C-s- 正向增量搜索 (
isearch-forward) 。 C-r- 反向增量搜索 (
isearch-backward) 。
你也可通过菜单栏的 'Edit->Search' 菜单调用增量搜索功能。
17.1.1. 增量搜索基础
C-s- 启动正向增量搜索 (
isearch-forward) 。 C-r- 启动反向增量搜索 (
isearch-backward) 。
按下 C-s (isearch-forward) 会开启正向增量搜索,该命令会读取键盘输入的字符,并将光标移至缓冲区中 下一个 匹配字符组合的末尾位置。
例如,按下 C-s 后再键入字母 F ,光标会跳至起始位置之后缓冲区中第一个 F 的后方;若接着键入字母 O,光标会移至第一个 FO 的后方,该 FO 中的 F 未必是此前找到的第一个 F;再键入一个 O,光标则会移至第一个 FOO 的后方。
搜索过程的每一步,Emacs 都会使用 isearch 文本外观,高亮显示当前匹配内容 —— 即缓冲区中与搜索字符串相匹配的文本(参见文本外观)。有关自定义该高亮效果的各类选项,可参见根据需求自定义搜索行为章节。当前的搜索字符串也会同时显示在回显区中。
若输入搜索字符串时出现错误,按下 DEL (isearch-delete-char) 即可撤销,每按一次 DEL 键,都会取消搜索过程中最后输入的一个内容项。当你键入的命令导致 搜索字符串、光标位置、搜索结果的成败状态、搜索方向、当前搜索结果的另一端位置或搜索的循环匹配状态 发生变化时,Emacs 都会记录一个新的 input item 输入内容项。有关处理搜索失败的更多方法,可参见增量搜索中的错误处理章节。
当光标定位到目标位置后,按下回车键 RET (isearch-exit) 即可结束搜索,光标将停留在搜索定位的位置。此外, 所有在搜索中无特殊含义的命令 ,都会先终止搜索,再执行该命令本身。例如,按下 C-a 会先退出搜索,再将光标移至行首;按下方向键会先退出搜索,再执行对应的光标移动操作,依此类推。仅当你接下来要输入的是可打印字符、 DEL 键、 RET 回车键,或搜索中的其他特殊字符( C-q 、 C-w 、 C-r 、 C-s 、 C-y 、 M-y 、 M-r 、 M-c 、 M-e ,以及下文将介绍的部分字符)时,才需要先按 RET 回车键退出搜索。你也可自定义退出搜索的相关命令,具体参见不退出增量搜索的操作章节。
有一个特殊情况:若 搜索字符串为空 时按下 RET 回车键,会直接启动 非增量搜索 (参见非增量搜索章节)。(该行为可自定义,参见根据需求自定义搜索行为章节。)
若要放弃搜索并返回搜索起始位置,按下 ESC ESC ESC (isearch-cancel) 或 C-g C-g (isearch-abort) 即可。
退出增量搜索时,Emacs 会将光标的原始位置添加到 标记环 中,且不会激活标记;你可通过按下 C-u C-SPC 或 C-x C-x ,回到启动搜索前的光标位置(参见标记环章节)。注意,该功能仅在 标记未被激活 时生效;若启动搜索时标记已处于激活状态,按下 C-u C-SPC 或 C-x C-x 都会直接跳至标记位置。
若要进行反向搜索,可按下 C-r (isearch-backward) 而非 C-s 来启动搜索。反向搜索会查找 起始位置之前 结束的匹配内容,这与正向搜索查找起始位置之后开始的匹配内容的规则相对应。
17.1.2. 重复增量搜索
若你正向搜索 “FOO” 并找到匹配项,却并非目标结果 —— 你要找的 “FOO” 出现在缓冲区更靠后的位置,此时可再次按下 C-s (isearch-repeat-forward)跳至该搜索字符串的下一个匹配项,或按下 C-r (isearch-repeat-backward) 跳至上一个匹配项,这些命令可重复执行任意次数。你也可为 C-s 和 C-r 添加数字前缀参数 n ,直接定位到第 n 个下一个或上一个匹配项。若跳转过度,可按下 DEL 键撤销部分 C-s 的执行结果。同理,在反向增量搜索中,每次按下 C-r (isearch-repeat-backward) 都会重复执行反向搜索。
在增量搜索过程中稍作停顿,Emacs 会高亮显示屏幕上该搜索字符串的 所有其他潜在匹配项 ,帮你预判按下 C-s 或 C-r 重复搜索时可跳转的位置。这些其他匹配项会采用与当前匹配项不同的样式高亮,使用可自定义的 lazy-highlight 文本外观(参见文本外观)。若你不需要该功能,可将变量 isearch-lazy-highlight 设为 nil 来禁用。有关匹配项高亮的其他自定义设置,参见根据需求自定义搜索行为章节。
退出某次搜索后,只需连续按下 C-s C-s ,即可再次搜索相同的字符串。第一个 C-s 用于调用增量搜索功能,第二个 C-s 表示再次搜索上一次的搜索字符串。同理,连续按下 C-r C-r 会反向搜索上一次的搜索字符串。判定上一次的搜索字符串时,无论该字符串此前是通过 C-s 还是 C-r 搜索的,均无影响。
若你正在执行正向搜索,却发现目标内容出现在搜索起始位置之前,可按下 C-r 切换为反向搜索,且搜索字符串保持不变。同理,在反向搜索中按下 C-s ,即可切换为正向搜索。
默认情况下,当你切换搜索方向时,首次键入的方向切换命令会让光标停留在原匹配项上,仅移动至该匹配项的另一端。若希望切换方向后直接跳至其他匹配项,可将变量 isearch-repeat-on-direction-change 设为 t 。
若搜索已无匹配结果,你仍按下 C-s 尝试重复搜索,搜索会从 缓冲区起始位置 重新开始;若反向搜索已无匹配结果,按下 C-r 重复搜索,会从 缓冲区末尾位置 重新开始。这种行为被称为 wrapping around循环匹配 ,发生后搜索提示中会显示 “Wrapped”(已循环)。若你继续搜索并越过最初的搜索起始位置,提示会变为 “Overwrapped”(过度循环),表示你正在重新访问已经查看过的匹配项。
你可通过自定义用户选项 isearch-wrap-pause ,控制搜索无更多匹配项时的行为:
- 设为 t(默认值):触发错误提示(再次重复搜索会执行循环匹配);
- 设为 no:发出提示音,且在定位到最后一个匹配项后立即执行循环匹配;
- 设为 no-ding:不发出提示音,直接执行循环匹配; 当取值为 no 或 no-ding 时,键入字符时搜索也会尝试执行循环匹配;
- 设为 nil:永不执行循环匹配,仅在最后一个匹配项处停止搜索。
若要复用此前的搜索字符串,可使用 search ring搜索环 功能。按下 M-p (isearch-ring-retreat)和 M-n (isearch-ring-advance)(下翻搜索环)可遍历搜索环,选择要复用的搜索字符串。这两个命令会将选中的搜索环项显示在迷你缓冲区中,你可对其进行编辑,按下 C-s / C-r 或 RET 回车键即可确认该字符串并开始搜索。搜索环中可保存的最近使用的搜索字符串数量,由变量 search-ring-max 指定,默认值为 16。
若你希望在迷你缓冲区中编辑当前搜索字符串,且 不使用搜索环中的项替换 它,可按下 M-e (isearch-edit-string) ,或在迷你缓冲区中单击鼠标左键。编辑完成后,按下 RET 回车键、 C-s 或 C-r ,即可结束编辑并开始搜索该字符串;按下 C-f 或右方向键,可将搜索起始缓冲区中光标后方的字符添加至当前搜索字符串中。
17.1.3. 增量搜索中的粘贴(Isearch Yanking)
多数情况下,你会希望将光标所在位置或其附近的文本作为搜索字符串,本节介绍的命令可让你便捷地实现这一操作。
C-w (isearch-yank-word-or-char) 会将光标处的下一个字符或单词追加至搜索字符串中,这是查找光标处文本其他匹配项的简便方法(程序会通过启发式规则判断撷取单个字符还是整词)。带数字前缀参数 n 执行该命令,会追加后续 n 个字符或 n 个单词。
C-M-w (isearch-yank-symbol-or-char) 会将光标处的下一个字符或符号追加至搜索字符串中,适用于快速查找光标处符号的其他匹配项(程序会通过启发式规则判断撷取单个字符还是整符号)。带数字前缀参数 n 执行该命令,会追加后续 n 个字符或 n 个符号。
M-s C-e (isearch-yank-line) 会将当前行光标后的剩余内容追加至搜索字符串中;若光标已位于行尾,则追加下一行内容。带数字前缀参数 n 执行该命令,会追加后续 n 行内容。
类似地, C-M-z (isearch-yank-until-char) 会将从光标位置开始,到指定字符下一次出现位置前的所有内容追加至搜索字符串(不包含该指定字符)。该命令在键盘宏中尤为实用,例如在编程语言或标记语言中,可通过指定字符定位语法单元的边界。带数字前缀参数 n 执行该命令,会追加从光标位置到指定字符第 n 次出现位置前的所有内容。
在增量搜索过程中, C-y (isearch-yank-kill) 会将当前剪切板中的内容追加至搜索字符串。若在增量搜索中按下 C-y 后再执行 M-y (isearch-yank-pop) ,会用更早的剪切内容替换此前追加的文本,其作用与常规的 M-y (yank-pop) 命令一致。在回显区 mouse-2 单击鼠标右键,会将当前的 X 窗口选择文本追加至搜索字符串 (isearch-yank-x-selection) ,参见与其他窗口程序的剪切粘贴 。
C-M-d (isearch-del-char) 会删除搜索字符串的最后一个字符, C-M-y (isearch-yank-char) 会将光标后的单个字符追加至搜索字符串。也可通过其他方式添加光标后的字符:按下 M-e 进入迷你缓冲区(参见重复执行增量搜索),在迷你缓冲区的搜索字符串末尾按下 C-f 或 RIGHT 右方向键,每按一次都会将光标后的下一个字符追加至搜索字符串。
默认情况下,若搜索为不区分大小写模式,撷取并追加至搜索字符串的文本会被转换为小写,以保持搜索的大小写无关性(参见大小写折叠)。但如果变量 search-upper-case (参见 search-upper-case)的取值并非 not-yanks ,该小写转换行为会被禁用。
若要将光标附近的文本拉取为初始搜索字符串,启动新的增量搜索,可按下 M-s M-. ,该快捷键执行 isearch-forward-thing-at-point 命令。若区域处于激活状态,命令会将区域内的文本撷取为搜索字符串;若区域未激活,程序会尝试撷取光标附近的网址、符号或表达式。具体撷取的内容类型由用户选项 isearch-forward-thing-at-point 定义。
17.1.4. 增量搜索错误处理
若输入的搜索字符串完全无匹配结果,回显区会显示 “搜索失败(Failing I-Search)”,同时光标会移至 Emacs 能匹配到该字符串最长前缀的位置后方。例如,若你搜索 “FOOT” 但缓冲区中无该内容,光标可能会停在 “FOOL” 里的 “FOO” 后方。回显区中,搜索字符串里无匹配结果的部分,会使用 isearch-fail 文本外观高亮显示。
此时你可执行多种操作:若输入的搜索字符串存在拼写错误,可按 DEL 键撤销上一个输入项(参见增量搜索基础操作),按 C-M-d 键逐个删除字符,或按 M-e 键编辑搜索字符串;若你希望停留在当前匹配到的位置,按 RET 回车键即可;也可按 C-g 键,该操作会从搜索字符串中移除无匹配结果的字符(如 “FOOT” 中的 “T”),仅保留能匹配到的部分(如 “FOOT” 中的 “FOO”)。若此时再次按下 C-g 键,则会彻底取消本次搜索,将光标恢复至搜索启动时的位置。
退出命令 C-g 在搜索过程中会执行特殊操作,具体行为取决于当前的搜索状态:若搜索已匹配到指定内容并处于等待输入状态,按 C-g 键会直接彻底取消搜索,将光标移回搜索启动的初始位置;若按下 C-g 键时,搜索字符串中仍有未匹配到的字符 —— 无论 Emacs 仍在尝试搜索该部分,还是已确认搜索失败 —— 这些未匹配的字符都会从搜索字符串中被移除。移除后搜索会恢复为成功状态并等待后续输入,因此再次按下 C-g 键,才会彻底取消本次搜索。
17.1.5. 增量搜索的特殊输入
除前文介绍的字符外,在增量搜索过程中键入部分其他字符会触发特殊功能,本节将对其逐一说明。
按下 M-s SPC ,可切换宽松空格匹配的开启 / 关闭状态(参见宽松空格匹配)。
按下 M-c 或 M-s c ,可切换搜索的大小写敏感状态(参见大小写折叠)。若搜索字符串中包含大写字母,搜索默认会开启大小写敏感模式。
按下 M-s ' ,可切换搜索是否将相似且等效的字符判定为匹配项(参见字符折叠)。若搜索字符串中包含带重音的字符,本次搜索会自动禁用字符折叠功能。
按下 M-s i (isearch-toggle-invisible) ,可切换搜索是否查找由覆盖属性隐藏的文本(参见大纲模式中的搜索)。若希望所有增量搜索都能查找因文本属性或覆盖属性而隐藏的文本中的匹配项,可将变量 search-invisible 自定义为 t 。
按下 M-r 或 M-s r (isearch-toggle-regexp) ,可在普通增量搜索与正则表达式增量搜索间切换(参见正则表达式搜索)。
按下 M-s _ ,可切换符号模式的开启 / 关闭状态(参见符号搜索)。
若要搜索换行符,可在输入搜索字符串时按下 C-j 。
若要搜索非 ASCII 字符,可在增量搜索过程中使用以下任意一种方法:
- 按下
C-q(isearch-quote-char) ,随后键入非图形字符或一组八进制数字,即可将对应字符添加至搜索字符串,其用法与在缓冲区中使用C-q插入字符一致(参见插入文本)。例如,在增量搜索中按下C-q C-s,会将 'control-S' 控制字符 S 添加至搜索字符串。 使用输入法(参见输入法)。若启动搜索时当前缓冲区已启用某输入法,在输入搜索字符串时,迷你缓冲区会沿用该输入法。输入搜索字符串期间,可按下
C-\(isearch-toggle-input-method) 切换输入法状态;也可按下C-^(isearch-toggle-specified-input-method) ,根据提示选择并启用非默认输入法。当增量搜索中启用输入法时,搜索提示会显示该输入法的助记符,格式如下:I-search [im]:
其中的输入法助记符
im为当前激活输入法的标识。在增量搜索中启用的任意输入法,后续会一直保持在当前缓冲区的启用状态。此外,可按下C-x \(isearch-transient-input-method) 临时启用临时输入法(参见临时输入法),通过该输入法向搜索字符串中插入单个字符,插入完成后输入法会自动禁用。- 按下
C-x 8 RET(isearch-char-by-name) ,随后键入字符的 Unicode 名称或十六进制编码点,即可将指定字符添加至搜索字符串,其用法与常规的insert-char命令一致(参见插入文本)。
你也可在搜索字符串中包含表情符号序列:按下 C-x 8 e RET (isearch-emoji-by-name) ,随后键入表情符号的 Unicode 名称(例如 smiling face 微笑脸、 heart with arrow 带箭头的心形),即可将指定表情符号添加至搜索字符串。若不清楚目标表情符号的名称,可使用 C-x 8 e l (emoji-list) 和 C-x 8 e d (emoji-describe) 命令查询(参见输入法)。
在增量搜索中按下 M-s o ,会调用 isearch-occur 命令,该命令会以当前搜索字符串为检索条件执行 occur 命令(参见 occur 命令)。
在增量搜索中按下 M-% (isearch-query-replace) ,会调用查询替换或正则表达式查询替换命令(具体取决于当前搜索模式),并将当前搜索字符串设为待替换的目标字符串。若为该命令添加负的数字前缀参数,会执行反向替换(参见查询替换)。按下 C-M-% (isearch-query-replace-regexp) ,会调用正则表达式查询替换命令,并将当前搜索字符串设为待替换的正则表达式。
在增量搜索中按下 M-TAB ,会调用 isearch-complete 命令,该命令会以搜索环(你此前使用过的所有搜索字符串)为补全候选列表,尝试对当前搜索字符串进行补全(参见补全功能)。在许多操作系统中, M-TAB 快捷键会被窗口管理器拦截;若你需要使用该功能,需将 isearch-complete 命令重新绑定至其他快捷键(参见交互式修改按键绑定)。
按下 M-s h r (isearch-highlight-regexp) ,可退出搜索并保持匹配项的高亮状态。该命令会调用 highlight-regexp 命令(参见交互式高亮),将由当前搜索字符串转换而来的正则表达式作为检索条件,并提示你选择用于高亮的文本外观。若希望高亮包含匹配项的整行文本(而非仅高亮匹配项本身),可按下 M-s h l (isearch-highlight-lines-matching-regexp) 。无论使用上述哪种高亮方式,按下 M-s h u (unhighlight-regexp) 即可取消所有高亮效果。
当增量搜索处于激活状态时,按下 C-h C-h (isearch-help-map) 可打开交互式帮助选项,其中包含所有特殊快捷键的列表。这些快捷键均属于按键映射表 isearch-mode-map (参见按键映射表)。
当增量搜索处于激活状态时,按下 M-s M-> 会跳至搜索字符串的最后一个匹配项,按下 M-s M-< 会跳至搜索字符串的第一个匹配项。若为这两个命令添加数字前缀参数 n ,会分别从缓冲区起始位置和末尾位置开始计数,跳至搜索字符串的第 n 个匹配项。
17.1.6. 不退出增量搜索
本节介绍如何控制在键入 搜索中无特定含义 的命令时,是否先退出搜索再执行该命令;同时还将说明三类特殊命令,键入这类命令时无需退出当前增量搜索,尽管其本身并非增量搜索的内置命令。
默认情况下,若键入的命令未被增量搜索绑定相关功能,Emacs 会 先退出搜索,再执行该命令 ,因此该命令会作用于启动搜索的原缓冲区。但如果将变量 search-exit-option 自定义为 append ,增量搜索无法解析的键入字符会直接 追加至搜索字符串中 。借助该设置,你可将 C-a 等控制字符纳入搜索字符串 —— 这类字符在默认情况下会触发退出搜索,并在缓冲区中执行其绑定的命令。
- 前缀参数
在增量搜索中,当你键入用于指定数字前缀参数的命令时(参见数字参数),该参数默认会作用于搜索的下一个操作,或退出搜索后执行的命令。换言之,输入前缀参数本身不会终止增量搜索。
在早期的 Emacs 版本中,输入前缀参数总会终止搜索。若要恢复该旧版行为,可将变量
isearch-allow-prefix设为nil。当
isearch-allow-scroll为非 nil 值时(见下文),无论isearch-allow-prefix是否为nil,前缀参数均会遵循上述默认行为,即不会终止搜索。- 滚动命令
默认情况下,执行滚动命令会退出增量搜索。但如果将变量
isearch-allow-scroll设为非 nil 值,即可在不退出搜索的前提下,使用滚动条,以及C-v、M-v、C-l等键盘滚动命令(参见滚动)—— 这类命令的scroll-command属性均为非 nil。该特性仅适用于通过 绑定的快捷键 调用这些命令,键入M-x仍会触发退出搜索。你可按常规方式为这些滚动命令添加前缀参数。该特性默认会防止当前匹配项被滚动至视野外;若将
isearch-allow-scroll自定义为特殊值unlimited,则会取消该限制。isearch-allow-scroll特性还会作用于其他部分命令,例如C-x 2(split-window-below) 和C-x ^(enlarge-window)(放大窗口)—— 这类命令并非严格意义上的滚动命令,但会影响文本在屏幕上的显示位置。事实上,所有isearch-scroll属性为非 nil 的命令,都会受该特性影响。因此,你可通过修改这些命令的属性,控制其是否被该特性作用。例如,若希望在后续所有 Emacs 会话的增量搜索中,均可使用
C-h l命令,可先通过C-h c查询该快捷键绑定的命令(参见按键的文档说明),即view-lossage;随后在初始化文件中添加以下配置行(参见Emacs 初始化文件):(put 'view-lossage 'isearch-scroll t)
该特性可应用于所有不会永久修改以下内容的命令:光标位置、缓冲区内容、匹配数据、当前缓冲区,以及选中的窗口和框架。且该命令本身不能尝试执行增量搜索。当
isearch-allow-scroll为nil时(默认值),该特性会被禁用。同理,若将变量
isearch-allow-motion设为非 nil 值,即可在不退出搜索的前提下,使用M-<、M->、C-v、M-v等键盘移动命令,分别跳至缓冲区中当前搜索字符串的第一个匹配项、最后一个匹配项、当前窗口后方的第一个匹配项和当前窗口前方的最后一个匹配项。执行这些移动命令时,搜索方向默认不会改变;若将变量
isearch-motion-changes-direction设为非 nil 值,则会改变搜索方向:执行M-<和C-v后搜索方向为正向,执行M->和M-v后搜索方向为反向。- 移动命令
若将
isearch-yank-on-move自定义为shift,你可在按住shift键的同时键入光标移动命令,以此扩展搜索字符串。该操作会将当前缓冲区中,光标移动后新位置之前的文本撷取并追加至搜索字符串。若将
isearch-yank-on-move设为t,无需按住Shift键,键入光标移动命令即可扩展搜索字符串;但该效果仅适用于符号上带有isearch-move属性的特定移动命令。
17.1.7. 迷你缓冲区搜索
若在迷你缓冲区处于激活状态时启动增量搜索,Emacs 会对迷你缓冲区的内容进行检索。与搜索普通缓冲区不同,搜索字符串不会显示在回显区中,因为该区域会被用于显示迷你缓冲区本身的内容。
若在迷你缓冲区中执行增量搜索未找到匹配结果,Emacs 会尝试 检索迷你缓冲区的历史记录 (参见迷你缓冲区历史记录)。你可将迷你缓冲区及其历史记录视作一系列连续的页面:最早的历史记录项位于第一页,当前的迷你缓冲区内容则在最后一页。正向搜索( C-s )会向后检索后续页面的内容,反向搜索( C-r )则会向前检索更早页面的内容。与搜索普通缓冲区的规则一致,无匹配结果的搜索会执行循环检索,从最后一页跳转至第一页,或从第一页跳转至最后一页。
当当前匹配项出现在某条历史记录项中时,该历史记录项会被调取至迷你缓冲区中。若你以常规方式退出增量搜索(例如按下 RET 回车键),该历史记录项会保留在迷你缓冲区中;若通过按下 C-g 取消搜索,迷你缓冲区的内容会恢复为启动搜索时的状态。
17.2. 非增量搜索(Nonincremental Search)
Emacs 也提供常规的非增量搜索命令,这类命令要求你在搜索开始前输入 完整的搜索字符串 。
C-s RET string RET- 搜索指定字符串。
C-r RET string RET- 反向搜索指定字符串。
启动非增量搜索时,先按下 C-s RET ,此时会调出迷你缓冲区用于输入搜索字符串;输入完成后按 RET 回车键确认,搜索随即执行。若未找到该字符串,搜索命令会触发错误提示。
按下 C-s RET 时, C-s 会照常调用增量搜索功能,而该功能被专门设定为:当你输入的搜索字符串为空时,自动调用非增量搜索命令(空参在增量搜索中并无实际作用)。 C-r RET 的执行逻辑同理,会调用反向非增量搜索命令。
非增量搜索也可通过菜单栏的 'Edit->Search' 菜单调用。
你也可使用两个更简洁的命令: M-x search-forward (正向搜索)和 M-x search-backward (反向搜索)。这两个命令仅对指定的字面字符串进行检索,除了大小写折叠外,不支持任何宽松搜索相关特性(参见搜索中的宽松匹配)。
17.3. 单词搜索(Word Search)
ward search单词搜索 会查找连续的单词序列,且忽略单词之间的标点符号类型。例如,若你输入的搜索字符串是由单个空格分隔的两个单词,该搜索会匹配这两个单词以一个或多个空格、换行符或其他标点符号分隔的任意序列。此功能在搜索文本文档时尤为实用,你无需担心目标单词之间是用换行符还是空格分隔。需注意,编程语言对应的主模式或其他专用模式可修改单词的定义,以适配其语法需求。
M-s w- 若增量搜索已激活,切换单词搜索模式 (
isearch-toggle-word);若未激活,启动正向增量单词搜索 (isearch-forward-word) 。 M-s w RET words RET- 执行正向非增量单词搜索,查找指定单词序列。
M-s w C-r RET words RET- 执行反向非增量单词搜索,查找指定单词序列。
M-s M-w- 在网络上搜索选区中的文本内容。
启动正向增量单词搜索,按下 M-s w 即可。若增量搜索尚未激活,该快捷键会执行 isearch-forward-word 命令;若增量搜索已激活(无论正向还是反向), M-s w 会执行 isearch-toggle-word 命令,切换为单词搜索模式,且保持搜索方向和当前搜索字符串不变。再次按下 M-s w ,可关闭单词搜索模式。
启动非增量单词搜索,按下 M-s w RET 执行正向搜索,按下 M-s w C-r RET 执行反向搜索,二者分别对应 word-search-forward 和 word-search-backward 命令。
增量单词搜索与非增量单词搜索的匹配规则略有不同:非增量单词搜索中,搜索字符串中的每个单词都必须与完整的单词精确匹配;增量单词搜索的匹配规则则更为宽松,在你输入搜索字符串的过程中,其首个和最后一个单词无需匹配完整单词,目的是让匹配过程能随你的输入实时增量进行。这一额外的宽松规则不适用于延迟高亮功能(参见增量搜索),该功能始终会匹配完整的单词。输入搜索字符串时,搜索提示中会显示 “待匹配(Pending)”,直至你按下 C-s 等重复搜索的快捷键。
单词搜索命令不执行字符折叠,切换宽松空格匹配模式(参见宽松空格匹配)对其也无任何效果。
在网络上搜索选区中的文本,按下 M-s M-w 即可。该命令会通过互联网搜索选区中的单词,使用的搜索引擎地址由变量 eww-search-prefix 指定(参见《Emacs 网页浏览器手册》中的 EWW 相关内容)。若选区未激活,或选区中无任何单词,该命令会提示用户输入待搜索的网址或关键词。
你也可通过 RFC 2229 定义的 DICT 协议查询词典服务器,查找单词的释义。Emacs 内置了该协议的客户端,按下 M-x dictionary-search RET ,即可连接至 DICT 词典服务器,查询指定单词的所有可用释义。该命令会提示用户输入待查询单词,默认使用光标所在位置的单词,随后请求词典服务器在一个或多个词典中返回该单词的释义。默认情况下,该命令会先尝试连接本地主机上的 DICT 词典服务器,若连接失败,经用户确认后会尝试连接 dict.org 服务器;可自定义变量 dictionary-server ,将其设为字符串类型的服务器地址,指定唯一使用的词典服务器(若仅需查询本地服务器,设为 “localhost” 即可)。通常, dictionary-search 命令会让服务器在其所有可用词典中检索单词,若为该命令添加数字前缀参数,其会提示用户指定单个词典进行检索。服务器的所有可用词典列表,可通过下方所述的 *Dictionary* 缓冲区中显示的「选择词典(Select dictionary)」按钮查看。
首次使用 dictionary-search 命令时,Emacs 会新建一个 *Dictionary* 缓冲区,并在其中启用专用模式。该缓冲区提供了选择词典、搜索其他单词释义等功能按钮,后续执行的 dictionary-search 命令会复用此缓冲区。若需新建一个同类缓冲区(例如在另一本词典中查询其他单词),按下 M-x dictionary RET 即可。
若在某一缓冲区中启用 dictionary-tooltip-mode 模式,Emacs 会自动查询鼠标指针所在位置单词的释义,并在工具提示中显示。当你阅读包含大量陌生单词的文本时,该功能会非常实用。
关于 dictionary-search 命令的其他配置选项,参见词典自定义组(参见自定义特定项)。
17.4. 符号搜索(Symbol Search)
symbol search符号搜索 与普通搜索极为相似,区别在于 搜索内容的边界必须与符号的边界相匹配 。此处的「symbol符号」含义由主模式决定,通常指源代码中的语法标记,例如 Emacs Lisp 模式中的 Lisp 符号。举例来说,若对 Lisp 符号 forward-word 执行增量符号搜索,该搜索不会匹配 isearch-forward-word 。因此,该功能主要适用于搜索源代码。
M-s _- 若增量搜索已激活,切换符号搜索模式 (
isearch-toggle-symbol) ;若未激活,启动正向增量符号搜 (isearch-forward-symbol) 。 M-s .- 启动正向增量符号搜索,且会将光标附近找到的符号预先加入搜索字符串。
M-s _ RET symbol RET- 执行正向非增量符号搜索,查找指定符号。
M-s _ C-r RET symbol RET- 执行反向非增量符号搜索,查找指定符号。
启动正向增量符号搜索,按下 M-s _ 即可(若待搜索符号就在光标附近,也可按下 M-s . )。若增量搜索尚未激活, M-s _ 会执行 isearch-forward-symbol 命令, M-s . 会执行 isearch-forward-symbol-at-point 命令。为 M-s . 添加数字前缀参数 n 时,该命令会查找光标处符号的第 n 个后续匹配项;若 n 为负值,则执行反向搜索。若增量搜索已激活,按下 M-s _ 会切换至符号搜索模式,同时保留原有的搜索方向和当前搜索字符串;再次按下 M-s _ ,即可关闭符号搜索模式。在增量符号搜索中,输入搜索字符串的过程中, 仅要求搜索字符串的开头与符号的开头相匹配 ,且搜索提示中会显示「待匹配(Pending)」,直至按下 C-s 等重复搜索的快捷键。
启动非增量符号搜索,按下 M-s _ RET 执行正向搜索,按下 M-s _ C-r RET 执行反向搜索。在非增量符号搜索中, 要求搜索字符串的开头与结尾分别与符号的开头和结尾严格匹配 。
符号搜索命令不执行字符折叠,切换宽松空格匹配模式(参见宽松空格匹配)对其也无任何效果。
17.5. 正则表达式搜索(Regular Expression Search)
regular expression 正则表达式(简称regexp)是一种匹配模式,可匹配一类符合规则的备选字符串。Emacs 同时提供了增量和非增量两种正则表达式搜索方式,正则表达式的语法将在下一节中讲解。
C-M-s- 启动正向增量正则表达式搜索 (
isearch-forward-regexp) 。 C-M-r- 启动反向增量正则表达式搜索 (
isearch-backward-regexp)。
启动正向增量正则表达式搜索可按下 C-M-s ,也可给 C-s 添加任意数值的前缀参数后执行,或在正向增量搜索中键入 M-r 切换。该命令与 C-s 的操作方式一致,会增量式读取输入的搜索字符串,但不会将其作为固定字符串去精确匹配缓冲区文本,而是将其视作正则表达式进行匹配。每向搜索字符串中添加字符,正则表达式就会相应延长,Emacs 也会用新的正则表达式重新执行搜索。执行反向增量正则表达式搜索,可按下 C-M-r 、给 C-r 添加前缀参数执行,或在反向增量搜索中键入 M-r 切换。
普通增量搜索中的所有特殊快捷键(参见增量搜索的特殊输入),在增量正则表达式搜索中均有类似功能。例如,启动搜索后立即按下 C-s ,会调取上一次使用的增量正则表达式搜索串并执行正向搜索。增量正则表达式搜索和普通增量搜索拥有相互独立的默认搜索串,且各自配有独立的搜索环,均可通过 M-p 和 M-n 进行遍历。搜索环中可保存的正则表达式最大数量由变量 regexp-search-ring-max 指定,默认值为 16。
与普通增量搜索不同,增量正则表达式搜索 默认不启用lax space宽松空格匹配 ,可按下 M-s SPC (isearch-toggle-lax-whitespace) 切换该功能。启用后,在增量正则表达式搜索中键入的任意空格,均可匹配一个或多个空白字符组成的任意序列。变量 search-whitespace-regexp 用于指定宽松空格匹配所使用的正则表达式,详情参见增量搜索的特殊输入。
此外,增量正则表达式搜索 不支持字符折叠功能 (参见搜索中的宽松匹配)。若在增量正则表达式搜索中按下 M-s ' 尝试切换字符折叠,搜索会自动转为普通字符串搜索,此前输入的匹配模式也会被当作字面量字符串解析。
在某些情况下,为增量正则表达式搜索的匹配串添加字符,可能会导致光标回退并重新执行搜索。例如,当你已搜索过 'foo' ,再为其添加 '\|bar' 后,光标会回退重新检索 —— 这是为了匹配出现在首个foo之前的首个bar(此时搜索提示会显示「待匹配(Pending)」,告知用户程序正在重新计算匹配结果),相关语法详情参见正则表达式语法。
正向和反向正则表达式搜索并非对称操作,因为 Emacs 中的正则表达式匹配始终 正向执行 ,从正则表达式的起始位置开始匹配。因此,正向正则表达式搜索会正向扫描文本,在每个可能的起始位置尝试正向匹配;反向正则表达式搜索会反向扫描文本,但同样在每个可能的起始位置执行正向匹配,这两种搜索方式并非镜像关系。
非增量正则表达式搜索可通过 re-search-forward 和 re-search-backward 命令实现,可通过 M-x 直接调用,也可在增量正则表达式搜索中按下 C-M-s RET 或 C-M-r RET 触发。通过 M-x 直接调用这两个命令时,程序会严格按照你指定的正则表达式执行搜索,因此除大小写折叠外,不支持任何宽松搜索特性(参见搜索中的宽松匹配)。
若为增量正则表达式搜索命令添加前缀参数执行,该命令会切换为普通字符串搜索,功能与 isearch-forward 和 isearch-backward 一致,详情参见增量搜索。
17.6. 正则表达式语法
本节(以及本手册整体)介绍的是用户日常使用的正则表达式特性。更多主要用于 Lisp 程序的扩展特性,参见《Emacs Lisp 参考手册》中的正则表达式相关章节。
正则表达式拥有专属语法:其中少量字符为 special constructs特殊构造符 ,其余均为 ordinary普通字符 。普通字符仅匹配其自身,无其他匹配效果。特殊字符包括 '$^.*+?[\' 仅在作为方括号表达式的结束符 ']' 时具有特殊性(见下文); '-' 仅在方括号表达式内具有特殊性。正则表达式中出现的其他所有字符均为普通字符,除非其前带有反斜杠 '\' 转义(在 Lisp 程序中使用正则表达式时,每个 '\' 都必须写为双反斜杠 '\\' ,参见本节末尾示例)。
例如, 'f'并非特殊字符,因此属于普通字符,正则表达式 'f' 仅匹配字符串 'f' ,不匹配其他任何字符串(也不匹配 'ff')。同理, 'o' 作为正则表达式仅匹配 'o'。(当忽略大小写匹配时,这些正则表达式也会匹配 'F' 和 'O' ,我们将此视为「匹配相同字符串」的扩展规则,而非例外情况。)
任意两个正则表达式 a 和 b 均可进行 拼接 ,拼接后的正则表达式匹配规则为:若字符串的开头部分能匹配 a ,剩余部分能匹配 b ,则该字符串可被拼接后的正则表达式匹配。一个简单的例子是,将 'f' 和 'o' 拼接得到正则表达式 'fo',其仅匹配字符串 'fo' 。若要实现更复杂的匹配效果,就需要使用特殊字符。以下是所有正则表达式特殊字符的说明:
.(句点)- 特殊字符,匹配除换行符外的任意单个字符。例如,正则表达式 ‘a.b’ 匹配以 ‘a’ 开头、以 ‘b’ 结尾的任意三字符字符串。
*本身并非独立构造符,而是后缀操作符,表示将其前面的正则表达式重复匹配任意次数(包括 0 次),且遵循 贪婪匹配原则 (尽可能多匹配)。因此, ‘o*’ 匹配任意数量的 ‘o’,也包括空字符串。
*始终作用于其前方最小的有效正则表达式。因此, ‘fo*’ 表示 ‘o’ 可重复匹配,而非 ‘fo’整体,该表达式可匹配f、fo、foo等字符串。正则表达式匹配器处理
*构造符的逻辑为:首先尽可能多地匹配 ‘*’ 修饰的表达式,再继续匹配模式的剩余部分;若剩余部分匹配失败,则进行 回溯 ,舍弃部分 ’*‘ 的匹配结果,直至模式剩余部分能成功匹配。例如,用 ‘ca*ar’ 匹配字符串 ‘caaar’ 时, ‘a*’ 最初会匹配全部 3 个 ‘a’ ,但模式剩余部分为 ‘ar’ ,而字符串中仅剩 ‘r’ ,因此此次匹配失败;随后进行回溯, ‘a*’ 仅匹配 2 个 ‘a’,此时正则表达式的剩余部分可成功匹配,整体匹配完成。+- 后缀操作符,与 ‘*’ 类似,但要求其前方的表达式至少匹配 1 次。因此, ‘ca+r’ 可匹配 ‘car’ 、 ‘caaaar’,但不匹配 ‘cr’ ;而 ‘ca*r’ 可匹配上述三个字符串。
?- 后缀操作符,与 ‘*’ 类似,但其前方的表达式匹配 0 次或 1 次,二者择一。因此, ‘ca?r’ 仅匹配 ‘car’ 或 ‘cr’ ,无其他匹配结果。
*?、+?、??上述操作符的 non-greedy非贪婪匹配 变体。默认的 ‘*’ 、 ‘+’ 、‘?’ 为贪婪匹配,即在保证整个正则表达式能匹配的前提下,尽可能多匹配字符;后缀添加 ‘?’ 后,变为非贪婪匹配,即在保证整个正则表达式能匹配的前提下, 尽可能少匹配字符 。
例如, ‘ab*’ 和 ‘ab*?’ 均可匹配字符串 ‘a’ 和 ‘abbbb’ ;但用二者匹配文本 ‘abbb’ 时, ‘ab*’ 会匹配整个字符串( 贪婪匹配,最长有效匹配 ),而 ‘ab*?’ 仅匹配 ‘a’ ( 非贪婪匹配,最短有效匹配 )。
非贪婪操作符会在指定起始位置匹配 最短的有效字符串 ;但在正向搜索中,始终会选择 最早出现的有效起始位置 进行匹配。例如,用 ‘a.*?$’ 匹配文本 ‘abbab’ (后接换行符)时,该表达式会匹配整个字符串 —— 因为从第一个 ‘a’ 开始即可完成匹配,匹配器会优先选择该起始位置。
[…]也称作 bracket expression备选字符集,匹配集合中的任意单个字符。
最简单的情况是,方括号之间的所有字符即为该集合的匹配范围。例如, ‘[ad]’ 匹配单个 ‘a’ 或单个 ‘d’; ‘[ad]*’ 匹配由任意数量a和d组成的字符串(包括空字符串)。由此可得, ‘c[ad]*r’ 可匹配 ‘cr’ 、‘car’ 、 ‘cdr’ 、 ‘caddaar’ 等字符串。
在字符集中,可通过 连字符 ‘-’ 连接起始和结束字符,表示字符范围 。例如, ‘[a-z]’ 匹配任意小写 ASCII 字母;字符范围可与单个字符自由混合,如 ‘[a-z\(%.]’ 匹配任意小写 ASCII 字母,或 ‘\)’ 、 ‘%’ 、 ‘.’ 。再如, ‘[α-ωί]’ 匹配所有小写希腊字母。
字符集中还可包含 special character classes特殊字符类 ,以 ‘[:’ 开头、 ‘:]’ 结尾的内容即为字符类,嵌在方括号表达式内使用。例如,
[[:alnum:]]匹配任意字母或数字。字符类的完整列表参见《Emacs Lisp 参考手册》中的字符类相关章节。若要在字符集中包含 ‘]’ ,必须将其设为 第一个字符 ,例如 ‘[]a]’ 匹配 ‘]’ 或 ‘a’ ;若要包含 ‘-’ ,可将其设为字符集的 最后一个字符 ,也可放在开头或字符范围之后,例如 ‘[]-]’ 匹配 ‘]’ 和 ‘-’ 。
若要在字符集中包含 ‘^’ ,可将其放在 除开头外的任意位置 (在开头时,字符集会变为补集 —— 见下文)。
在 忽略大小写 的搜索中使用字符范围时,范围的首尾字符需统一大小写(均大写、均小写),或均为非字母字符。 ‘A-z’ 这类混合大小写的字符范围,其匹配行为未被明确定义,且在未来的 Emacs 版本中可能发生变化。
[^ …]以 ‘[^’ 开头的方括号表达式为 complemented character set补集字符集 ,匹配 除指定字符外的任意单个字符 。例如, ‘
[^a-z0-9A-Z]’ 匹配所有非 ASCII 字母和数字的字符。‘^’ 仅在字符集开头时具有特殊性,其余位置均为普通字符; ‘^’ 后的第一个字符,其 ‘-’ 和 ‘]’ 均无特殊性(即无需转义即可直接表示自身)。
补集字符集 可以匹配换行符 ,除非将换行符明确列入不匹配的字符中。这一点与
grep等程序中的正则表达式处理逻辑不同。^特殊字符,匹配空字符串,但 仅在被匹配文本的行首生效 ,其余位置均匹配失败。因此, ‘
^foo’ 仅匹配出现在行首的 ‘foo’ 。出于历史兼容性考虑, ‘^’ 仅在 正则表达式的开头 ,或 ‘\(’ 、‘\|’ 之后使用时,才具有行首匹配的含义。
$其用法与脱字符 ‘^’ 类似,但仅在行尾位置完成匹配。例如,正则表达式 ‘x+$’ 可匹配行尾由一个或多个 ‘x’ 组成的字符串。
出于历史兼容性考量, ‘$’ 仅在正则表达式的末尾,或在 ‘\)’ 、 ‘\|’ 之前使用时,才具备上述行尾匹配的含义。
\拥有两个功能:转义特殊字符(包括自身 ‘\’ ),以及引入扩展的特殊构造符。
由于 ‘\’ 的转义作用, '
\$' 作为正则表达式仅匹配 '$' , ‘\[’ 仅匹配 ‘[’ ,依此类推。以 ‘\’ 开头的扩展特殊构造符,参见下一节内容。
注意:出于历史兼容性,若特殊字符出现在 其特殊含义无意义的上下文 中,会被当作普通字符处理。例如, ‘*foo’ 中的 ‘*’ 会被视为普通字符,因为其前方无任何可被修饰的表达式。不建议依赖此行为编写正则表达式,无论特殊字符出现在何处,都建议通过转义明确其普通字符的含义。
’\‘ 反斜杠在方括号表达式内无特殊性 ,因此无法取消 ‘-’ 、 ‘^’ 、 ‘]’ 的特殊含义。当这些字符无特殊含义时,无需为其添加反斜杠——这一操作无任何实际意义;而当它们具有特殊含义时,反斜杠可合法地加在其前方,例如 ‘[^\]’ (在 Lisp 字符串语法中需写为 "[^\\]"),该表达式匹配除反斜杠外的任意单个字符。
17.7. 正则表达式中的反斜杠
在绝大多数情况下,反斜杠 '\' 后接任意字符,仅会匹配该字符本身。但存在若干例外情况:以 '\' 开头的双字符序列拥有特殊含义,而该序列的第二个字符单独使用时,始终为普通字符。以下是所有以 '\' 构成的特殊构造符说明:
\|表示匹配备选项。在两个正则表达式
a和b之间加入 '\|' ,构成的新表达式可匹配任意能被a或b匹配的文本。其匹配逻辑为:先尝试用a匹配,若匹配失败,再尝试用b匹配。例如, '
foo\|bar' 仅匹配 'foo' 或 'bar' ,不匹配其他任何字符串。'
\|' 的作用范围为 其两侧最大的有效正则表达式 ,仅能通过圆括号组 '\( … \)' 限定其作用范围。正则表达式的完整回溯机制可处理 '
\|' 的多次嵌套使用场景。\( … \)表示分组构造符,主要有三项作用:
- 为其他操作符包裹一组由 '
\|' 分隔的备选项。例如, '\(foo\|bar\)x' 可匹配 'foox' 或 'barx' ; - 为后缀操作符 '
*' 、 '+' 、 '?' 包裹复杂的表达式,使其成为操作符的作用对象。例如, 'ba\(na\)*' 可匹配 'banana' 、 'bananana' 等包含任意数量(0 个及以上) 'na' 的字符串; - 记录匹配到的子串,供后续引用使用。
上述第三项作用并非括号分组的固有属性,而是为同一 '
\( … \)' 构造符赋予的另一重独立功能。实际使用中,这两种含义通常不会产生冲突;若出现冲突,可使用下文介绍的匿名分组解决。- 为其他操作符包裹一组由 '
\(?: … \)- 表示 shy group匿名分组 ,该分组不会记录匹配到的子串,也无法通过后文的 '
\d' 语法引用(见下文)。此构造符在正则表达式的机械拼接场景中十分实用,可仅为语法需求添加分组,而不会干扰需要被引用的分组的编号顺序。 \d表示反向引用,匹配第
d个 '\( … \)' 分组此前匹配到的完全相同的文本。当 '
\( … \)' 分组完成匹配后,正则表达式匹配器会记录该分组匹配到的文本的起始和结束位置。在正则表达式的后续位置,使用 '\' 后接数字d,即可表示「匹配第d个 '\( … \)' 分组匹配到的文本」。正则表达式中出现的前 9 个 '
\( … \)' 分组,会按照左括号在表达式中出现的先后顺序,被依次分配编号1至9。因此,可使用 '\1' 至 '\9' 引用对应分组匹配到的文本。例如, '
\(.*\)\1' 可匹配任意无换行符、且由两个完全相同的子串拼接而成的字符串。其中 '\(.*\)' 会匹配前半部分(可为任意内容),而后续的 '\1' 则必须匹配与前半部分完全一致的文本。若某个 '
\( … \)' 分组因后接 '*' 等操作符而完成多次匹配,匹配器 仅会记录最后一次的匹配结果 。\{m\}- 作为后缀操作符,表示 精确匹配 m 次 ,即其前方的正则表达式必须连续匹配恰好
m次。例如, 'x\{4\}'仅匹配字符串 'xxxx' ,不匹配其他任何内容。 \{m,n\}- 作为后缀操作符,表示 匹配 m 至 n 次 ,即其前方的正则表达式必须连续匹配至少
m次、至多n次。若省略n,则表示无上限,仅要求前方表达式匹配至少m次。- '
\{0,1\}' 与 '?' 等价; - '
\{0,\}' 与 '*' 等价; - '
\{1,\}' 与 '+' 等价。
- '
\`- 匹配空字符串,且仅在被匹配的字符串 或 缓冲区(或其可访问部分)的起始位置生效。
\'- 匹配空字符串,且仅在被匹配的字符串 或 缓冲区(或其可访问部分)的结束位置生效。
\=- 匹配空字符串,且仅在当前光标位置生效。
\b表示 单词边界 ,匹配空字符串,且仅在单词的起始或结束位置生效。因此, '
\bfoo\b' 可匹配作为独立单词出现的 'foo' , '\bballs?\b' 可匹配作为独立单词出现的 'ball' 或 'balls' 。无论缓冲区首尾的相邻字符为何, '
\b' 均可在缓冲区的起始和结束位置匹配。\B- 匹配空字符串,且仅在非单词边界的位置生效(与 '
\b' 含义相反)。 \<- 匹配空字符串,且仅在单词的起始位置生效。仅当缓冲区起始位置后接单词构成字符时, '
\<' 才可在该位置匹配。 \>- 匹配空字符串,且仅在单词的结束位置生效。仅当缓冲区的内容以单词构成字符结尾时, '
\>' 才可在缓冲区结束位置匹配。 \w- 匹配任意单词构成字符,具体的字符范围由 Emacs 的语法表定义,参见《Emacs Lisp 参考手册》中的《语法表》相关章节。
\W- 匹配任意非单词构成字符(与 '
\w' 含义相反)。 \_<- 表示符号边界,匹配空字符串,且仅在符号的起始位置生效。符号指由一个或多个符号构成字符组成的序列,而符号构成字符指语法类型为 '
w' (单词)或 '_' (下划线)的字符。仅当缓冲区起始位置后接符号构成字符时, '\_<' 才可在该位置匹配。与单词规则一致,符号构成字符的范围也由语法表定义。 \_>- 匹配空字符串,且仅在符号的结束位置生效。仅当缓冲区的内容以符号构成字符结尾时, '
\_>' 才可在缓冲区结束位置匹配。 \sc- 匹配任意语法类型为
c的字符。其中c为指定语法类别的标识字符,例如 'w' 代表单词构成字符、 '-' 或 ' ' 代表空白字符、 '.' 代表普通标点字符等。语法类别的完整列表参见《Emacs Lisp 参考手册》中的《语法类代表格》。 \Sc- 匹配任意语法类型非
c的字符(与 '\sc' 含义相反)。 \cc- 匹配任意属于类别
c的字符。例如, '\cc' 匹配中文字符、 '\cg' 匹配希腊字符等。若要查看所有已定义的字符类别,可执行命令M-x describe-categories RET。 \Cc- 匹配任意不属于类别
c的字符(与 '\cc' 含义相反)。
所有与单词、语法相关的构造符,其行为均由语法表的配置决定,参见《Emacs Lisp 参考手册》中的语法表相关章节。
17.8. 正则表达式示例
以下为一个正则表达式示例 —— 该表达式与 Emacs 默认用于识别句子结尾(不含后续空格)的正则表达式类似,对应变量为 sentence-end-base :
[.?!][]\"')}]*
该表达式由两个连续的部分构成:第一部分是匹配句点、'?' 问号或 '!' 感叹号的字符集,第二部分是匹配 ']' 右括号、引号或 ')' 圆括号、'}'大括号的字符集,且第二部分可匹配零次或多次。
17.9. 搜索中的宽松匹配(Lax Matching During Searching)
通常情况下,你会希望搜索命令忽略输入的搜索字符串与待搜索文本之间的某些细微差异。例如,不同长度的空白字符序列通常被视为等效;字母的大小写差异通常不影响匹配,这类规则被称为 character equivalence字符等效性 。
本节将介绍 Emacs 的 lax search 宽松搜索 特性,以及如何根据自身需求对其进行定制。
默认情况下,搜索命令会执行 lax space matching宽松空格匹配 :搜索字符串中的单个空格或连续空格序列,可匹配文本中任意一个或多个空白字符组成的序列。更准确地说,Emacs 会将搜索字符串中的每段空格序列,匹配为用户选项 search-whitespace-regexp 所指定的正则表达式。该选项的默认值将任意空格和制表符序列视为空白字符,因此搜索字符串 'foo bar' 可匹配文本中的 'foo bar' 、'foo bar'、'foo bar' 等形式(但不匹配 'foobar')。若你希望让空格同时匹配换行符、空格和制表符组成的序列,可自定义该选项,将其值设为正则表达式 '[ \t\n]+' (增量正则表达式搜索的默认行为与此不同,参见正则表达式搜索章节)。
若你需要让空白字符 精确匹配 ,可在增量搜索过程中按下 M-s SPC (isearch-toggle-lax-whitespace) 关闭宽松空格匹配,再次按下该快捷键则可重新开启。若要对所有搜索禁用宽松空格匹配,可将 search-whitespace-regexp 设为 nil ,此时搜索字符串中的每个空格将仅匹配文本中的单个空格。
在 Emacs 中,若你输入的搜索字符串均为小写字母,搜索命令默认 会忽略待搜索文本的大小写 。例如,搜索 'foo' 时,文本中的 'Foo' 和 'fOO' 也会被匹配。正则表达式(尤其是字符集)的匹配规则同理, '[ab]' 会匹配'a' 、 'A' 、 'b' 、 'B' 。该特性被称为 case folding大小写折叠 ,增量搜索和非增量搜索模式均支持此特性。
若搜索字符串中 任意位置包含大写字母 ,则搜索会变为 大小写敏感 模式。例如,搜索 'Foo' 时,不会匹配文本中的 'foo' 或 'FOO' 。该规则同时适用于正则表达式搜索和字面字符串搜索,若将搜索字符串中的大写字母删除,大小写敏感的效果也会随之消失。变量 search-upper-case 用于控制该特性:若其值为非nil,搜索字符串中的大写字母会触发大小写敏感搜索;若设为 nil ,则会禁用大写字母的该作用。该变量的默认值为 not-yanks ,此配置下,若搜索字符串包含大写字母则开启大小写敏感,同时会将撷取到搜索字符串中的文本(参见增量搜索中的文本撷取)转换为小写,使这类搜索默认保持大小写不敏感。
若将变量 case-fold-search 设为 nil ,则所有字符 必须完全精确匹配 (包括大小写)。该变量为缓冲区局部变量,修改其值通常仅对当前缓冲区生效,除非你更改其默认值(参见局部变量章节)。该变量同样适用于非增量搜索,包括替换命令(参见替换命令章节)和迷你缓冲区历史记录匹配命令(参见迷你缓冲区历史记录章节)执行的搜索。
在增量搜索过程中,按下 M-c 或 M-s c (isearch-toggle-case-fold) 可切换本次搜索的大小写敏感状态。该切换效果仅对当前增量搜索有效,但会覆盖因在当前搜索字符串中添加或删除大写字母而产生的大小写匹配规则。
另有多个相关变量用于控制特定命令或操作的搜索与匹配大小写敏感性,例如 tags-case-fold-search 控制 find-tag 命令的大小写敏感性。若要查找这类变量,可执行 M-x apropos-variable RET case-fold-search RET 。
大小写折叠会忽略字符间的大小写差异,使大写字符与对应小写字符相互匹配。 character folding字符折叠 是大小写折叠的扩展特性,它会忽略相似字符之间更广泛的差异。例如,开启字符折叠后,字母 a 会匹配其所有带重音的变体(如 ä 和 á ),即匹配时会忽略区分这些变体的变音符号;此外, a 还会匹配其他外形相似、或在图形表示中包含 a 的字符,如 Unicode 字符 U+00AA(阴性序数指示符)和 U+24D0(圆形小写字母 a)。同理,ASCII 双引号 " 会匹配 Unicode 标准中定义的所有双引号变体。字符折叠甚至可让一个或多个字符组成的序列,匹配另一个不同长度的字符序列,例如双字符序列 ff 会匹配 Unicode 字符 U+FB00(拉丁文连字小写 ff),序列 (a) 会匹配 U+249C(带圆括号的拉丁文小写字母 a)。那些并非完全相同、但在字符折叠规则下可相互匹配的字符序列,被称为 equivalent character sequences等效字符序列 。
通常情况下,Emacs 的搜索命令 默认不执行字符折叠 ,即不会匹配等效字符序列。你可通过将变量 search-default-mode 自定义为 char-fold-to-regexp 来启用该特性(参见根据需求自定义搜索行为章节)。在增量搜索过程中,按下 M-s ' (isearch-toggle-char-fold) 可切换本次搜索的字符折叠状态,该效果仅对当前搜索有效(替换命令的默认行为与此不同,由独立选项控制,参见替换命令与宽松匹配章节)。
默认情况下,若在搜索字符串中输入某字符的显式变体(如 ä ),不会匹配其基础字符(如 a )。但如果将变量 char-fold-symmetric 自定义为 t ,搜索命令会将等效字符同等对待:在搜索字符串中使用任意一个等效字符,都会匹配待搜索文本中的所有等效字符,因此输入带重音的 ä 时,会同时匹配基础字母a及其所有变体(如 á )。
你可通过可自定义变量 char-fold-include 添加新的字符折叠规则,或通过 char-fold-exclude 移除现有规则;也可将 char-fold-override 自定义为 t ,此时会禁用所有默认字符等效性规则,仅保留你通过 char-fold-include 自行添加的规则。
17.10. 替换命令
Emacs 提供了多个用于执行查找替换操作的命令。除简单的 M-x replace-string (字符串替换)命令外,还有 M-% (查询替换)命令,该命令会逐个定位搜索模式的匹配项,并询问你是否执行替换。
替换命令默认对 从光标位置到缓冲区末尾 的文本生效 ;当选区处于激活状态时,替换操作将仅作用于选区内的文本(参见标记与选区)。基础替换命令可将一个 search string搜索字符串 (或正则表达式)替换为一个指定的 replacement string替换字符串 。你也可以使用 expand-region-abbrevs 命令(参见缩写展开的控制),并行执行多项替换操作。
17.10.1. 无条件替换
M-x replace-string RET string RET newstring RET- 将字符串的每一处匹配项替换为新字符串。
若要将光标位置之后的所有 'foo' 替换为 'bar' ,可执行命令 M-x replace-string ,并传入两个参数 'foo' 和 'bar' 。替换操作仅作用于光标位置之后的文本,因此如果需要覆盖整个缓冲区,必须先将光标移至缓冲区起始位置。从光标处到缓冲区末尾的所有匹配项都会被替换;若要将替换范围限定在缓冲区的某一部分,需先选中该区域。当区域处于激活状态时,替换操作将仅在该区域内进行(详见《标记与区域》章节)。
replace-string 命令执行完毕后,光标会停留在最后一个被替换的匹配项位置。该命令执行前的光标位置会被添加到标记环中,但不会激活标记;可使用 C-u C-SPC 快捷键返回该位置(详见《标记环》章节)。
若为该命令添加前缀参数,则仅会替换那些被单词边界包围的匹配项。
关于替换命令中大小写敏感性与字符折叠的详细说明,详见《替换命令与宽松匹配》章节。
17.10.2. 正则表达式替换
M-x replace-string 命令用于替换指定字符串的 精确匹配项 。与之类似的命令 M-x replace-regexp ,则可替换符合指定正则表达式模式的 任意匹配 内容(详见《正则表达式语法》章节)。
M-x replace-regexp RET regexp RET newstring RET- 将所有匹配该正则表达式的内容替换为新字符串。
在 replace-regexp 命令中, newstring新字符串并非必须是固定文本:它可以 引用正则表达式匹配结果的全部或部分内容 。新字符串中的 '\&' 代表被替换的 整个匹配内容 ;新字符串中的 '\d' (其中 d 是从 1 开始的数字),代表正则表达式中 第 d 个带括号的分组所匹配的内容 (这一用法被称为 “反向引用”)。 '\#' 代表当前命令 已完成的替换次数 ,以十进制数字表示 —— 第一次替换时, '\#' 代表 0;第二次替换时,代表 1,依此类推。例如:
M-x replace-regexp RET c[ad]+r RET \&-safe RET
可将(例如) 'cadr' 替换为 'cadr-safe' ,将 'cddr' 替换为 'cddr-safe' 。
M-x replace-regexp RET \(c[ad]+r\)-safe RET \1 RET
则可实现上述操作的逆向转换。若需在替换文本中包含反斜杠 '\' ,必须输入 '\\' 。
如果希望在 每次替换时手动输入部分替换字符串 ,可在替换字符串中使用 '\?' 。执行替换时,Emacs 会在迷你缓冲区中打开替换字符串供你编辑,光标会自动定位在 '\?' 所在的位置。
本小节的剩余内容适用于 特定专业任务 ,且需要掌握 Lisp 语言相关知识,多数读者可跳过此部分。
你可以使用 Lisp 表达式来计算替换字符串的部分内容。具体方法是,在替换字符串中写入 '\,' ,并在其后跟上对应的表达式。每次替换时,Emacs 会计算该表达式的值,将其直接转换为文本(若表达式的值为字符串,则直接使用字符串内容),并将该文本代入替换字符串,替代原表达式的位置。若表达式为一个符号,那么符号名称后的一个空格会被视为符号名称的一部分,表达式的值会同时替换符号名称与该空格。
在这类表达式内部,你可以使用一些特殊序列。与常规用法一致, '\&' 和 '\d' 分别代表整个匹配内容的字符串形式,以及对应子匹配内容的字符串形式。其中 d 可以是多位数字;若第 d 个带括号的分组未匹配到任何内容, '\d' 的值则为 nil 。你也可以使用 '\#&' 和 '\#d',将对应匹配内容 以数字形式引用 (此用法仅在匹配内容或子匹配内容为数字格式时有效)。此处的 '\#' 同样代表已完成的替换次数。
例如,我们可以通过以下方式交换文本中的 x 和 y:
M-x replace-regexp RET \(x\)\|y RET \,(if \1 "y" "x") RET
在通过 '\' , 构造替换字符串时, format 函数往往能发挥重要作用(详见《Emacs Lisp 参考手册》中的《格式化字符串》章节)。例如,要在第 73 至 80 列的位置添加连续编号的字符串(如 'ABC00042'),且仅在这些列原本无内容时执行添加操作,可执行以下命令:
M-x replace-regexp RET ^.\{0,72\}$ RET \,(format "%-72sABC%05d" \& \#) RET
17.10.3. 替换命令与宽松匹配
本节介绍替换命令在宽松匹配下的行为(参见搜索中的宽松匹配)及其自定义方法。总体而言,替换命令的默认匹配规则,相较于对应的搜索命令会更为严格。
与增量搜索不同,替换命令默认不启用宽松空格匹配(参见宽松空格匹配)。若要为替换操作启用该功能,需将变量 replace-lax-whitespace 设为非nil值(该设置仅影响 Emacs 查找待替换文本的方式,不作用于替换文本本身)。
配套变量 replace-regexp-lax-whitespace ,用于控制 query-replace-regexp 命令在查找匹配模式时,是否启用宽松空格匹配。
若替换命令的第一个参数(搜索串) 全部由小写字母组成 ,则在查找待替换匹配项时会忽略大小写 —— 此行为的前提是 case-fold-search 和 search-upper-case 均为非nil值。若 search-upper-case (参见 search-upper-case)设为 nil ,则搜索是否忽略大小写仅由 case-fold-search 单独决定,与第一个参数的字母大小写无关。若 case-fold-search 设为 nil ,则所有搜索均始终区分大小写。
此外,若替换命令的第二个参数(替换串) 全部或部分为小写字母 , 替换命令会尝试保留每个匹配项的大小写格式 。例如执行命令:
M-x replace-string RET foo RET bar RET
会将小写的 'foo' 替换为小写的 'bar' 、全大写的 'FOO' 替换为 'BAR' 、首字母大写的 'Foo' 替换为 'Bar' (小写、全大写、首字母大写,是 replace-string 命令唯一能识别的三种大小写格式)。
需注意,Emacs 会通过分析 待替换文本中的每个单词 ,决定是否对替换文本进行大写或首字母大写转换;仅当待替换文本的所有单词采用 相同的大小写格式 时,才会保留其格式。例如执行命令:
M-x replace-string RET foo bar RET baz quux RET
会将 'Foo Bar' 替换为 'Baz Quux' ,原因是 'Foo Bar' 中的两个单词均为首字母大写格式;而对于 'Foo bar' ,该命令会将其替换为 'baz quux' (即保持替换文本的原始大小写不变),因为 'Foo bar' 中的两个单词大小写格式不同。
关于何为 “word单词”,由当前缓冲区生效的语法表决定(参见《Emacs Lisp 参考手册》中的语法表章节);例如在文本模式中, 'Foo_Bar' 会被视为两个单词,而在部分支持编程语言的主模式中,它可能被视作单个单词。
若替换串中包含 大写字母 ,则该部分内容在每次插入时,均会保持大写形式。若第一个参数(搜索串)中包含大写字母,第二个参数(替换串)会 严格按照输入内容原样替换 ,不进行任何大小写转换。同理,若 case-replace 或 case-fold-search 任一变量设为 nil ,替换操作也会直接执行,不做大小写转换。
替换命令在查找待替换文本时, 默认不启用字符折叠 (参见字符折叠)。若要在 query-replace 和 replace-string 命令的匹配过程中启用字符折叠,需将变量 replace-char-fold 设为非nil值(该设置仅影响 Emacs 查找待替换文本的方式,不作用于替换文本本身,也对 replace-regexp 命令无效)。
17.10.4. 查询替换(Query Replace)
M-% string RET newstring RET- 将指定字符串的部分匹配项替换为新字符串。
C-M-% regexp RET newstring RET- 将指定正则表达式的部分匹配项替换为新字符串。
若你无需替换foo的所有匹配项,仅需修改其中一部分为bar,可使用 M-% (query-replace) 命令。该命令会逐个查找foo的匹配项,高亮展示每个匹配项并询问是否执行替换。除了交互式确认这一特性,该命令的其余行为与 replace-string (无条件替换)完全一致(参见无条件替换);特别地,在默认开启 case-replace 的情况下,命令会自动保留匹配项的大小写格式(参见替换命令与宽松匹配)。带数字前缀参数执行该命令时,仅会匹配以单词分隔符为边界的完整单词匹配项;带负的前缀参数执行时,会反向向前查找并替换匹配项。
C-M-% 命令执行的是正则表达式的交互式查询替换 (query-replace-regexp) ,其行为与 replace-regexp (正则表达式替换)一致,仅增加了交互式确认替换的步骤。
你可复用上述命令的历史替换规则:当 query-replace 或 query-replace-regexp 命令提示输入搜索串时,按下 M-p 和 M-n 可翻阅历史替换记录,记录将以 'from -> to' 原内容 -> 替换内容的形式展示(其中原内容为搜索模式,替换内容为目标字符串,分隔符由变量 query-replace-from-to-separator 定义),按下 RET 回车键即可选中并复用该条替换规则。若将该变量设为 nil ,替换记录将不会被加入命令历史,也无法被复用。
这类交互式替换命令会通过 query-replace 高亮样式标记当前匹配项,将变量 query-replace-highlight 设为 nil 可关闭该高亮;同时会像增量搜索一样,通过 lazy-hightlight 延迟高亮标记其他匹配项(参见增量搜索),将 query-replace-lazy-highlight 设为 nil 可关闭此效果。默认情况下, query-replace-regexp 会在迷你缓冲区中展示当前匹配项经替换后的结果;若希望保留 '\&' 、 '\n' 等特殊序列不被展开,可自定义 query-replace-show-replacement 变量。与增量搜索中通过 search-highlight-submatches 高亮子表达式匹配项的功能类似(参见根据需求自定义搜索行为),变量 query-replace-highlight-submatches 用于控制正则表达式替换命令是否高亮子表达式的匹配项。
将变量 query-replace-skip-read-only 设为非 nil 值后,替换命令会忽略只读文本中的匹配项,该变量默认值为 nil (即不忽略只读文本中的匹配项)。
当命令定位到字符串或正则表达式的匹配项并发出询问时,可输入以下字符执行对应操作:
SPCy- 将当前匹配项替换为新字符串,继续查找下一个匹配项。
DELDeleteBACKSPACEn- 跳过当前匹配项,不执行替换,直接查找下一个。
, (逗号)替换当前匹配项并展示替换结果,随后需再次输入字符决定后续操作。因替换已完成,此场景下删除键和空格的作用一致,均为跳至下一个匹配项。
此时可按下
C-r(详见下文)编辑已替换的文本,也可使用撤销命令(如C-x u,参见撤销操作)撤销本次替换; 撤销操作会直接退出交互式查询替换 ,若需继续替换,需通过C-x ESC ESC RET重新启动命令(参见重复迷你缓冲区命令)。RETq- 直接退出命令,不再执行后续任何替换。
. (句点)- 替换当前匹配项,且替换后直接退出命令,不再查找后续匹配项。
! (感叹号)- 无需再次确认,直接替换剩余所有匹配项。
^ (脱字符)- 返回上一个匹配项的位置(或原匹配项的位置),适用于误操作修改匹配项,或需要重新检查上一个匹配项的场景。
u- 撤销最后一次的替换操作,并回到该次替换的匹配项位置。
U- 撤销本次命令中所有的替换操作,回到第一次替换的匹配项位置。
C-r- 进入递归编辑模式,适用于无需直接替换、需手动编辑当前匹配项的场景。编辑完成后,按下
C-M-c退出递归编辑模式,继续查找下一个匹配项(参见递归编辑模式)。 C-w- 删除当前匹配项,随后进入与
C-r相同的递归编辑模式,可手动输入文本替代被删除的匹配项;编辑完成后,按下C-M-c退出递归编辑模式,继续查找下一个匹配项。 e- 在迷你缓冲区中编辑新的替换字符串,按下回车键退出迷你缓冲区后,会用编辑后的内容替换当前匹配项,且该内容会成为后续所有匹配项的默认替换字符串。
E- 功能与
e类似,但本次替换会 严格按替换串的原始大小写执行 。例如,原替换规则为将foo替换为bar时,Foo会默认被替换为Bar;按下E后,当前匹配项会直接替换为小写的bar,不做大小写适配。 C-l- 重绘屏幕刷新显示,操作后需再次输入字符,决定当前匹配项的处理方式。
Y (大写)- 在多缓冲区替换场景中(如
Dired的Q命令,对选中文件执行交互式查询替换),替换当前缓冲区及剩余所有缓冲区中的全部未处理匹配项。该操作会对本次及后续所有替换询问均自动回答 “yes”,无需用户再手动确认。 N (大写)- 在多缓冲区替换场景中,跳过当前缓冲区的剩余所有匹配项,直接切换至下一个缓冲区。该操作会对当前替换询问回答 “no”,并放弃当前缓冲区的所有后续替换询问,继续处理序列中的下一个缓冲区。
C-h?F1- 展示上述所有操作选项的汇总提示信息,查看后需再次输入字符,决定当前匹配项的处理方式。
除上述字符外,输入 其他任意字符都会直接退出 query-replace交互式查询替换 ,且该字符会被重新读取作为按键序列的一部分执行对应操作。例如,输入 C-k 会先退出 query-replace 命令,再执行 “删除至行尾” 的操作;输入 C-g 则会直接退出 query-replace ,无其他后续操作。
若替换命令已退出,需重新启动时,可按下 C-x ESC ESC —— 该快捷键会重复执行上一次的交互式查询替换(因命令的参数均通过迷你缓冲区读取),详见 C-x ESC ESC 相关说明。
变量 search-invisible 决定了交互式查询替换命令对隐藏文本的处理方式,参见大纲模式中的搜索。
关于对选中文件执行交互式查询替换的 Dired Q 命令,参见文件操作相关章节;此外,在 Dired 中通过正则表达式匹配重命名、复制或创建文件链接的相关命令,参见 Dired 中的文件名转换。
17.11. 其他搜索与循环命令
以下是一些用于查找正则表达式匹配项的其他命令。若模式中不包含大写字母且 case-fold-search 变量非空,这些命令在匹配时均忽略大小写。除始终搜索整个缓冲区的 multi-occur 和 multi-occur-in-matching-buffers 外,所有命令均作用于 从光标位置到缓冲区末尾 的文本;若选区处于激活状态,则作用于选区。
M-x multi-isearch-buffers- 提示输入一个或多个缓冲区名称,按
RET回车键结束;随后在这些缓冲区中 启动多缓冲区增量搜索 。(若在某个缓冲区中搜索失败,按下下一个C-s会尝试在指定的下一个缓冲区中继续搜索,依此类推。)带前缀参数执行时,会提示输入一个正则表达式,并在匹配该正则表达式的所有缓冲区中启动多缓冲区增量搜索。 M-x multi-isearch-buffers-regexp- 该命令与
multi-isearch-buffers功能基本一致,区别在于它执行的是 多缓冲区增量正则表达式搜索 。 M-x multi-isearch-files- 提示输入一个或多个文件名,按
RET回车键结束;随后在这些文件中启动 多文件增量搜索 。(若在某个文件中搜索失败,按下下一个C-s会尝试在指定的下一个文件中继续搜索,依此类推。)带前缀参数执行时,会提示输入一个正则表达式,并在匹配该正则表达式的所有文件中启动多文件增量搜索。 M-x multi-isearch-files-regexp该命令与
multi-isearch-files功能基本一致,区别在于它执行的是 多文件增量正则表达式搜索 。在部分设置了缓冲区局部变量
multi-isearch-next-buffer-function的模式中(例如Change Log mode变更日志模式),多文件增量搜索会被自动激活。M-x occurM-s o提示输入一个正则表达式,随后显示缓冲区中 所有包含该匹配项的行 的列表。在提示界面按下
M-n,可复用之前增量搜索使用过的搜索字符串。匹配到的文本会通过match(字体样式)高亮显示。带数字参数n执行时,会在每个匹配行的前后各显示n行上下文内容。上下文的默认行数由变量
list-matching-lines-default-context-lines指定。当变量list-matching-lines-jump-to-current-line非空时,当前行会以list-matching-lines-current-line-face样式高亮显示,且光标会定位到该行后的第一个匹配项处。若增量搜索正在进行,直接按下
M-s o即可基于 当前的搜索字符串 执行该命令。注意:输入的正则表达式匹配项会被扩展为 整行匹配 ,且若某个匹配项的起始位置在前一个匹配项的结束位置之前,该匹配项将不被计入。
*Occur*缓冲区以Occur 模式作为主模式:- 按下
n和p键可跳至下一个 / 上一个匹配项;带数字前缀参数时,可按指定次数跳至对应匹配项(数字键绑定了 ~digit-argument~ ,因此输入5 n即可跳至后面第5个匹配项,无需先按C-u); - 按下空格键(
SPC)和删除键(DEL)可上下滚动*Occur*缓冲区; - 点击某个匹配项,或将光标移至该匹配项处并按回车键,会跳转到被搜索的原始缓冲区中对应的位置;
- 按下
o和C-o可在另一个窗口中显示该匹配项(=C-o= 不会选中新窗口); - 也可使用 M-g M-n(next-error,下一个错误)命令逐个访问所有匹配项(参见编译模式);
- 按下
q可关闭显示*Occur*缓冲区的窗口,并将该缓冲区隐藏。
在
*Occur*缓冲区中按下e,会将该缓冲区设为可写状态并进入Occur 编辑模式,此时可编辑匹配行,且所有编辑操作会同步至原始缓冲区的对应文本。按下C-c C-c可退出 Occur 编辑模式,恢复为普通 Occur 模式。命令
M-x list-matching-lines是M-x occur的同义词。- 按下
M-x multi-occur- 该命令与
occur功能基本一致,区别在于它可在 多个缓冲区中同时搜索 ,执行时会提示逐个指定需要搜索的缓冲区名称。 M-x multi-occur-in-matching-buffers- 该命令与 ~multi-occur~ 功能类似,区别在于 待搜索的缓冲区 通过 匹配已访问文件名的正则表达式 指定;带前缀参数执行时,会改为通过正则表达式匹配 缓冲区名称 来指定待搜索缓冲区。
M-x how-many- 提示输入一个正则表达式,随后 统计并打印 光标位置之后的缓冲区中该表达式的匹配次数;若选区处于激活状态,则仅统计选区中的匹配次数。
M-x flush-lines- 提示输入一个正则表达式,随后 删除 光标位置之后的文本中 所有包含该匹配项的行 ;命令执行完毕后,会打印被删除的匹配行数量。
- 若当前行中光标位置之后存在匹配项,该行会被删除;
- 若选区处于激活状态,则仅对选区生效:若某行部分包含在选区内,且其匹配项 完全位于选区内 ,该行会被删除;
- 若某个匹配项跨越多行,这些行都会被一并删除;该命令会先删除匹配行,再继续查找下一个匹配项,因此会忽略在前一个匹配项结束行的同一行起始的新匹配项。
M-x keep-lines- 提示输入一个正则表达式,随后 删除 光标位置之后的文本中 所有不包含该匹配项的行 ;
- 若光标未位于行首,该命令始终保留当前行;
- 若选区处于激活状态,则仅对选区生效:永远不会删除仅部分包含在选区内的行(行尾的换行符视为该行的一部分);
- 若某个匹配项跨越多行,这些行都会被一并保留。
M-x kill-matching-lines- 功能与
flush-lines一致,区别在于该命令会 将被删除的匹配行 添加至 删除环(kill ring) 中,且所有匹配行会被合并为单个字符串(包含行之间的换行符)后存入删除环。 M-x copy-matching-lines- 功能与
kill-matching-lines一致,区别在于该命令不会 从缓冲区中删除匹配行 ,仅将其复制至删除环。
17.12. 根据需求定制搜索
本节介绍其他章节未提及的各类搜索相关自定义配置。
增量搜索的默认模式由变量 search-default-mode 指定,其值可为 nil 、 t 或一个函数:
- 若值为
nil,默认执行 字面量搜索 ,不进行字符折叠,但会根据case-fold-search和~search-whitespace-regexp~ 分别开启大小写折叠和宽松空白匹配(参见「搜索中的宽松匹配」); - 若值为
t,增量搜索默认执行 正则表达式搜索 ; - 该变量的默认值为一个函数,仅会执行大小写折叠和宽松空白匹配。
正在进行的增量搜索中, 当前匹配项 会通过 isearch 面(字体样式)高亮显示,将变量 search-highlight 设为 nil 可关闭该高亮效果。
当搜索正则表达式时(例如使用快捷键 C-M-s ),子表达式的高亮规则由变量 search-highlight-submatches 决定:
- 若该变量值为
nil,子表达式无特殊高亮; - 若值非nil,正则表达式中由 '
\( … \)' 定义的子表达式匹配到的文本,会用不同的面进行高亮。
默认情况下,Emacs 定义了两个专用高亮面: isearch-group-1 和 isearch-group-2 。基于这两个面的规则: 奇数序号的子表达式 由 isearch-group-1 高亮, 偶数序号的子表达式 由 isearch-group-2 高亮。例如搜索正则表达式 'foo-\([0-9]+\)\([a-z]+\)' 时, '[0-9]+' 匹配的内容会显示 isearch-group-1 样式, '[a-z]+' 匹配的内容会显示 isearch-group-2 样式。
若按相同命名规则自定义更多高亮面(如 isearch-group-3 、 isearch-group-4 等),则第 M 、 N+M 、 2N+M 个(以此类推)子表达式会由 isearch-group-M 高亮,其中 N 为所有 isearch-group-M 格式高亮面的总数。
当前屏幕中 可见的其他匹配项 ,会通过 lazy-highlight 面样式进行高亮,将变量 isearch-lazy-highlight 设为 nil 可关闭该懒高亮效果。以下是用于自定义懒高亮的其他变量:
lazy-highlight-initial-delay- 高亮可见匹配项前的等待时间,单位为秒。仅当搜索字符串的长度小于
lazy-highlight-no-delay-length的取值时,该配置才生效。 lazy-highlight-no-delay-length- 当搜索字符串的长度大于或等于该变量的取值时,懒高亮会立即触发,无等待时间。
lazy-highlight-interval- 依次高亮多个匹配项时的时间间隔,单位为秒。
lazy-highlight-max-at-a-time- 一次最多高亮的匹配项数量,达到该数量后会先检查用户输入再继续。若该值设置过大,高亮过程会耗时较久;此期间若按下
C-s或C-r继续搜索,Emacs 需完成所有高亮后才会响应,因此 更小的取值 能提升 Emacs 的响应速度。 isearch-lazy-count- 在搜索提示栏中显示当前匹配项的序号和匹配项的总数。
lazy-count-prefix-formatlazy-count-suffix-format- 这两个变量共同决定了
isearch-lazy-count的显示格式,用于定义当前匹配数和总匹配数的前后缀样式。
默认情况下,若增量搜索中搜索字符串为空,按下回车键( RET )会启动非增量搜索(实际流程为:先允许编辑搜索字符串,再次按下回车键后执行搜索)。若将变量 search-nonincremental-instead 设为 nil ,则无论搜索字符串是否为空,按下回车键都会直接退出增量搜索。
默认情况下,增量搜索和查询替换命令会 匹配不可见文本 ,但当当前匹配项离开该不可见文本区域后,会立即隐藏相关匹配的高亮。若将变量 isearch-hide-immediately 设为 nil ,则找到匹配项的不可见文本会保持显示,直到搜索或替换命令执行完毕。
在低速终端中进行增量搜索(例如通过低速网络连接远程机器的显示器),搜索过程中需要重绘屏幕的大部分区域,操作体验会较差。Emacs 为低速终端提供了专用的显示模式:搜索时会弹出一个独立的小窗口,仅在该窗口中显示匹配项周边的文本。小窗口的重绘速度更快,能有效缓解低速终端的操作卡顿问题。相关配置变量如下:
search-slow-speed:设置波特率阈值,当终端波特率低于该值时,Emacs 会自动启用该低速终端显示模式;search-slow-window-lines:控制搜索结果弹出窗口的行数,默认值为 1 行。该窗口默认出现在启动 搜索的缓冲区窗口底部 ;若该变量取负值,则窗口会显示在顶部,且窗口行数为该负值的绝对值。
18. 拼写错误修正命令
本章介绍在编辑过程中发现错误时可使用的实用命令。其中最基础的是撤销命令 C-/ (同时绑定至 C-x u 和 C-_ )。该命令可撤销单个操作命令、某一命令的部分执行结果(如查询替换的部分操作),或连续的多次字符输入操作。连续按下 C-/ 可依次撤销更早的修改操作,直至达到撤销记录的存储上限。
除本章介绍的命令外,你也可使用删除类命令清除文本,例如 DEL (delete-backward-char) 。这类命令已在本手册的前文介绍,详见「文本删除」章节。
18.1. 撤销(Undo)
撤销命令可还原缓冲区文本中近期的修改操作。每个缓冲区会单独记录自身的修改,撤销命令始终作用于 当前缓冲区 。你可以根据缓冲区的记录,撤销其中所有的修改操作。通常,每个编辑命令都会在撤销记录中生成一条独立条目;但为了提升撤销操作的灵活性,部分命令(如查询替换)会将自身的修改拆分为多条记录。连续的字符输入操作则通常会合并为 单条撤销记录 ,让撤销操作更简洁。
撤销快捷键
C-/C-x uC-_- 撤销当前缓冲区撤销记录中的一条条目 (
undo) 。
基础撤销操作
要执行撤销,按下 C-/ (或其等效快捷键 C-_ 、 C-x u ) 6 即可。该操作会还原缓冲区中最近一次的修改,并将光标移回修改前的位置。连续按下 C-/ (或其等效快捷键),可依次还原当前缓冲区中更早的修改。若所有已记录的修改均已被还原,执行撤销命令会触发错误提示。
撤销序列的中断与重做
除撤销命令外的任意操作,都会中断连续的撤销序列。从操作执行的那一刻起,你此前完成的整段撤销操作,会自身被写入撤销记录。因此,若要重新应用已被撤销的修改,可按下 C-f (前移字符)或其他无副作用的命令中断撤销序列,随后多次按下 C-/ ,撤销部分此前的撤销操作即可。
若你希望继续执行撤销,而非重做已完成的撤销操作,可使用 M-x undo-only 命令。该命令功能与 undo 一致,但不会重做已被撤销的修改。与之互补的是 M-x undo-redo 命令,它可撤销此前的撤销操作,且自身不会被记录为可撤销的操作。
恢复意外修改的缓冲区
若发现缓冲区被意外修改,最简便的恢复方式是反复按下 C-/ ,直至模式行开头的星号消失(参见「模式行」章节)。当撤销命令让模式行的星号消失时,意味着缓冲区内容已恢复至最近一次读取或保存文件时的状态。若你不确定是否是有意修改了缓冲区,可先按一次 C-/ ,看到最近一次的修改被还原后,即可判断该修改是否为操作失误:若是意外修改,保持撤销后的状态即可;若是有意修改,按上述方法重做该修改即可。
此外,你也可使用 M-x revert-buffer 命令,丢弃缓冲区自最近一次打开或保存后所有的修改(参见「还原缓冲区」章节)。
选区的选择性撤销
当存在激活的选区时,执行任意撤销操作都会触发选择性撤销:仅还原选区内最近一次的修改,而非整个缓冲区的修改。但当临时标记模式(Transient Mark mode)关闭时(参见「关闭临时标记模式」), C-/ 会始终作用于整个缓冲区,忽略选区的存在。这种情况下,可为撤销命令添加前缀参数来执行选择性撤销:按下 C-u C-/ 即可;若要继续还原同一选区内更早的修改,直接重复执行撤销命令即可,无需再添加前缀参数。
无撤销记录的特殊缓冲区
部分专用缓冲区不会生成撤销记录: 名称以空格开头 的缓冲区始终不会记录,这类缓冲区由 Emacs 内部使用,用于存储用户通常不会查看或编辑的文本。
撤销记录的内存限制
当某个缓冲区的撤销信息占用空间过大时,Emacs 会在垃圾回收过程中 不定期丢弃最旧的记录 。你可通过设置变量 undo-limit 、 undo-strong-limit 和 undo-outer-limit ,指定 Emacs 保留的撤销信息容量,变量值均以字节为单位。
undo-limit(软限制):Emacs 会保留足够多的命令撤销数据,直至达到该容量上限,甚至可小幅超出,但不会保留超出部分的更早命令数据,默认值为 160000 字节。undo-strong-limit(硬限制):更严格的容量上限,若某条历史命令的撤销数据会让总占用量超出该值,该命令的记录会被直接丢弃(最近一次的修改记录除外),默认值为 240000 字节。
无论上述两个变量的取值如何, 最近一次的修改记录永远不会被丢弃 ,除非其占用空间超过 undo-outer-limit (默认值为 24000000 字节)。当达到该极限时,Emacs 会丢弃此次的撤销数据并向你发出警告 —— 这是 唯一无法撤销上一条命令 的情况。若出现该问题,你可增大 undo-outer-limit 的取值,降低后续出现该情况的概率;但如果该命令并非预期会生成超大撤销数据,这很可能是程序漏洞,建议你进行反馈(参见「漏洞反馈」章节)。
18.2. 文本交换(Transposing Text)
C-t- 调换两个字符 (
transpose-chars) 。 M-t- 调换两个单词 (
transpose-words) 。 C-M-t- 调换两个配对表达式 (
transpose-sexps) 。 C-x C-t- 调换两行文本 (
transpose-lines) 。 M-x transpose-sentences- 调换两个句子 (
transpose-sentences) 。 M-x transpose-paragraphs- 调换两个段落 (
transpose-paragraphs) 。 M-x transpose-regions- 调换两个区域的文本。
相邻两个字符输反是最常见的输入错误,可使用 C-t 命令 (transpose-chars) 修正。默认情况下, C-t 会调换 光标两侧 的两个字符;若光标位于行尾,该命令不会将行尾字符与换行符无效调换,而是调换该行的 最后两个字符 。因此,若发现字符输反后立即操作,直接按 C-t 即可修正;若发现较晚,需先将光标移至输反的两个字符之间,再按下 C-t 。若你误将单词末尾的字符与后方的空格调换,可使用单词移动命令( M-f 、 M-b 等)快速定位光标;其他情况则常用反向搜索( C-r )定位,详见「搜索与替换」章节。
M-t 命令会调换 光标前的单词 与 光标后的单词 (transpose-words) ,执行时光标会向前跳过一个单词,同时将光标前方或包含光标的单词一并向前拖动(PS:这里的前方指光标右边),单词之间的标点符号位置保持不变。例如,文本 'FOO, BAR' 经调换后会变成 'BAR, FOO' ,而非 'BAR FOO,' 。若光标位于行尾,该命令会将光标前的单词与下一行的第一个单词调换。
C-M-t (transpose-sexps) 是功能类似的配对表达式调换命令(详见「带配对括号的表达式」), C-x C-t (transpose-lines) 用于调换两行文本。 M-x transpose-sentences 和 M-x transpose-paragraphs 则分别用于调换句子和段落,这两个命令的工作逻辑与 M-t 一致,仅调换的文本单位不同。
为调换命令添加数字前缀参数,该参数会作为重复执行的次数:命令会将光标前方或包含光标的字符(/ 单词 / 表达式 / 行),跨越多个同类文本单位进行调换。例如,按下 = C-u 3 C-t= ,会将光标前的字符向前跨 3 个字符调换,将文本 'f∗oobar' 转换为 'oobf∗ar',效果等同于连续按 3 次 C-t ;按下 C-u -4 M-t ,会将光标前的单词向后跨 4 个单词调换;按下 C-u - C-M-t ,可撤销普通 C-t 命令的调换效果。
数字前缀参数为 0时被赋予特殊含义(若无特殊定义,重复次数为 0 的命令将无任何操作):调换光标后结束的文本单位,与标记后结束的文本单位(字符 / 单词 / 表达式 / 行)。
M-x transpose-regions 命令用于调换两类文本:一类是光标与标记之间的文本,另一类是标记环中最后两个标记之间的文本(详见「设置标记」)。若为该命令添加数字前缀参数,会将光标与标记之间的文本,与标记环中向前数指定次数的 连续两个标记之间 的文本进行调换。该命令最适合一次性调换多段字符、单词、句子或段落。
18.3. 大小写转换(Case Conversion)
M-- M-l- 将最后一个单词转换为小写。注意,
M--即Meta键-减号。 M-- M-u- 将最后一个单词全部转换为大写。
M-- M-c- 将最后一个单词转换为首字母大写、其余小写的格式。
输错单词大小写是极为常见的输入错误。因此,大小写转换命令 M-l 、 M-u 、 M-c 搭配负参数使用时,会触发一项特殊功能:执行命令时光标不会移动。当你发现刚输入的最后一个单词大小写有误,只需直接执行对应的大小写转换命令,即可继续后续输入,无需调整光标位置。详见「大小写转换命令」章节。
18.4. 拼写检查与修正
本节介绍用于检查单个单词或部分缓冲区文本拼写的命令。这些命令仅在安装了拼写检查程序(Hunspell、Aspell、Ispell 或 Enchant 中的任意一款)时才能运行。上述程序并非 Emacs 的组成部分,但通常会预装在 GNU/Linux 及其他自由操作系统中。详见《Aspell 使用手册》中的 Aspell 相关章节。
若仅安装了一款拼写检查程序,首次调用本节所述命令时,Emacs 会自动检测并加载该程序。若安装了多款,则可通过自定义变量 ispell-program-name 来指定使用哪一款。
M-$- 检查并纠正光标所在位置单词的拼写 (
ispell-word) 。若选区处于激活状态,则检查选区范围内所有单词 C-u M-$- 若上一次拼写检查操作被中断,执行此命令可恢复该操作 (
ispell-continue) M-x ispell- 检查并纠正整个缓冲区的拼写。若选区处于激活状态,则仅检查选区内容
M-x ispell-buffer- 检查并纠正整个缓冲区的拼写
M-x ispell-region- 检查并纠正选区范围内的拼写
M-x ispell-message- 检查并纠正邮件草稿的拼写,自动排除引用内容
M-x ispell-comments-and-strings- 检查并纠正缓冲区或选区中代码的注释与字符串内容的拼写
M-x ispell-comment-or-string-at-point- 检查光标所在位置的代码注释或字符串的拼写
M-x ispell-change-dictionary RET dict RET- 重启拼写检查进程,并指定使用 词典名 对应的词典
M-x ispell-kill-ispell- 终止拼写检查子进程
M-TABESC TABC-M-i- 基于拼写词典及其他补全来源,补全光标前方的单词 (
completion-at-point) M-x flyspell-mode- 启用 Flyspell 模式,该模式会高亮显示所有拼写错误的单词
M-x flyspell-prog-mode- 启用适用于编程场景的 Flyspell 模式,仅检查代码注释与字符串中的拼写
基础拼写检查操作流程
要检查并选择性纠正光标附近或前方单词的拼写,可按下 M-$ (ispell-word) 。若选区已激活, M-$ 会检查选区中所有单词(详见 “标记与选区” 章节)。当暂态标记模式关闭时, M-$ 会忽略选区,始终作用于光标附近的单词(详见 “关闭暂态标记模式” 章节)。若带前缀参数执行 C-u M-$ ,则会调用 ispell-continue 命令,恢复此前被 X 或 C-g 中断的拼写检查操作。
同理, M-x ispell 命令会在选区激活时检查选区,否则检查整个缓冲区。 ispell-buffer 和 ispell-region 命令则分别明确指定对整个缓冲区或选区进行拼写检查。编写电子邮件时,可使用 M-x ispell-message 命令,该命令会检查整个缓冲区,但自动排除缩进内容或疑似引用自其他邮件的文本(详见 “发送邮件” 章节)。处理源代码时,可使用 ispell-comments-and-strings 或 ispell-comment-or-string-at-point 命令,仅检查注释或字符串字面量的拼写。
拼写错误的处理方式
当拼写检查命令检测到疑似拼写错误的单词时,会提示用户进行处理。程序通常会显示一份带编号的近似单词列表 —— 即与错误单词拼写相近的正确词汇。此时需输入单个字符来执行相应操作,有效操作指令如下:
digit- 数字键。仅本次将错误单词替换为列表中对应编号的近似单词。
SPC- 跳过当前单词 —— 标记其为拼写错误,但不做修改。
r new RET- 仅本次将错误单词替换为输入的 “新单词”(替换后的字符串会被重新扫描,排查是否存在其他拼写错误)。
R new RET- 将错误单词替换为输入的 “新单词”,并启动查询替换功能,可选择在缓冲区其他位置批量替换该单词(替换后的内容会被重新扫描)。
a- 接受当前单词 —— 在本次编辑会话中,将其视为正确拼写。
A- 接受当前单词 —— 在本次编辑会话及当前缓冲区内,将其视为正确拼写。
i- 将当前单词添加到个人词典文件中,此后即使重启 Emacs,该单词也会被识别为正确拼写。
m- 功能与
i类似,同时可指定词典补全信息。 u- 将当前单词的小写形式添加到个人词典文件中。
l word RET- 在词典中搜索与 “待查单词” 匹配的词汇,搜索结果将作为新的近似单词列表,可输入对应数字键选择替换。“待查单词” 中可使用 '
*' 作为通配符。 C-gX- 中断交互式拼写检查,将光标停留在正在检查的单词处。后续可通过
C-u M-$恢复检查。 x- 退出交互式拼写检查,并将光标返回至检查开始前的位置。
q- 退出交互式拼写检查,并终止拼写检查子进程。
C-r- 进入递归编辑模式(详见 “递归编辑层级” 章节)。按下
C-M-c退出递归编辑后,拼写检查会自动恢复。该模式可在不中断检查的前提下查看缓冲区文本,但禁止在递归编辑期间修改缓冲区内容,尤其不能改动疑似拼写错误的单词 —— 退出递归编辑时,所有修改都会被撤销。若需修改错误单词,应使用r或R指令,或按下X中断检查、编辑文本后,再用C-u M-$恢复。 - -
C-z - 挂起 Emacs 或最小化当前窗口。
- -
? - 显示所有操作指令的说明列表。
单词补全功能
使用 M-TAB (completion-at-point) 可补全光标所在位置的单词。输入单词前缀后按下 M-TAB ,即可从补全列表中选择所需词汇。若窗口管理器拦截了 M-TAB 快捷键,可改用 ESC TAB 或 C-M-i 。
拼写检查子进程管理
拼写检查子进程启动后会保持运行状态,等待后续操作,因此后续拼写检查命令的执行速度会更快。若需终止该进程,可执行 M-x ispell-kill-ispell 命令。通常无需手动终止,因为该进程仅在执行拼写纠错时占用系统资源,闲置时不会消耗处理器性能。
词典配置说明
拼写检查程序会从两类词典中查询拼写规则:标准词典与个人词典。
- 标准词典:由变量
ispell-local-dictionary指定;若该变量值为nil,则使用ispell-dictionary的值;若两个变量均为nil,则使用拼写程序的默认词典。执行M-x ispell-change-dictionary命令可设置当前缓冲区的标准词典,并重启子进程以应用新词典。 - 个人词典:由变量
ispell-personal-dictionary指定;若该变量值为空,拼写程序会在各程序的默认路径下查找个人词典。
拼写检查所用的词典通常对应特定语言,默认语言由系统环境与区域设置决定。若需检查其他语言文本的拼写,需同时修改标准词典与个人词典,可通过 ispell-change-dictionary 命令完成配置。
Hunspell 特殊功能:Hunspell 支持同时加载多本词典进行拼写检查。若需启用该功能,需在使用 Hunspell 检查混合语言文本前,执行 M-x ispell-hunspell-add-multi-dic 命令,按提示输入以逗号分隔的多语言词典名称列表(例如 'en_US' , 'de_DE' , 'ru_RU' )。配置完成后,检查混合上述语言的文本时无需频繁切换词典。
注意:若多种语言使用同一套书写系统,可能出现 “某单词在一种语言中拼写错误,但在另一语言词典中被判定为正确” 的情况,此时该拼写错误可能被遗漏。
单词补全词典
单词补全功能使用独立的词典,其文件名由变量 ispell-complete-word-dict 指定。补全词典需与拼写检查词典区分开,因为补全功能无法利用拼写检查所依赖的单词词根与词缀信息来识别单词变体。部分语言仅提供拼写检查词典,无对应的单词补全词典。
Flyspell Mode
Flyspell 模式是一种次要模式,可在输入文本时自动进行拼写检查。当检测到无法识别的单词时,会高亮标记该单词。执行 M-x flyspell-mode 可在当前缓冲区切换该模式的开关状态。若需在所有文本模式缓冲区中默认启用该模式,可将 flyspell-mode 添加至 text-mode-hook (详见 “钩子函数” 章节)。
注意:由于 Flyspell 模式需要检查光标经过的每个单词,可能会降低光标移动与滚动操作的速度。此外,该模式不会自动检查未输入或未经过的文本,需手动执行 flyspell-region 或 flyspell-buffer 命令进行检查。
默认情况下, Flyspell 模式会高亮标记所有输入、修改或光标经过的错误单词。若将变量 flyspell-check-changes 自定义为非空值,则仅检查输入或编辑过的单词。
当 Flyspell 模式高亮标记某个错误单词时,可通过以下方式纠错:
mouse-2鼠标中键点击该单词 (flyspell-correct-word) ,会弹出包含候选纠正词与操作选项的菜单;若需改为mouse-3鼠标右键弹出菜单,启用context-menu-mode即可。- 按下
C-.或ESC TAB(flyspell-auto-correct-word) ,会依次推荐多个候选纠正词。 - 按下
C-c $(flyspell-correct-word-before-point) ,会弹出候选纠正词菜单。当然,也可直接手动编辑单词进行纠错。
Flyspell Prog Mode
该模式的功能与普通 Flyspell 模式类似,但仅检查代码注释与字符串常量中的拼写,非常适合编辑程序代码。执行 M-x flyspell-prog-mode 可在当前缓冲区切换该模式的开关状态。若需在所有编程模式缓冲区中默认启用该模式,可将 flyspell-prog-mode 添加至 prog-mode-hook (详见 “钩子函数” 章节)。
19. 键盘宏(Keyboard Macros)
本章将介绍如何记录一系列编辑命令,以便后续便捷地重复执行。
keyboard macro 键盘宏是由 Emacs 用户自定义的命令,用于替代另一组按键序列。例如,若你需要连续四十次输入 C-n M-d C-d 这组操作,可自定义一个执行该操作的键盘宏,随后只需再调用此宏 39 次,即可大幅提升操作效率。
定义键盘宏的方式,是通过执行并记录构成该宏的一系列命令。换言之,在定义键盘宏的过程中,其对应的操作会被首次执行。通过这种方式,你能直观看到各命令的执行效果,无需在脑中推演操作结果。完成宏的定义后,该键盘宏即被创建,且实际上已执行过一次;后续只需调用此宏,就能重复执行整套操作。
键盘宏与 Emacs 普通命令的区别在于:键盘宏基于 Emacs 命令语言编写,而非 Lisp 语言。这让新手能更轻松地编写键盘宏,也让它作为临时快捷操作时更易用。但 Emacs 命令语言的编程能力有限,无法编写具备智能逻辑或通用型的功能,这类需求则需要使用 Lisp 语言实现。
19.1. 基本使用
F3- 开始定义键盘宏 (
kmacro-start-macro-or-insert-counter) 。 F4- 若正处于键盘宏定义状态,则结束定义;否则,执行最近定义的键盘宏 (
kmacro-end-or-call-macro) 。 C-u F3- 重新执行上一个键盘宏,随后将后续按键追加至该宏的定义中。
C-u C-u F3- 直接将后续按键追加至上一个键盘宏的定义,不重新执行该宏。
C-x C-k r- 在区域内所有行首执行最近定义的键盘宏 (
apply-macro-to-region-lines) 。 C-x (- 开始定义键盘宏(旧式快捷键,对应函数
kmacro-start-macro);带前缀参数时,将后续操作追加至上一个宏的定义。 C-x )- 结束键盘宏定义(旧式快捷键,对应函数
kmacro-end-macro);前缀参数作为该宏的执行重复次数。 C-x e- 执行最近定义的键盘宏 (
kmacro-end-and-call-macro) ;前缀参数作为执行重复次数。
定义键盘宏时,按下 F3 即可启动。此后你输入的所有按键,会在正常执行操作的同时,被记录为键盘宏的定义内容。此时模式行中会显示 'Def' 标识,提醒当前处于宏定义状态。定义完成后,按下 F4 (kmacro-end-or-call-macro) 即可终止宏定义。例如:
F3 M-f foo F4
该操作会定义一个键盘宏,功能为:向前移动一个单词,然后插入文本「foo」。注意, F3 和 F4 本身不会被纳入宏的定义内容。
宏定义完成后,按下 F4 即可调用该宏。对于上述示例,调用宏的效果与直接输入 M-f foo 完全一致。( 需 注意 F4 命令的双重作用 :若处于宏定义过程中,它是结束定义的快捷键;若未处于定义状态,则用于调用最近的键盘宏。)你也可以为 F4 指定数字前缀参数n,表示将该宏连续执行 n 次。若前缀参数为 0,宏会无限重复执行,直至触发错误或你按下 C-g 终止(在 MS-DOS 系统中为 C-Break )。
上述示例体现了键盘宏的一个实用技巧:若你需要在文本中按固定间隔重复某一操作,可将 移动类命令 纳入宏的定义。在该示例中,重复调用宏会在每一个后续单词后插入字符串「foo」,实现按间隔批量操作。
终止键盘宏定义后,按下 C-u F3 可将后续按键追加至该宏的定义末尾。该操作等效于重新按下 F3 启动定义,并手动重新输入宏的原有所有内容,因此会先重新执行一次该宏的原有定义。若将变量 kmacro-execute-before-append 的值设为 nil ,则追加定义前不会重新执行原有宏(该变量默认值为 t )。若想直接追加定义而不执行原有宏,可按下 C-u C-u F3 。
当宏定义中包含需要通过迷你缓冲区读取参数的命令时,你在迷你缓冲区中输入的内容,会与该命令一同被记录到宏定义中。因此回放该宏时,该命令会使用定义宏时输入的相同参数。例如:
F3 C-a C-k C-x b foo RET C-y C-x b RET F4
该操作定义的键盘宏,功能为:删除当前行,将其粘贴至缓冲区「foo」,随后返回原缓冲区。
绝大多数键盘命令在宏定义中均可正常使用,仅存在少量例外:
- 按下
C-g(keyboard-quit) 会直接终止当前的键盘宏定义; - 按下
C-M-c(exit-recursive-edit) 的行为不可靠:若退出的是宏定义过程中启动的递归编辑,其行为符合预期;但若退出的是调用键盘宏前就已启动的递归编辑,该操作会同时终止键盘宏的执行; - 鼠标事件同样存在不确定性:尽管宏定义中可以记录鼠标操作,但回放宏时,鼠标事件会使用 定义宏时的原始鼠标位置 ,而非回放时的鼠标位置,其执行效果难以预测。
命令 C-x C-k r (apply-macro-to-region-lines) 的作用是,在选中区域内 所有行的行首 执行最近定义的键盘宏。该命令会逐行执行:先将光标移至行首,再调用键盘宏。
除上述 F3 和 F4 的新式快捷键外,Emacs 还支持一套定义和执行键盘宏的 旧式快捷键 :
C-x ((kmacro-start-macro) 启动宏定义,与F3一致,带前缀参数时会将操作追加至上一个宏;C-x )(kmacro-end-macro) 结束宏定义;C-x e(kmacro-end-and-call-macro) 执行最近定义的宏。
若在宏定义过程中按下 C-x e ,会直接终止定义并立即执行该宏。在首次按下 C-x e 后,可连续按下 e 键,多次重复执行该宏;也可像 F4 一样,为 C-x e 指定前缀参数,作为宏的执行重复次数。
为 C-x ) 指定数字前缀参数时,表示定义完成后立即重复执行该宏。由于宏的定义过程本身就是首次执行,因此 C-u 4 C-x ) 表示在定义完成后,再额外执行该宏 3 次。
执行耗时较长的键盘宏时,有时需要手动触发屏幕重绘,以查看宏的执行进度,此时可使用命令 C-x C-k d 。举一个简单的示例: C-x ( M-f C-x C-k d C-x ) 定义的宏,在执行 C-u 42 C-x e 时,会在每次迭代中触发一次屏幕重绘。
19.2. 键盘宏环(Keyboard Macro Ring)
所有已定义的键盘宏都会记录在 键盘宏环 中。Emacs 中仅有一个键盘宏环,供所有缓冲区共享使用。
C-x C-k C-k- 执行宏环首位置的键盘宏 (
kmacro-end-or-call-macro-repeat) 。 C-x C-k C-n- 轮换键盘宏环至下一个宏(更早定义的宏)(
kmacro-cycle-ring-next) 。 C-x C-k C-p- 轮换键盘宏环至上一个宏(更晚定义的宏)(
kmacro-cycle-ring-previous) 。
所有对键盘宏环进行操作的命令,均使用统一的 C-x C-k 前缀。这类命令中的绝大多数,可连续执行和重复调用,无需再次输入 C-x C-k 前缀。例如:
C-x C-k C-p C-p C-k C-k C-k C-n C-n C-k C-p C-k C-d
该操作的执行逻辑为:将键盘宏环轮换至倒数第二个宏,执行当前宏环首位置的该宏三次,轮换回宏环原本的首位置宏并执行一次,再轮换至上一个宏并执行,最后将该宏从宏环中删除。
命令 C-x C-k C-k (kmacro-end-or-call-macro-repeat) 用于执行宏环首位置的键盘宏。执行后可直接按下 C-k 重复调用该宏,也可直接按下 C-n 或 C-p 对宏环进行轮换操作。
若正处于键盘宏定义状态, C-x C-k C-k 的功能与 F4 一致,区别在于执行该命令后,可直接使用本节的大部分快捷键,无需再次输入 C-x C-k 前缀。例如,直接按下 C-k 即可重新执行该宏。
命令 C-x C-k C-n (kmacro-cycle-ring-next) 和 C-x C-k C-p (kmacro-cycle-ring-previous) 用于轮换键盘宏环,将下一个 / 上一个键盘宏移至宏环的首位置,新的宏环首位置宏的定义内容会显示在回显区中。操作后可直接连续按下 C-n 或 C-p 继续轮换宏环,直至目标宏出现在宏环首位置;若要立即执行新的宏环首位置宏,直接按下 C-k 即可。
请注意,Emacs 将 宏环首位置的宏 视作最近定义的键盘宏。例如,按下 F4 会执行该宏,执行 C-x C-k n 可为该宏命名。
键盘宏环中可存储的宏的最大数量,由可自定义变量 kmacro-ring-max 决定。
19.3. 键盘宏计数器
每个键盘宏都配有一个关联的 计数器 ,启动宏定义时,该计数器会初始化为 0。借助这个当前计数器,你可以在缓冲区中插入一个数值,该数值会根据宏的调用次数动态变化。默认情况下,每当计数器的数值被插入缓冲区时,其值会自动递增。
除当前计数器外,键盘宏还会维护一个 历史计数器 ,用于记录当前计数器上一次被递增或设置时的数值。请注意,若将当前计数器的递增值设为 0(例如执行 C-u 0 C-x C-k C-i ),当前计数器的数值也会被记录为历史计数器的数值。
F3- 在键盘宏定义过程中,将键盘宏计数器的当前值插入缓冲区 (
kmacro-start-macro-or-insert-counter) 。 C-x C-k C-i- 将键盘宏计数器的当前值插入缓冲区 (
kmacro-insert-counter) 。 C-x C-k C-c- 设置键盘宏计数器的数值 (
kmacro-set-counter) 。 C-x C-k C-a- 将前缀参数的值累加至键盘宏计数器 (
kmacro-add-counter) 。 C-x C-k C-f- 指定键盘宏计数器的插入格式 (
kmacro-set-format) 。
在定义键盘宏时,执行命令 F3 (kmacro-start-macro-or-insert-counter) 会将该宏计数器的当前值插入缓冲区,且计数器值自动加 1。(若未处于宏定义状态, F3 的功能为启动宏定义,详见「基础使用」章节。)你可以通过指定数字前缀参数,设置非 1 的自定义递增值;若仅指定 C-u 作为前缀,该操作会插入 历史计数器 的数值,且不会修改当前计数器的数值。
以下举例说明如何使用键盘宏计数器制作带编号的列表,执行如下按键序列即可实现:
F3 C-a F3 . SPC F4
在该键盘宏的定义过程中,字符串 '0. ' 会被插入当前行首。若此时将光标移至缓冲区其他位置,按下 F4 调用该宏,目标行首会插入字符串 '1. ';后续每一次调用宏,会依次插入'2. '、'3. ',依此类推。
命令 C-x C-k C-i (kmacro-insert-counter) 的功能与宏定义中的 F3 完全一致,且可在 宏定义之外 的场景使用。当未定义或执行任何键盘宏时,该命令会插入 键盘宏环首位置 对应宏的计数器值,并完成计数器的递增。
命令 C-x C-k C-c (kmacro-set-counter) 可将当前宏计数器的数值设为前缀参数指定的数字。若在宏内部执行该命令,其设置会作用于宏的每一次重复执行;若在宏执行过程中仅指定 C-u 作为前缀,该操作会将计数器重置为 本次宏迭代开始 时的数值,撤销本次迭代中对计数器的所有递增操作。
命令 C-x C-k C-a (kmacro-add-counter) 会将前缀参数的数值累加至当前宏计数器。若仅指定 C-u 作为参数,该操作会将计数器重置为 任意键盘宏上一次插入的数值 (通常情况下,该数值为当前宏上一次插入的计数器值)。
命令 C-x C-k C-f (kmacro-set-format) 会通过迷你缓冲区提示你输入计数器的 插入格式字符串 ,默认格式为 '%d' ,表示以无补位的十进制形式插入数字。若在迷你缓冲区中直接回车,可将格式重置为该默认值。你可以输入 format 函数支持的任意格式字符串,且该字符串需适配 单个整数 作为额外参数的使用场景(详见《Emacs Lisp 参考手册》中的「格式化字符串」章节)。在迷你缓冲区中输入格式字符串时, 无需添加双引号 。
若在未定义或执行任何键盘宏时执行该命令,新设置的格式会作用于 后续所有新建的宏 ,已存在的宏仍会使用其定义时生效的格式;若在定义键盘宏的过程中设置格式,该设置仅对 当前正在定义的宏 从设置节点开始生效,不会影响其他宏。宏执行时,每一步的计数器插入都会使用 其定义时对应步骤生效的格式 ;在宏执行过程中修改计数器格式,与在宏定义过程中修改的效果一致,均不会影响其他宏。
通过 C-x C-k C-f 设置的格式, 不会影响 寄存器中存储数值的插入方式。
你也可以将寄存器用作计数器,在宏的每一次重复执行中实现递增,其效果与键盘宏计数器完全一致(详见「在寄存器中保存数字」章节)。但对于绝大多数使用场景,直接使用键盘宏计数器会更简便。
19.4. 带变量的宏执行(Executing Macros with Variations)
在键盘宏中,你可以实现类似 query-replace 查询替换的效果,让宏在每次执行时都向你确认是否执行修改操作。
C-x q- 宏执行至该位置时,弹出确认提示 (
kbd-macro-query) 。
定义宏时,在需要触发确认的位置按下 C-x q 即可。宏定义过程中,该按键不会产生任何效果;但后续执行该宏时,运行到该位置会弹出交互式确认,询问是否继续执行。
当 C-x q 触发确认时,可输入以下有效响应:
SPC(ory)- 继续执行该键盘宏。
DEL(orn)- 跳过本次宏迭代的剩余操作,直接开始下一次迭代。
RET(orq)- 跳过本次宏迭代的剩余操作,并取消后续所有宏迭代。
C-r- 进入递归编辑模式,可在该模式下执行非宏定义内的编辑操作。使用
C-M-c退出递归编辑后,会再次弹出宏执行的确认提示;此时按下SPC空格,将继续执行该宏的剩余定义操作。你需要自行保证退出递归编辑时,光标和文本的状态能让宏的后续操作达到预期效果。
带前缀参数的 C-u C-x q ,功能与普通的 C-x q 完全不同。无论是在 宏定义过程中 按下该组合键,还是在 宏执行过程中 触发该操作,都会进入递归编辑模式并从键盘读取输入。
- 宏定义阶段:在该递归编辑中执行的操作, 不会被记录到宏定义 中;
- 宏执行阶段:该递归编辑为每次宏迭代提供了执行 个性化编辑操作 的机会。详见「递归编辑层级」章节。
19.5. 键盘宏的命名与保存
C-x C-k n- 为最近定义的键盘宏指定命令名(有效期为当前 Emacs 会话) (
kmacro-name-last-macro) 。 C-x C-k b- 将最近定义的键盘宏绑定至某一按键序列(有效期为当前会话)(
kmacro-bind-to-key) 。 M-x insert-kbd-macro- 将键盘宏的定义以 Lisp 代码形式插入缓冲区。
若你希望保存键盘宏以供后续使用,可通过 C-x C-k n (kmacro-name-last-macro) 为其命名。该命令会通过迷你缓冲区读取一个名称作为参数,并将该名称定义为执行当前形式的最新键盘宏的指令(若后续你为该宏追加了新的定义内容,此命名对应的宏定义 不会 随之改变)。宏的名称为 Lisp 符号,通过此方式定义后,该名称将成为有效的命令名,可通过 M-x 调用,也可通过 keymap-global-set 为其绑定按键(详见「键盘映射」章节)。若你指定的名称已有非键盘宏类型的原有定义,系统会弹出错误提示,且不会对原有设置做任何修改。
你也可使用 C-x C-k b (kmacro-bind-to-key) 将最新的键盘宏(当前形式)绑定至指定按键序列,执行该命令后输入想要绑定的按键序列即可。该命令可将宏绑定至全局键盘映射中的任意按键序列,但由于多数按键序列已有其他绑定功能,你需要谨慎选择。若你尝试绑定的按键序列在任意键盘映射中已有绑定,该命令会在替换原有绑定前向你确认。
为避免因覆盖原有绑定引发问题, C-x C-k 0 至 C-x C-k 9 以及 C-x C-k A 至 C-x C-k Z 这些按键序列被预留出来,专供你绑定自定义键盘宏使用。实际上,绑定这类预留按键序列时,你只需输入对应的数字或字母,无需输入完整的按键序列。例如:
C-x C-k b 4
该操作会将最新的键盘宏绑定至按键序列 C-x C-k 4 。
当键盘宏拥有命令名后,你可将其定义保存至文件中,以便在其他编辑会话中使用。操作步骤如下:
- 打开你想要保存宏定义的文件;
- 执行命令:
M-x insert-kbd-macro RET macroname RET
该命令会在缓冲区中插入一段 Lisp 代码,这段代码在后续执行时,会定义出与当前宏完全一致的键盘宏(你无需理解 Lisp 代码,因为 insert-kbd-macro 会自动为你生成对应的代码)。之后保存该文件即可,后续可通过 load-file 命令加载此文件(详见「Emacs 的 Lisp 代码库」章节)。若你将宏定义保存至初始化文件 ~/.emacs 中(详见「Emacs 初始化文件」章节),则每次启动 Emacs 时,该宏都会被自动定义。
若为 insert-kbd-macro 指定前缀参数,该命令会额外生成一段 Lisp 代码,记录你为该宏名绑定的所有按键(若有),这样在你加载文件时,该宏会被重新分配至原有绑定的按键
19.6. 编辑键盘宏
C-x C-k C-e- 编辑最近定义的键盘宏 (
kmacro-edit-macro) 。 C-x C-k e 宏名 RET- 编辑已定义的指定名称的键盘宏 (
edit-kbd-macro) 。 C-x C-k l- 将最近的 300 次按键操作作为键盘宏进行编辑 (
kmacro-edit-lossage) 。
按下 C-x C-k C-e 或 C-x C-k RET (kmacro-edit-macro) ,即可编辑最近定义的键盘宏。该命令会将宏的定义格式化后展示在专属缓冲区中,并进入编辑宏的专用主模式。在该缓冲区中按下 C-h m ,可查看编辑宏的详细操作说明;编辑完成后,按下 C-c C-c 即可保存修改并退出编辑。
kmacro-edit-macro 所使用的 edmacro-mode (宏编辑主模式)提供了一系列便捷命令,用于编辑格式化后的宏定义:按下 C-c C-q (edmacro-insert-key) ,可将后续输入的按键序列按正确格式插入缓冲区,功能与 C-q (quoted-insert) 类似;按下 C-c C-r (edmacro-set-macro-to-region-lines) ,可将选中区域的文本替换为宏的定义内容。若选中的区域并非从行首开始或至行尾结束,该命令会自动扩展区域以包含完整行;若区域结束于某行的行首,则该最后一行不会被纳入替换范围。
若要编辑已命名的键盘宏,或绑定至按键的键盘宏,可按下 C-x C-k e (edit-kbd-macro) ,随后输入调用该宏的方式即可 —— 无论是 C-x e 、 M-x name ,还是其他绑定的按键序列均可。
按下 C-x C-k l (kmacro-edit-lossage) ,可将最近的 300 次按键操作提取出来,作为键盘宏进行编辑。默认情况下,最新的按键操作会显示在缓冲区的末尾;若将变量 edmacro-reverse-macro-lines 设为 t ,宏的按键序列会以倒序形式展示。
19.7. 逐步编辑键盘宏(Stepwise Editing a Keyboard Macro)
按下 C-x C-k SPC (kmacro-step-edit-macro) ,来交互式地逐步重放并编辑上一个键盘宏,每次处理一条命令。除非你按下 q 或 C-g 退出宏编辑,否则编辑后的宏将替换宏环中最新的那个宏。
该宏编辑功能会在迷你缓冲区中显示最新的宏定义,同时标注即将执行的第一条(或下一条)命令,并弹出提示让你选择执行操作。输入 '?' 可查看所有操作选项的说明,可用操作如下:
SPC、y- 执行当前命令,并跳至键盘宏中的下一条命令。
n、d、DEL- 跳过并删除当前命令。
f- 在本次宏执行中跳过当前命令,但不将其从宏定义中删除。
TAB- 执行当前命令,同时执行紧随其后的所有同类命令;例如,可使用
TAB执行一串字符的插入操作(对应连续的自插入命令)。 c- 继续执行宏(不再进行后续编辑),直至宏定义结束。若执行正常终止,编辑后的宏将替换原宏。
C-k- 跳过并删除键盘宏中剩余的所有命令,终止逐步编辑,并用编辑后的宏替换原宏。
q、C-g- 取消键盘宏的逐步编辑,丢弃对宏所做的所有修改。
i key.. C-j- 读取并执行一串按键序列(不包含最后的
C-j),将其插入到键盘宏中当前命令的前方,且不跳至下一条命令。 I key..- 读取并执行一个按键序列,将其插入到键盘宏中当前命令的前方,且不跳至下一条命令。
r key.. C-j- 读取并执行一串按键序列(不包含最后的
C-j),用其替换键盘宏中的当前命令,并跳至插入的按键序列之后。 R key..- 读取并执行一个按键序列,用其替换键盘宏中的当前命令,并跳至插入的按键序列之后。
a key.. C-j- 先执行当前命令,再读取并执行一串按键序列(不包含最后的
C-j),将其插入到键盘宏中当前命令的后方;随后跳至当前命令与插入序列之后的下一条命令。 A key.. C-j- 先执行键盘宏中剩余的所有命令,再读取并执行一串按键序列(不包含最后的
C-j),将其追加至键盘宏的末尾;随后终止逐步编辑,并用编辑后的宏替换原宏。
19.8. 列出与编辑键盘宏
执行命令 M-x list-keyboard-macros RET ,即可显示现有所有键盘宏的列表。该命令会在名为 *Keyboard Macro List* 的缓冲区中弹出 Kmacro Menu 宏操作菜单 ,列表中每一行对应一个宏,展示其位置编号、计数器值、计数器格式、格式化后的计数值以及宏的按键序列。以下是宏列表的示例:
Position Counter Format Formatted Keys 0 8 %02d 08 N : SPC <F3> RET 1 0 %d 0 l o n g SPC p h r a s e
宏列表的排序规则为:当前最新的宏排在顶部,位置编号为 0,其余旧宏按 键盘宏环 中的存储顺序依次排列(详见「键盘宏环」章节)。通过该宏操作菜单,你可调整宏的排序、修改宏的计数器值、计数器格式及按键序列。此菜单所在的缓冲区为只读模式,仅能通过本节介绍的专用命令进行修改。执行任意修改命令后,宏操作菜单会实时刷新,展示宏属性和宏环的最新状态。在该缓冲区中,既可使用 Emacs 常规的光标移动命令,也可使用菜单专属的导航命令;按下 C-h m 或 '?' (describe-mode) ,即可查看所有专用命令的说明。
你可使用以下命令修改宏的属性(操作时光标置于对应宏的行上即可):
#- 修改当前行对应宏在宏环中的位置编号(详见「键盘宏环」章节)。
C-x C-t- 将当前行的宏上移一行,功能与文本行交换命令
transpose-lines一致。 c- 修改当前行对应宏的计数器值(详见「键盘宏计数器」章节)。
f- 修改当前行对应宏的计数器格式。
e- 调用
edit-kbd-macro命令,编辑当前行对应宏的按键序列(详见「编辑键盘宏」章节)。 RET- 修改当前光标所在列对应的宏属性,自动调用上述对应属性的修改命令。
以下命令用于在列表中删除或复制宏:
d- d(宏操作菜单专用)
- 为当前行的宏标记删除标识,随后光标移至下一行 (
kmacro-menu-flag-for-deletion) 。行首出现字符D即表示标记成功,标记的宏仅在执行x命令后才会被真正删除(见下文)。若当前存在激活的选中区域,该命令会为区域内所有宏标记删除标识。 x- x (宏操作菜单专用)
- 删除列表中所有已标记删除标识的宏 (
kmacro-menu-do-flagged-delete) 。 m- m(宏操作菜单专用)
- 为当前行的宏添加标记,随后光标移至下一行 (
kmacro-menu-mark) 。行首出现字符 '*' 即表示标记成功,已标记的宏可被C和D命令批量操作(见下文)。若当前存在激活的选中区域,该命令会为区域内所有宏添加标记。 C- C(宏操作菜单专用)
- 复制宏,在宏当前的列表位置插入副本 (
kmacro-menu-do-copy) 。例如,对位置 0 的宏执行该命令,会在位置 1 插入其副本,原位置 1 及之后的宏均顺次下移。若存在激活的选中区域,批量复制区域内所有宏;若无选中区域但有已标记的宏,批量复制所有标记宏;若既无选中区域也无标记宏,仅复制当前行的宏。前两种批量复制的场景下,命令会先弹出确认提示,确认后再执行复制。 D- D(宏操作菜单专用)
删除宏并将其从宏环中移除 (
kmacro-menu-do-delete) 。例如,对位置 0 的宏执行该命令,会删除该宏,原位置 1 的宏将成为新的当前宏并移至位置 0,同时从宏环中弹出。若存在激活的选中区域,批量删除区域内所有宏;若无选中区域但有已标记的宏,批量删除所有标记宏;若既无选中区域也无标记宏,仅删除当前行的宏。所有场景下,命令都会先弹出确认提示,确认后再执行删除。
该命令是
d和x删除方式的替代方案。u- u(宏操作菜单专用)
- 取消当前行宏的标记和删除标识,随后光标下移至下一行 (
kmacro-menu-unmark) 。若存在激活的选中区域,取消区域内所有宏的标记和删除标识。 DEL- DEL (宏操作菜单专用)
- 功能与
u命令一致(见上文);若当前无激活的选中区域,操作后光标会上移至上一行 (kmacro-menu-unmark-backward) 。 U- U(宏操作菜单专用)
- 取消列表中所有宏的标记和删除标识 (
kmacro-menu-unmark-all) 。
20. 文件处理
操作系统将数据永久存储在命名 files文件 中,因此你使用 Emacs 编辑的大部分文本都来自文件,最终也会保存到文件中。
要编辑文件,你需要让 Emacs 读取该文件并创建一个包含文件文本副本的缓冲区,这一操作称为 visiting 访问 文件。编辑命令直接作用于缓冲区中的文本,即 Emacs 内部的这份副本;只有当你将缓冲区内容回存到文件时,所做的修改才会体现在文件本身中。
除了访问和保存文件,Emacs 还支持删除、复制、重命名、追加文件内容,保留文件的多个版本,以及对文件目录进行操作。
20.1. 文件名
Emacs 中许多对文件执行操作的命令,都需要你通过迷你缓冲区指定文件名(参见《文件名与迷你缓冲区》章节)。
在迷你缓冲区中,你可以使用常规的补全和历史记录命令(参见《迷你缓冲区》章节)。注意,文件名补全会忽略扩展名出现在变量 completion-ignored-extensions 中的文件(参见《补全选项》章节)。此外,大多数读取文件名的命令会采用 带确认的宽松补全 方式:你可以输入不存在的文件名,但如果在补全到一个不存在的文件名后直接按下 RET 回车键,Emacs 会显示 '[Confirm 确认]',你需要再次按下 RET 回车键完成确认。详细说明参见《补全的退出方式》章节。
迷你缓冲区的历史记录命令在读取文件名时提供了一些特殊功能,参见《迷你缓冲区历史记录》章节。
每个缓冲区都有一个 default directory默认目录 ,存储在缓冲区局部变量 default-directory 中。每当 Emacs 通过迷你缓冲区读取文件名时,通常会将默认目录作为初始内容插入到迷你缓冲区中。你可以将变量 insert-default-directory 设为nil,禁止该插入行为(参见《文件名与迷你缓冲区》章节)。无论是否插入默认目录,Emacs 始终将所有相对文件名解析为相对于默认目录的路径,例如,输入不带目录的文件名,即表示默认目录下的该文件。
当你访问一个文件时,Emacs 会将该访问缓冲区的 default-directory 设为该文件所在的目录。当你通过 C-x b 这类命令创建一个未访问任何文件的新缓冲区时,其默认目录通常会从创建时的当前缓冲区复制而来(参见《缓冲区的创建与选择》章节)。你可以使用命令 M-x pwd 查看当前缓冲区的 default-directory 值。命令 M-x cd 会提示你输入目录名,并将当前缓冲区的 default-directory 设为该目录(此操作不会改变缓冲区对应的文件名,若有)。
举个例子,当你访问文件 /u/rms/gnu/gnu.tasks 时,默认目录会被设为 /u/rms/gnu/ 。此时若调用一个读取文件名的命令,在迷你缓冲区中仅输入 'foo' (省略目录),表示文件 /u/rms/gnu/foo ;输入 '../.login' ,表示文件 /u/rms/.login ;输入 'new/foo' ,表示文件 /u/rms/gnu/new/foo 。
在迷你缓冲区中输入文件名时,你可以使用两个快捷方式: //双斜杠 会忽略该双斜杠中第二个斜杠之前的所有内容, '~/' 则代表你的 主目录 。参见《文件名与迷你缓冲区》章节。
字符 '$' 用于在文件名中 替换环境变量 。环境变量名由 '$' 之后的所有字母数字字符组成;你也可以将环境变量名用大括号包裹后跟在 '$' 之后。例如,若你通过 Shell 命令输入 export FOO=rms/hacks 设置了名为 FOO 的环境变量,那么 /u/$FOO/test.c 和 /u/${FOO}/test.c 均是 /u/rms/hacks/test.c 的简写形式。如果该环境变量未定义,则不会进行替换,字符 '$' 将按原义保留。注意,在 Emacs 外部设置的环境变量,只有在 Emacs 启动前生效,才会对 Emacs 产生影响。
若要访问的文件名中包含 '$' 字符,且并且你不希望它被当作环境变量展开,可输入 '$$' 。在对单个 '$' 执行变量替换的同时,这 2 个 '$' 会被转换为 1 个 '$' 。你也可以使用 '/:' 将整个文件名引起来(参见《引用文件名》章节)。以字面量 '~' 开头的文件名,同样需要用 '/:' 引用。
你可以在文件名中包含非 ASCII 字符,参见《文件名的编码系统》章节。
20.2. 打开文件(Visiting Files)
C-x C-f- 访问文件 (
find-file) - (no term)
C-x C-r::以只读方式访问文件,禁止修改 (find-file-read-only)C-x C-v- 替换访问另一个文件,替代上一次访问的文件 (
find-alternate-file) C-x 4 f- 在另一个窗口中访问文件 (
find-file-other-window) ,不改变当前选中窗口的显示内容 C-x 5 f- 在新框架中访问文件 (
find-file-other-frame) ,不改变当前选中框架的显示内容 M-x find-file-literally- 以无格式转换的方式访问文件,不处理文件内容的任何转换
访问文件指将文件内容读取到 Emacs 缓冲区中,以便进行编辑操作。Emacs 会为每个访问的文件创建一个新的缓冲区。
要访问文件,输入 C-x C-f (find-file) ,并通过迷你缓冲区输入目标文件名。在迷你缓冲区中,可输入 C-g 终止该命令。关于在迷你缓冲区中输入文件名的详细说明,参见《文件名》章节。
若指定的文件存在但系统禁止你读取,回显区会显示错误信息(在 GNU 和 Unix 系统中,你可通过 su 或 sudo 方式访问此类文件,参见《远程文件》章节)。若访问成功,屏幕会显示文件的新文本,模式行也会显示对应的缓冲区名称(参见《模式行》章节)。Emacs 通常会从文件名中提取缓冲区名,省略目录部分,例如访问文件 /usr/rms/emacs.tex 时,对应的缓冲区名为 emacs.tex 。若该名称的缓冲区已存在,Emacs 会生成一个唯一名称,默认方式是添加基于目录名的后缀(如 '<rms>' 、 '<tmp>' 等),你也可选择其他命名方式,参见《让缓冲区名唯一》章节。
创建新文件可直接使用相同的命令 C-x C-f ,只需输入新的文件名即可。此时回显区会显示 '(New file)' ,其余操作与访问一个已存在的空文件完全一致。
访问文件后,所有编辑命令产生的修改都仅作用于 Emacs 缓冲区,直到你保存缓冲区,修改才会同步到实际的文件中(参见《保存文件》章节)。若缓冲区包含未保存的修改,我们称该缓冲区已修改,此时若未保存就关闭缓冲区,修改将会丢失。模式行左侧的两个星号 '**' 是缓冲区已修改的标识。
若你访问的文件已在 Emacs 中打开, C-x C-f 不会创建新的缓冲区副本,而是直接切换到已存在的缓冲区。在此之前,Emacs 会检查该文件自上次访问或保存后是否被外部修改,若已修改,会提示你重新读取文件内容。
若你尝试访问的文件大小超过变量 large-file-warning-threshold 的取值(默认值为 10000000,约 10MB),Emacs 会先请求你的确认。输入 y 可继续正常访问文件,输入 l 则以无格式转换方式访问文件(详见下文)。以无格式转换方式访问大文件能提升导航和编辑速度,因为该方式会关闭各类可能占用大量资源的功能。请注意,Emacs 无法访问超过 最大缓冲区 大小的文件,该限制由 Emacs 可分配的内存空间和可表示的整数范围决定(参见《使用多个缓冲区》章节),若尝试访问,回显区会显示 “超出最大缓冲区大小” 的错误信息。
若你尝试访问的文件,其对应的主模式(参见《主模式》章节)会使用树分析解析库(tree-sitter),当文件字节大小超过变量 treesit-max-buffer-size 的取值时,Emacs 会发出警告。该变量默认值:64 位 Emacs 为 40MB,32 位 Emacs 为 15MB。此限制是为了避免 Emacs 内存耗尽,因为大缓冲区中基于 tree-sitter 的主模式会被禁用 —— 典型的 tree-sitter 解析器所需的内存约为其解析文本大小的 10 倍。
若你输入的文件名包含 Shell 风格的通配符,Emacs 会访问所有匹配该通配符的文件(在不区分大小写的文件系统中,通配符匹配会忽略字母大小写)。通配符包括 '?' 、 '*' 和 '[…]' 序列。若要在迷你缓冲区中输入作为文件名一部分的通配符 '?' ,需输入 C-q ? 进行转义。若文件名本身包含通配符,关于其访问方法参见《引用文件名》章节。你可通过自定义变量 find-file-wildcards 禁用通配符匹配功能。
若你尝试访问的文件已在某个缓冲区中打开,但该文件被外部程序修改,Emacs 通常会询问你是否从磁盘重新读取文件。若你将变量 query-about-changed-file 设为 nil ,Emacs 不会发出询问,而是继续显示缓冲区中修改前的内容,并在回显区提示你如何从文件恢复缓冲区内容。
若你因输错文件名意外访问了错误的文件,可输入 C-x C-v (find-alternate-file) 访问实际需要的文件。 C-x C-v 与 C-x C-f 功能类似,但会先关闭当前缓冲区(若缓冲区已修改,会先提示你保存)。当 C-x C-v 读取待访问的文件名时,会将完整的默认文件名插入迷你缓冲区,且光标定位在目录部分之后,若你仅输错了文件名的小部分,该方式会非常便捷。
若你尝试访问的文件实际是一个目录,Emacs 会调用目录编辑器 Dired(Emacs 目录浏览器),参见《目录编辑器 Dired》章节。你可将变量 find-file-run-dired 设为 nil 禁用该行为,此时尝试访问目录会被视为错误操作。
对于实际为文件集合的归档文件,Emacs 会以特殊模式打开,该模式会调用类 Dired 的操作环境,支持对归档内的成员文件进行操作,关于该功能的详细说明参见《文件归档》章节。
若你访问的文件被操作系统禁止修改,或被标记为只读,Emacs 会将对应的缓冲区也设为只读,避免你进行修改后无法保存。你可使用 C-x C-q (read-only-mode) 将只读缓冲区改为可写,参见《各类缓冲区操作》章节。
若你希望以只读方式访问文件,防止意外修改,可使用 C-x C-r (find-file-read-only) 替代=C-x C-f= 。
C-x 4 f (find-file-other-window) 与 C-x C-f 功能类似,区别在于该命令会在 另一个窗口 中选中目标文件的缓冲区,执行该命令前的当前窗口会保持原有缓冲区的显示。若执行该命令时仅存在一个窗口,Emacs 会将该窗口拆分为两个,一个窗口保持原有内容,另一个窗口显示新访问的文件,参见《多个窗口》章节。
C-x 5 f (find-file-other-frame) 与上述命令类似,区别在于该命令会在 新框架 中打开文件,或选中已显示该文件的现有框架,参见《框架与图形化显示》章节。
在图形化显示界面中,还有两种额外的文件访问方式:
- 若 Emacs 基于合适的 GUI 工具包编译,通过鼠标调用的命令(点击菜单栏或工具栏)会使用工具包的标准文件选择对话框,而非通过迷你缓冲区提示输入文件名。在 GNU/Linux 和 Unix 平台,基于 GTK+、LessTif 和 Motif 工具包编译的 Emacs 支持该功能;在 MS-Windows 和 Mac 系统,GUI 版本的 Emacs 默认支持该功能。关于该功能的自定义方法,参见《使用对话框》章节。
- Emacs 支持拖放操作 :将文件拖入普通的 Emacs 窗口,该窗口会直接访问该文件。例外情况:将文件拖入显示 Dired 缓冲区的窗口,会将该文件移动或复制到该窗口显示的目录中。详细说明参见《拖放操作》和《Dired 的其他功能》章节。
在文本模式终端中,或在未基于 GUI 工具包编译的 Emacs 图形化显示界面中,你可通过菜单栏的「文件(File)」菜单访问文件,该菜单包含「访问新文件(Visit New File)」和「打开文件(Open File)」选项。
每次访问文件时,Emacs 会自动扫描文件内容,检测其使用的字符编码和行尾格式,并在缓冲区中将其转换为 Emacs 内部的编码和行尾格式。当你保存缓冲区时,Emacs 会执行反向转换,将文件以原始的编码和行尾格式写入磁盘,参见《编码系统》章节。
若你希望将文件作为纯 ASCII 字符序列编辑,不进行任何特殊的编码或格式转换,可使用 M-x find-file-literally 命令。该命令与 C-x C-f 一样用于访问文件,但不会执行格式转换(参见《Emacs Lisp 参考手册》中的《格式转换》)、字符编码转换(参见《编码系统》)和自动解压缩(参见《访问压缩文件》),也不会因变量require-final-newline的设置为文件添加末尾换行符(参见《自定义文件保存方式》)。若你已以常规(非无格式转换)方式访问过同一文件,该命令会询问你是否改为以无格式转换方式访问。
某些文件之间会存在松散的关联关系,这类文件被称为 sibling files兄弟文件 。例如编辑 C 语言文件时,若存在文件 "foo.c" ,通常会对应一个头文件 "foo.h" ,该头文件即为其兄弟文件;同一文件的不同版本也可视为兄弟文件,例如 "src/emacs/emacs-27/lisp/allout.el" 和 "src/emacs/emacs-28/lisp/allout.el" 。Emacs 提供了 find-sibling-file 命令用于在兄弟文件之间跳转,但无法默认推测用户希望哪些文件互为兄弟文件,因此你可通过修改用户选项 find-sibling-rules 自由配置,该选项是一个由「匹配 / 扩展」规则组成的列表。
例如,要实现从 ".c" 文件到 ".h" 文件的映射,可进行如下配置:
(setq find-sibling-rules '(("\\([^/]+\\)\\.c\\'" "\\1.h")))
(ff-find-related-file 命令也提供类似功能,该命令专为 C 语言文件设计,参见《C 模式的其他命令》章节。)
又如,若你希望将 "src/emacs/DIR/file-name" 目录下的所有文件视为其他同层级目录下同名文件的兄弟文件,可配置:
(setq find-sibling-rules '(("src/emacs/[^/]+/\\(.*\\)\\'" "src/emacs/.*/\\1")))
如上所示,该选项的每一条规则均为 (MATCH EXPANSION...) 的形式。其中 match匹配规则 是匹配已访问文件名的正则表达式, expansion扩展规则 可通过 '\\1' 、 '\\2' 等引用匹配规则中的捕获组。Emacs 会将扩展后的字符串作为正则表达式,在文件系统中匹配符合条件的文件。
有两个特殊的钩子变量可扩展文件访问的操作逻辑:
- 访问不存在的文件时,Emacs 会执行
find-file-not-found-functions中的所有函数。该变量是一个函数列表,函数会被依次调用(无参数),直到某个函数返回非nil值。这并非普通钩子,变量名以 '-functions' 而非 '-hook' 结尾,正是为了区分这一特性。 - 成功访问任意文件(无论文件是否存在)后,Emacs 会无参数调用
find-file-hook中的所有函数,该变量是一个 普通钩子 。若访问的是不存在的文件,会先执行find-file-not-found-functions中的函数,再执行该钩子。关于钩子的详细说明参见《钩子》章节。
你可通过多种方式为文件自动指定编辑所用的主模式(参见《选择文件模式》),也可为文件指定专属的局部变量(参见《文件中的局部变量》)。
20.3. 保存文件
在 Emacs 中保存缓冲区,指将缓冲区的内容回写到其对应的源文件中。
20.3.1. 保存文件命令
以下是与文件的保存和写入相关的命令。
C-x C-s- 将当前缓冲区保存至对应文件 (
save-buffer) C-x s- 保存任意或全部已修改的缓冲区至各自对应文件 (
save-some-buffers) M-~- 清除当前缓冲区的已修改标记 (
not-modified);带前缀参数(C-u)时,标记当前缓冲区为已修改状态 C-x C-w- 将当前缓冲区以指定文件名保存 (
write-file),即另存为 M-x set-visited-file-name- 修改当前缓冲区后续保存时对应的文件名
M-x rename-visited-file- 功能与
M-x set-visited-file-name一致,同时会重命名缓冲区正在访问的原文件(若存在)
若你希望保存文件并让修改永久生效,输入 C-x C-s (save-buffer) 即可。保存完成后,该命令会在回显区显示如下提示信息:
Wrote /u/rms/gnu/gnu.tasks
若当前缓冲区未被修改(自缓冲区创建或上次保存后无任何更改),则不会实际执行保存操作 —— 因为此操作无任何实际效果,取而代之的是, C-x C-s 会在回显区显示:
(No changes need to be saved)
带前缀参数执行 C-u C-x C-s 时,Emacs 会标记该缓冲区,使其在 下一次保存时生成备份文件 ,相关说明参见《备份文件》章节。
C-x s (save-some-buffers) 命令会逐个询问是否保存已修改的缓冲区,你可对每个缓冲区选择对应的操作,可选响应与查询替换的操作方式类似:
ySPC- 保存当前缓冲区,继续询问其余缓冲区
nDEL- 不保存当前缓冲区,继续询问其余缓冲区
!- 保存当前缓冲区及所有剩余缓冲区,后续不再询问
qRET- 终止
save-some-buffers命令,不再保存任何缓冲区 .- 保存当前缓冲区,直接退出
save-some-buffers命令,不再询问其余缓冲区 C-r- 查看当前正在询问的缓冲区内容;退出查看模式后,会回到
save-some-buffers的询问界面,重新发起该缓冲区的保存询问 C-f- 退出
save-some-buffers命令,直接切换至当前正在询问的缓冲区 d- 对比当前缓冲区与对应源文件的差异,可直观查看即将保存的修改内容(该操作会调用
diff-buffer-with-file命令,参见《文件对比》章节) C-h- 显示上述操作选项的帮助信息
你可自定义变量 save-some-buffers-default-predicate 的取值,以此控制 Emacs 会对哪些缓冲区发起保存询问。
退出 Emacs 的快捷键 C-x C-c 会调用 save-some-buffers 命令,因此也会弹出相同的保存询问。
若你修改了缓冲区但并不希望保存这些更改,需执行相关操作避免误保存;否则每次使用 C-x s 或 C-x C-c 时,都有可能误保存该缓冲区。你可执行的操作之一是输入 M-~ (not-modified) ,该命令会清除缓冲区的已修改标识,此后所有保存命令都会判定该缓冲区无需保存(符号 '~' 在数学中常表示 “非”,因此 M-~ 可理解为 Meta键版的 “非” 操作)。此外,你也可通过重新从文件读取文本,撤销自访问或上次保存文件后做出的所有修改,该操作称为 恢复缓冲区 ,参见《恢复缓冲区》章节(你也可重复执行撤销命令 C-x u ,直至撤销所有修改,但恢复缓冲区的操作会更简便)。
M-x set-visited-file-name 命令用于修改当前缓冲区正在访问的文件名, 该命令会通过迷你缓冲区读取新的文件名 ,随后将缓冲区标记为访问该新文件,并相应修改缓冲区名称。该命令不会将缓冲区内容保存至新文件,仅会修改 Emacs 内部的记录,为后续的保存操作做准备;同时它会将缓冲区标记为已修改,确保你在该缓冲区执行 C-x C-s 时能触发保存。
若你希望将缓冲区标记为访问新文件并 立即保存 ,可使用 C-x C-w (write-file) 命令。该命令等效于先执行 set-visited-file-name ,再执行 C-x C-s ,区别在于:若指定的新文件已存在, C-x C-w 会先请求你的确认。若对一个未访问任何文件的缓冲区执行 C-x C-s ,其效果与 C-x C-w 一致 —— 会读取一个文件名,将缓冲区标记为访问该文件,并将内容保存至该文件。未访问任何文件的缓冲区,其默认文件名由 缓冲区名称 与 缓冲区默认目录 组合而成(参见《文件名》章节)。
在大多数情况下,若新文件名的后缀隐含了对应的主模式, C-x C-w 会自动将缓冲区切换至该主模式; set-visited-file-name 命令也具备此功能,相关说明参见《选择文件模式》章节。
若你希望将当前缓冲区内容保存至其他文件, 但不将缓冲区标记为访问该文件 ,可先执行 mark-whole-buffer ( C-x h )全选缓冲区内容,再执行 M-x write-region 命令(参见《各类文件操作》章节)。
当 Emacs 即将保存文件时,若检测到磁盘上该文件的最新修改时间,与 Emacs 上次读取或写入该文件的时间不一致,会立即向你发出提示。这一情况通常意味着文件被 同时编辑 并引发了问题,需要你及时处理,相关说明参见《同时编辑》章节。
20.3.2. 备份文件(Backup Files)
在大多数操作系统中,重写文件会直接清除该文件的原有内容记录。因此,从 Emacs 中保存文件本会覆盖文件的旧内容 —— 但实际不会,因为 Emacs 会在执行实际保存操作前,将文件旧内容妥善复制到另一个文件中,这个文件即为 backup file备份文件 。
Emacs 仅在从访问文件的缓冲区 首次保存该文件 时,为其创建备份文件。后续无论你对该文件执行多少次保存操作,其备份文件的内容都不会改变。但如果关闭该缓冲区后再次访问此文件,Emacs 会为其生成一个新的备份文件。
对于绝大多数文件,是否创建备份文件由变量 make-backup-files 决定。在多数操作系统中,该变量的默认值为 t ,即 Emacs 会自动生成备份文件。
对于由 版本控制系统 管理的文件(参见《版本控制》章节),是否创建备份文件由变量 vc-make-backup-files 决定。该变量默认值为 nil ,因为当所有历史版本都已存储在版本控制系统中时,备份文件属于冗余文件(参见《通用选项》章节)。
你可根据需要设置 Emacs,为每个文件仅保留 单个备份文件 ,或为编辑的每个文件生成一系列 带编号的备份文件 (参见《单备份与编号备份》小节)。
变量 backup-enable-predicate 的默认配置,会禁止为 临时文件目录 中的文件创建备份文件,临时文件目录由变量 temporary-file-directory 或 small-temporary-file-directory 指定。
即便缓冲区此前已执行过保存操作,你也可明确让 Emacs 为该缓冲区重新生成一个备份文件:
- 若通过
C-u C-x C-s保存缓冲区,当你再次保存该缓冲区时,此次保存的版本会被生成为新的备份文件; C-u C-u C-x C-s会先将文件原有内容生成为新的备份文件,再执行缓冲区的保存操作;C-u C-u C-u C-x C-s则会同时完成两项操作:将文件原有内容生成备份文件,且设置为 再次保存时 ,将本次新保存的内容也生成新的备份文件。
你可自定义变量 backup-directory-alist ,指定匹配特定模式的文件,在指定目录中创建备份文件。一种常用配置是向该变量添加元素 ("." . dir) ,让所有文件的备份都生成在绝对路径为 dir 的目录中。Emacs 会自动修改备份文件的名称,避免不同目录下同名文件的备份发生命名冲突。另一种配置是添加 ("." . ".~") ,将备份文件生成在原文件所在目录的隐藏子目录 '.~' 中,若该目录不存在,Emacs 会自动创建以完成备份。
20.3.2.1. 单一备份或编号备份
Emacs 创建备份文件时,默认的命名方式是在待编辑文件的文件名后追加符号 '~' ;例如,文件 eval.c 对应的备份文件为 eval.c~ 。
若因访问权限限制,Emacs 无法以常规名称创建备份文件,会将备份文件写入 ~/.emacs.d/%backup%~ 路径。该路径下仅能存在一个此类备份文件,因此仅可获取最近一次生成的该类备份。
Emacs 也支持 创建编号备份文件 ,其命名规则为在原文件名后添加 '.~' 、数字序号和另一个 '~' 。例如,文件 eval.c 的编号备份文件依次为 eval.c.~1~ 、 eval.c.~2~ ,序号可延续至 eval.c.~259~ 乃至更大的数字。
变量 version-control 用于控制 Emacs 生成单备份文件还是多个编号备份文件,其可选取值及对应规则如下:
nil- 若文件已有编号备份,则继续生成编号备份;否则生成单备份文件(默认取值)
t- 始终生成编号备份文件
never- 从不生成编号备份文件,始终生成单备份文件
该变量的常规配置方式为通过初始化文件或自定义缓冲区进行 全局设置 ;同时也可在单个缓冲区中进行局部设置,以控制该缓冲区对应文件的备份生成规则(参见《局部变量》章节)。部分模式(如 Rmail 模式)会自动设置该变量,也可在访问指定文件时,让 Emacs 自动为其设置该变量的局部取值(参见《文件中的局部变量》章节)。
若你设置了环境变量 VERSION_CONTROL (用于指定各类 GNU 工具的备份文件生成规则),Emacs 在启动时也会遵循该环境变量的配置,自动对应设置 Lisp 变量 version-control :
- 当环境变量值为
t或numbered时,Emacs 中version-control设为t - 当环境变量值为
nil或existing时,Emacs 中version-control设为nil - 当环境变量值为
never或simple时,Emacs 中version-control设为never - 若将变量
make-backup-file-name-function设为合适的 Lisp 函数,可覆盖 Emacs 默认的备份文件命名规则。
20.3.2.2. 备份文件的自动删除
为避免过度占用磁盘空间,Emacs 可自动删除编号备份文件的旧版本。通常情况下,Emacs 会保留编号最靠前的若干个备份和最新的若干个备份,将中间的所有备份文件删除。该清理操作会在 每次生成新备份文件时 执行。
备份文件的自动清理规则由两个变量控制: kept-old-versions 和 kept-new-versions 。二者的取值分别对应 每次生成新备份时 ,需要保留的最旧(编号最小)备份文件数量,以及需要保留的最新(编号最大)备份文件数量。处于两者之间的备份文件(即排除上述保留的最旧和最新备份后,剩余的中间备份)均为冗余的中间版本,会被自动删除。这两个变量的取值仅在新备份生成后、清理冗余版本时生效,且刚生成的新备份会被计入 kept-new-versions 的保留数量中。两个变量的默认值均为 2 。
若变量 delete-old-versions 的取值为 t ,Emacs 会在后台静默删除冗余的备份文件,不弹出任何提示;若取值为 nil (默认值),Emacs 会先询问你是否确认删除这些冗余备份版本,再执行后续操作;若为其他任意值,Emacs 则不会自动删除任何备份文件。
你也可使用 Dired 中的句点( . )命令手动删除备份旧版本,相关操作参见《批量标记文件》章节。
20.3.2.3. 复制备份与重命名备份
创建备份文件的方式分为 复制原文件 和 重命名原文件 两种。当原文件存在多个文件名(硬链接)时,两种方式的效果会截然不同:若将原文件重命名为备份文件,那么原文件的其他关联文件名会成为该备份文件的名称;若选择复制原文件生成备份,原文件的其他关联文件名仍指向你正在编辑的文件,通过这些文件名访问到的内容也会是文件的新内容。
备份方式的选择也会影响文件的属主和所属用户组:采用复制方式时,文件的属主和所属组不会发生变化;采用重命名方式时,你会成为文件的新属主,文件的所属组则会变为系统默认组(不同操作系统的默认组规则存在差异)。
Emacs 选择复制或重命名方式的判断逻辑如下:
- 若变量
backup-by-copying为非nil值(默认值为nil),采用复制方式。 - 若上述变量为
nil,但变量backup-by-copying-when-linked为非nil值(默认值为nil)且原文件存在多个硬链接文件名,采用复制方式。 若上述两个变量均为
nil,但变量backup-by-copying-when-mismatch为非nil值(默认值为t)且重命名会导致文件属主或所属组发生变化,采用复制方式。若将
backup-by-copying-when-mismatch设为nil,Emacs 会检查文件属主的用户ID和所属组ID:若其中任意一个 ID 的数值不大于变量backup-by-copying-when-privileged-mismatch的取值,Emacs 仍会按照backup-by-copying-when-mismatch为非nil的规则执行,即采用复制方式。- 若以上条件均不满足,默认采用重命名方式。
当文件由版本控制系统管理时(参见《版本控制》章节),Emacs 通常不会以常规方式为该文件创建备份。但向版本控制系统提交(也称作 checking in,参见《版本控制的基本概念》章节)文件的新版本,在某些方面与创建备份类似,其中一个弊端是:这类操作通常会破坏文件的硬链接,导致你所访问的文件名与该文件的其他关联文件名失去关联。该问题与 Emacs 无关,是由版本控制系统本身的操作导致的。
部分文件存储服务支持 文件版本管理功能 :会记录文件的历史版本,并允许恢复至任意历史版本。若你希望在 Emacs 中编辑这类服务托管的文件时,仍能使用该功能,可将变量 backup-by-copying 自定义为非nil值。
编辑重要文件时,采用复制原文件的方式创建备份也十分实用:这能确保即便在创建备份后、保存编辑内容前的过程中发生异常,原文件仍会保留其原始名称。此外,你也可将变量 file-precious-flag 设为非nil值,该设置会默认采用复制方式创建备份,同时还能在保存编辑内容时,防止出现 I/O 错误。
20.3.3. 文件保存定制
若变量 require-final-newline 的取值为 t ,在保存或写入文件时,若文件末尾无换行符,Emacs 会自动为其添加,且不弹出任何提示。若取值为 visit ,Emacs 仅在 访问文件后 ,为末尾无换行符的文件添加换行符(此操作会标记缓冲区为已修改状态,你可撤销该操作)。若取值为 visit-save ,则 Emacs 会在 访问文件和保存文件时 ,均执行上述末尾换行符的添加操作。若取值为 nil ,Emacs 不会修改文件末尾的内容;若为其他非nil值,Emacs 会先询问你是否添加换行符,再执行后续操作。该变量的默认值为 nil 。
部分主模式专为特定类型的文件设计,而这类文件通常要求末尾必须有换行符。此类主模式会将 require-final-newline 的取值设为变量 mode-require-final-newline 的取值,该变量默认值为 t 。你可通过修改 mode-require-final-newline ,控制这些主模式对文件末尾换行符的处理方式。
若该选项为非nil值,且你通过 符号链接 访问文件,当变量 file-precious-flag 也为非nil值时,Emacs 在保存缓冲区时会 断开该符号链接 ,并将缓冲区内容写入与该符号链接同名的实际文件中(关于 file-precious-flag 的更多说明,参见《Emacs Lisp 参考手册》)。若你希望此类场景下,Emacs 将缓冲区内容保存至符号链接 指向的原文件 (从而保留该符号链接),可将变量 file-preserve-symlinks-on-save 自定义为 t 。
通常情况下,程序写入文件时,操作系统会先将文件数据临时缓存至主内存,再将数据提交至二级存储设备。该机制虽能大幅提升性能,但存在数据丢失风险:若系统在缓存数据提交前断电,缓存中的数据会丢失;且在部分平台中,其他进程可能无法立即感知到文件的修改。
为降低该风险,Emacs 可在保存文件后调用 fsync 系统调用,强制将缓存数据写入二级存储。但使用 fsync 并不能完全消除数据丢失或修改通知延迟的问题,原因有二:一是许多系统并未对 fsync 提供完善支持;二是 Emacs 的文件保存流程通常还依赖于目录更新操作,即便 fsync 执行正常,这类目录更新也可能在系统崩溃时丢失。
变量 write-region-inhibit-fsync 用于控制 Emacs 是否在保存文件后调用 fsync ,该变量默认值为 t (即不调用)。
Emacs 在写入 自动保存文件 时,绝不会调用 fsync —— 因为这类文件本身就存在数据丢失的可能性。
20.3.4. 防止同时编辑冲突
同时编辑 指两名用户访问同一文件,双方均对文件做出修改并执行保存操作的情况。若未及时向用户告知该情况,先完成保存的一方,后续会发现其修改内容被覆盖丢失。
在部分系统中,当第二位用户开始修改文件时,Emacs 会立即检测到并发出警告;而在所有系统中,Emacs 都会在你执行保存操作时进行检查,若检测到你即将覆盖其他用户的修改,会及时弹出警告。此时你可执行相应的修正操作,而非直接保存文件,避免覆盖丢失其他用户的修改成果。
当你在访问某文件的 Emacs 缓冲区中做出 首次修改 时,Emacs 会记录该文件被你 锁定 (锁定的实现方式为:在该文件所在目录中创建一个命名特殊、内容特殊的符号链接 7,更多细节参见《Emacs Lisp 参考手册》中的文件锁定相关章节)。当你保存修改后,Emacs 会自动解除该文件的锁定。该机制的核心逻辑是: 当访问某文件的缓冲区存在未保存修改时,该文件会被锁定 。
你可将变量 create-lockfiles 设为 nil ,禁止 Emacs 创建锁定文件。注意:此操作会让你失去该功能带来的保护。你也可通过变量 lock-file-name-transforms ,控制锁定文件的存储位置。
若你尝试修改的文件,已被其他用户锁定,此时就会发生 collision锁定冲突 。Emacs 检测到冲突后,会调用 Lisp 函数 ask-user-about-lock ,向你询问处理方式;你也可重新定义该函数,实现自定义的冲突处理逻辑。该函数的标准定义会向你提出问题,并接受以下三种响应:
s- 抢占锁定。原持有该文件锁定的用户会失去锁定权限,锁定权限转移至你。
p- 继续编辑。无视其他用户的锁定,继续编辑该文件。
q- 退出操作。触发文件被锁定的错误(
file-locked),缓冲区内容保持不变 —— 你本次尝试做出的修改不会实际生效。
若 Emacs 程序或操作系统发生崩溃,可能会遗留一些 失效的锁定文件 ,导致你偶尔收到虚假的锁定冲突警告。当你确认该冲突为虚假冲突时,只需输入 p ,告知 Emacs 继续执行编辑操作即可。
请注意,文件锁定机制 基于文件名实现 :若一个文件拥有多个名称(如硬链接、符号链接),Emacs 无法阻止两名用户通过不同名称同时编辑该文件。
在某些情况下,Emacs 无法创建锁定文件,例如:Emacs 缺少对应的系统权限,或因其他原因无法生成锁定文件。即便如此,Emacs 仍能在你尝试保存文件时检测到冲突,检测方式为 检查文件的最后修改时间 :若该文件自 Emacs 上次访问或保存后,被其他方式修改过,此时执行保存会覆盖丢失这些修改。Emacs 会立即显示警告信息,并在保存前向你请求确认;输入 'yes' 则执行保存,输入 'no' 或按下 C-g 则取消保存操作。
若你收到通知,得知该文件已发生同时编辑,可使用 M-x diff-buffer-with-file 命令对比缓冲区内容与文件的实际内容,查看差异(参见《文件对比》章节)。
你可将变量 remote-file-name-inhibit-locks 设为 t ,禁止 Emacs 创建远程文件的锁定文件。
次要模式 lock-file-mode 可通过交互方式调用,用于切换当前缓冲区中 create-lockfiles 变量的局部取值。
20.3.5. 文件影子(Shadowing Files)
你可将指定文件的完全相同的 shadow copies影子副本 保存在多个位置,这些位置甚至可以在不同的计算机上。要实现该功能,首先你需要创建一个 shadow file group影子文件组 ,即一组在多个站点间共享、且文件名完全相同的文件构成的集合。该文件组为永久配置,不仅在当前 Emacs 会话中生效,在后续的 Emacs 会话中也同样有效。影子文件组创建完成后,每次退出 Emacs 时,程序都会将你编辑过的该文件,同步复制到其所属影子文件组中的其他文件位置。你也可以不退出 Emacs,直接输入 M-x shadow-copy-files 执行该同步复制操作。
shadow cluster影子集群 是一组共享目录的主机,因此向集群中的任一主机执行文件的复制操作,即可完成集群内所有主机上该文件的更新。每个影子集群都有专属名称,同时需要指定集群中 主主机 的网络地址(即文件的同步目标主机),以及一个能匹配集群中所有其他主机名的正则表达式。你可通过 M-x shadow-define-cluster 命令定义影子集群。
M-x shadow-initialize- 启用文件影子功能
M-x shadow-define-literal-group- 声明单个文件在多个站点间共享
M-x shadow-define-regexp-group- 将匹配指定正则表达式的所有文件,设为在多主机间共享的文件
M-x shadow-define-cluster RET 集群名 RET- 定义一个名为「集群名」的影子文件集群
M-x shadow-copy-files- 同步复制所有待处理的影子文件
M-x shadow-cancel- 取消部分文件的遮蔽同步指令
创建影子文件组可使用 M-x shadow-define-literal-group 或 M-x shadow-define-regexp-group 命令,更多使用细节可查阅这两个命令的文档字符串。
在将文件同步复制到其各影子副本位置前,Emacs 会向你请求确认。你可选择 "no" ,跳过本次该文件的同步复制操作。若你希望永久取消某个文件的影子同步功能,可使用 M-x shadow-cancel 命令,删除或修改该文件所属的影子文件组配置。
20.3.6. 自动更新时间戳
你可设置让文件中的 时间戳 在每次保存文件时自动更新(时间戳也可被称作日期戳或最后修改时间)。在文件文本中嵌入时间戳,能确保即便文件因被复制或转换而丢失文件系统层面的修改时间,文件的实际写入时间也能被留存。
设置自动时间戳功能需分两步操作:首先,需在文件的 前八行内 的任意位置添加一个时间戳模板,模板格式可写为:
Time-stamp: <>
也可按你的需求写成:
Time-stamp: " "
添加好该模板后,你可立即使用 M-x time-stamp 命令,一次性更新当前缓冲区的时间戳。Emacs 会自动检测文件中的模板,若找到模板,就会将当前的日期、时间、作者及其他信息填充到尖括号或双引号之间(若缓冲区中无此模板, time-stamp 命令将不执行任何操作)。生成首个时间戳后,该模板所在行可能会显示为:
Time-stamp: <1993-07-06 11:05:14 terryg>
其次,将 time-stamp 函数添加至 before-save-hook 钩子中,配置 Emacs 在每次保存文件时自动运行该函数(参见《钩子》章节)。你既可以使用 M-x customize-option 命令自定义 before-save-hook 选项(参见《自定义特定项》章节),也可以编辑你的初始化文件,添加如下配置行:
(add-hook 'before-save-hook 'time-stamp)
20.3.6.1. 时间戳定制
若要为特定文件自定义时间戳格式,可在该文件的 局部变量列表 中设置变量 time-stamp-pattern (参见《指定文件变量》章节)。通过该变量,你可以修改 time-stamp 命令匹配时间戳模板的规则,同时也能指定命令在文件中的模板查找范围;具体用法可查阅该变量的内置文档(使用 C-h v 命令,参见《通过命令或变量名获取帮助》章节)。
举一个简单的例子,若某文件的开头位置有如下内容:
publishing_year_and_city = "Published nnnn in Boston, Mass.";
那么在该文件末尾添加以下注释,即可告知 time-stamp 命令如何识别并更新这个自定义模板:
// Local Variables:
// time-stamp-pattern: "Published %Y in Boston"
// End:
该匹配规则表示,时间戳的前缀文本为 "Published" ,后缀文本为 "in Boston" 。如果 time-stamp 命令在文件 前八行 中找到同时包含这两段文本的内容,就会按照 %Y 的格式要求,将两段文本之间的内容替换为当前年份。
对文件局部变量完成任何修改后,需输入 M-x normal-mode 命令重新读取这些变量配置。
再举一个示例,将时间戳插入 HTML 文档的最后一个段落中。由于该模板位于文档末尾,而非文件前八行,因此在 time-stamp-pattern 的开头添加 -10/ ,告知 time-stamp 命令在文件 最后 10 行 中查找模板;其中的 %% 表示使用默认的时间戳格式(由变量 time-stamp-format 指定)。
文档内容示例:
…
<p>Last modified: </p>
</body>
</html>
<!--
Local Variables:
time-stamp-pattern: "-10/Last modified: %%</p>$"
End:
-->
Emacs 会默认根据你的 区域设置 (参见《环境变量》章节)和 时区 (参见《Emacs Lisp 参考手册》中的《时间与日期》章节)格式化时间戳。你可以设置变量 time-stamp-time-zone ,覆盖默认使用的时区。关于时间戳的具体格式化规则,以及其他影响格式化的相关变量,可查阅变量 time-stamp-format 的内置文档。
20.3.6.2. 强制单个文件的时间戳更新
若你正在编辑一个由多位作者协作的文件,且无法确定其他作者是否已在其 Emacs 初始化文件中 全局启用 时间戳功能,可在该文件的 局部变量列表 中,将 time-stamp 函数添加至缓冲区的 before-save-hook 钩子,以此为该特定文件强制启用时间戳功能。以下是基于前文示例的扩展配置:
// Local Variables: // eval: (add-hook 'before-save-hook 'time-stamp nil t) // time-stamp-pattern: "Published %Y in Boston" // End:
本示例中同时配置了上述两项内容,若你希望使用默认的时间戳匹配规则,也可仅使用 eval 配置项,而不设置 time-stamp-pattern 。
20.4. 恢复缓冲区(Reverting a Buffer)
若你对访问文件的缓冲区做出了大量修改,之后又想放弃这些修改,可通过恢复操作还原至文件的已保存版本。执行该操作只需按下 C-x x g 即可。由于误执行恢复操作可能会丢失大量工作成果,因此若缓冲区存在未保存修改,Emacs 会先向你请求确认,再执行恢复。
revert-buffer 命令会尽量智能定位光标位置:若文件仅做了少量修改,光标会停留在与修改前大致相同的文本位置;但如果文件做了大幅修改,光标最终可能会出现在完全不同的位置。
执行恢复操作后,缓冲区的 已修改标记会被清除 。但该操作会将本次恢复的所有修改内容,作为单次修改记录添加到缓冲区的撤销历史中(参见《撤销》章节)。因此,即便执行了恢复,若你后续改变主意,仍可按下 C-/ 或其等效快捷键,恢复此前被撤销的修改内容。
若想以更保守的方式恢复缓冲区,可使用 revert-buffer-with-fine-grain 命令。该命令的基础功能与 revert-buffer 一致,但会尽可能做到 无破坏性恢复 ,尽力保留缓冲区中的所有标记、文本属性和覆盖层。当你对文件做出了大量修改时,这种恢复方式的执行速度可能会很慢,你可以修改变量 revert-buffer-with-fine-grain-max-seconds ,指定该方式替换缓冲区内容的最长耗时(以秒为单位)。请注意,该设置并不能保证 revert-buffer-with-fine-grain 命令的整体执行时间不超过该限值。
部分与文件无关联的缓冲区也可执行恢复操作,例如 Dired 缓冲区,对这类缓冲区而言,恢复意味着重新计算并刷新其内容。而通过 C-x b 显式创建的空白缓冲区无法执行恢复操作,若尝试对其执行 revert-buffer ,命令会直接抛出错误。
当你编辑的文件会 自动且频繁地发生变化 (例如,某个持续运行的进程生成的输出日志文件),让 Emacs 无需询问直接恢复该文件会非常实用。若要启用该功能,需将变量 revert-without-query 设为一个正则表达式列表。当文件名匹配列表中的任意一个正则表达式时,若文件在磁盘上发生了变化,且对应的缓冲区本身无未保存修改, find-file 和 revert-buffer 命令会自动恢复该文件(若你已对缓冲区文本做了修改,自动恢复会丢弃这些修改,因此该情况下不会执行)。
快捷键 C-x x g 绑定的是 revert-buffer-quick 命令,该命令与 revert-buffer 功能一致,但 弹出的提示更少 。与 revert-buffer 不同,若当前缓冲区关联了文件且无未保存修改,该命令不会弹出任何确认提示。同时,该命令会遵循用户选项 revert-buffer-quick-short-answers 的配置:若该选项为非nil值,会使用简短的 'y/n' 确认提示,而非冗长的 'yes/no' 提示。
你也可以设置 Emacs,让缓冲区在其关联的文件在磁盘上发生变化时 自动执行恢复 ,相关配置参见《自动恢复:让缓冲区自动保持最新》章节。
请注意,恢复缓冲区时,Emacs 会为该缓冲区关联的文件启用对应的主模式(具体规则参见《选择文件模式》章节)。因此,恢复操作最终启用的主模式会受模式重映射配置的影响:若你在恢复前自定义了 major-mode-remap-alist ,恢复后启用的主模式可能与原模式不同。
20.5. 自动恢复:保持缓冲区自动更新(Auto Revert)
当缓冲区关联的磁盘文件被其他程序修改时,缓冲区内容会与文件不同步。若要让缓冲区始终保持最新,可输入 M-x auto-revert-mode 启用 自动恢复模式 ,该模式会在磁盘文件发生修改时,自动恢复缓冲区内容。若要为所有文件缓冲区启用该功能,可输入 M-x global-auto-revert-mode 开启 全局自动恢复模式 。
当缓冲区存在未保存修改,或其对应的磁盘文件被删除、重命名时,自动恢复功能不会对该缓冲区执行恢复操作。
自动恢复模式的一个常用场景是 跟踪 系统日志这类文件,让其他程序对该文件的修改能实时显示在 Emacs 中。只需将光标移至缓冲区末尾,后续文件内容更新时,光标会始终停留在末尾位置。但如果你确定该文件只会在末尾追加内容,建议改用 自动恢复尾部模式 (auto-revert-tail-mode),该模式针对此场景的执行效率更高,且对远程文件同样适用。
缓冲区被自动恢复时,Emacs 会弹出提示信息,将变量 auto-revert-verbose 设为 nil= 可关闭该提示。
自动恢复模式默认不会检查或恢复远程文件,因该操作通常耗时过久,将变量 auto-revert-remote-files 设为非nil值可修改此行为。
自动恢复模式默认通过 file notifications文件通知机制 工作,由操作系统将文件系统的修改情况通知给 Emacs。将变量 auto-revert-use-notify 设为 nil 可禁用该机制,此时 Emacs 会通过 轮询 方式检查文件修改,默认轮询间隔为 5 秒,可通过变量 auto-revert-interval 修改轮询时间。
并非所有系统都支持文件通知机制,在不支持该机制的系统中, auto-revert-use-notify 会默认设为 nil 。
即便启用了文件通知机制,自动恢复模式默认仍会定期轮询文件是否修改。多数场景下轮询并无必要,仅依靠通知机制可减少资源消耗,将变量 auto-revert-avoid-polling 设为非nil值即可关闭轮询。但需注意,部分文件系统的通知机制失效,尤其是类 Unix 系统中的网络文件系统(这类文件可被其他机器修改),此时轮询操作是必要的。若 auto-revert-avoid-polling 为非nil但需对部分文件强制轮询,可设置变量 auto-revert-notify-exclude-dir-regexp ,让匹配该正则表达式的文件排除在通知机制之外,转而使用轮询。
在 Dired 缓冲区中(参见《目录编辑器 Dired》章节),当缓冲区对应目录中有文件被创建或删除时,自动恢复模式会刷新该缓冲区的内容。
若要恢复版本控制系统管理下文件的早期版本,可参考相关命令说明(参见《撤销版本控制操作》章节)。关于访问版本控制系统管理的文件时,自动恢复模式的特殊行为说明,参见《版本控制与模式行》章节。
20.5.1. 非文件缓冲区的自动恢复
全局自动恢复模式默认仅对 文件缓冲区 执行自动恢复操作。要为特定非文件缓冲区启用自动恢复,有两种方式:一是在目标缓冲区中启用自动恢复模式(执行 M-x auto-revert-mode 命令);二是将变量 global-auto-revert-non-file-buffers 设为非nil值,后者会为所有已实现该功能的非文件缓冲区(如下方列表所示)全局启用自动恢复。
与文件缓冲区相同,非文件缓冲区在你编辑操作期间,或缓冲区中包含恢复后可能丢失的信息时,默认不会执行自动恢复。因此, 已修改的非文件缓冲区不会触发自动恢复 。这一规则的实际应用存在一定复杂性,因为判断非文件缓冲区何时应标记为已修改,通常比文件缓冲区的判断难度更高。
另一处需要注意的细节是,出于效率考量,自动恢复功能通常不会尝试检测缓冲区中所有可能的变更,仅会识别 主要变更 或 易于检测的变更 。因此,为非文件缓冲区启用自动恢复,并不总能保证缓冲区中的所有信息都是最新的,手动执行恢复操作依然有其必要性。
与之相反的是,部分非文件缓冲区会按照 auto-revert-interval 变量设定的秒数 定时自动恢复 (目前该规则仅适用于缓冲区菜单)。此类场景下,即便变量 auto-revert-verbose 设为非nil,自动恢复操作也不会打印任何提示信息。
部分非文件缓冲区可通过其 默认目录 的文件通知机制实现可靠的自动更新,Dired 缓冲区就是典型示例。对应的主模式可通过在缓冲区中将变量 buffer-auto-revert-by-notification 设为非nil值来标识这一特性,让自动恢复功能无需再执行定期轮询。需注意,该方式的文件通知仅针对目录本身的变更, 不包含目录内文件的变更 。
非文件缓冲区自动恢复的具体规则因缓冲区类型而异,相关细节会在对应章节中说明。
20.5.1.1. 缓冲区菜单的自动恢复
若已启用非文件缓冲区的自动恢复功能, 缓冲区菜单 (参见《对多个缓冲区执行操作》章节)会按照 auto-revert-interval 变量设定的秒数定时自动恢复,无论是否存在实际的变更需要同步(因为检查是否需要恢复的耗时,可能反而比直接执行恢复操作更长)。
若缓冲区菜单被错误标记为已修改状态,只需按下 g 手动恢复,自动恢复功能便会恢复正常。但需注意,若你已为部分缓冲区标记了删除或显示操作,执行恢复时需格外小心 —— 因为恢复操作会 清除所有标记 。而添加标记的操作会为缓冲区设置已修改标识,这一机制恰好能防止自动恢复功能误删标记
20.5.1.2. Dired 缓冲区的自动恢复
Dired 缓冲区仅在其主目录的文件列表发生变化时(如新增或删除文件)触发自动恢复;若仅为单个文件的信息变更(如文件大小修改),或插入的子目录内容发生变化,不会触发自动恢复。即便已为 Dired 缓冲区启用自动恢复功能,若要确保列表中所有信息均为最新,仍需按下 g 键手动执行恢复操作。
有时你会发现,修改或保存主目录列表中的文件后,缓冲区似乎也触发了自动恢复,这是因为对文件的修改或保存操作,往往会间接导致目录本身发生变化(例如生成备份文件或自动保存文件),但这种情况并非必然发生。
若 Dired 缓冲区被标记为 “已修改”,且其中无需要保留的修改内容,多数情况下按下 g 键手动恢复后,即可恢复自动恢复功能,仅存在一种例外情况:若你为文件添加了标记或标记符,仍可安全执行缓冲区恢复操作,该操作不会清除已添加的标记或标记符(除非被标记的文件已被删除);但恢复后,缓冲区仍会保持 “已修改” 状态,自动恢复功能也无法恢复。这一设计的原因是,当你为文件添加标记或标记符时,通常表明正针对该缓冲区进行操作,大概率不希望缓冲区在无提示的情况下发生变更。
若希望在保留文件标记 / 标记符的同时恢复自动恢复功能,可按下 M-~ 将缓冲区标记为 “未修改”;但后续若再次添加、删除或修改标记 / 标记符,缓冲区会重新被标记为 “已修改”。
注意:远程 Dired 缓冲区目前不支持自动恢复;通过 Shell 通配符或文件参数筛选出部分文件生成的 Dired 缓冲区,同样不支持自动恢复。 *Find* 和 *Locate* 缓冲区也无自动恢复功能。
此外,在部分系统中,Dired 缓冲区的自动恢复功能可能无法达到预期效果。
20.6. 自动保存:防止数据丢失(Auto-Saving)
Emacs 会定期将每个已打开的文件,保存至一个独立的文件中,且不会改动你实际编辑使用的原文件,这一功能称为 自动保存 。若系统发生崩溃,该功能能避免你丢失过多的编辑成果。
当 Emacs 判定达到自动保存时机时,会对每个缓冲区进行检查:若该缓冲区启用了自动保存功能,且自上次自动保存后有内容修改,便会对其执行自动保存操作。当变量 auto-save-no-message 设为 nil (默认值)时,若此次有文件实际执行了自动保存,回显区会显示提示信息「正在自动保存…」;若要关闭该提示,可将此变量自定义设置为非nil值。自动保存过程中若发生错误,Emacs 会捕获错误信息,避免其干扰你正在输入执行的命令。
20.6.1. 自动保存文件
自动保存操作通常不会将内容保存至你所打开的原文件中,因为贸然保存并非你想要永久保留的修改,会造成不必要的麻烦。反之,自动保存会将内容写入一个独立的文件,即 自动保存文件 ;只有当你显式执行保存操作时(如按下快捷键 C-x C-s ),修改内容才会写入原文件。
默认情况下,自动保存文件的命名规则为:在原文件名称的首尾各添加一个井号 '#' 。例如,打开文件 'foo.c' 对应的缓冲区,其自动保存文件为 '#foo.c#' 。对于大多数未关联实际文件的缓冲区,仅当你显式发起请求时才会执行自动保存;这类缓冲区的自动保存文件命名,会先在缓冲区名称首尾添加 '#' ,再在末尾拼接数字和字母以保证唯一性。比如,用于撰写待发送邮件的 *mail* 缓冲区,其自动保存文件可能命名为 #*mail*#704juu 。除非你重新编写 Emacs 相关功能代码(涉及 make-auto-save-file-name 和 auto-save-file-name-p 函数),否则自动保存文件都会遵循此命名规则。缓冲区的自动保存文件名,会在该缓冲区开启自动保存功能时完成计算。
变量 auto-save-file-name-transforms 允许你对自动保存文件名进行一定程度的自定义,你可通过该变量指定一系列正则表达式及替换规则,实现对自动保存文件名的转换。该变量的默认值会将远程文件(参见「远程文件」相关内容)的自动保存文件,存放至本地机器的临时文件目录中。
当你在一个大缓冲区中删除大量文本内容时,该缓冲区的自动保存功能会暂时关闭。此设计的原因是:若你并非有意删除这些文本,保留了被删内容的自动保存文件,会对你恢复数据更有帮助。若要在该情况下重新启用自动保存功能,可按下 C-x C-s 保存缓冲区,或执行命令 C-u 1 M-x auto-save-mode 。
若你希望自动保存操作直接写入原文件,而非独立的自动保存文件,可启用全局次要模式 auto-save-visited-mode 。开启该模式后,自动保存的效果将与显式保存完全一致。请注意,该模式与前文所述的自动保存模式相互独立,你可同时启用两种模式。但如果某一缓冲区已激活自动保存模式,且废弃变量 auto-save-visited-file-name 被设为非nil值,那么该缓冲区将不受 auto-save-visited-mode 模式的影响。
你可通过变量 auto-save-visited-interval ,自定义 auto-save-visited-mode 模式下自动保存的时间间隔,其默认值为 5 秒。而变量 auto-save-interval 和 auto-save-timeout 对 auto-save-visited-mode 模式不产生作用,关于这些变量的详细说明,参见「控制自动保存」章节。
当你将缓冲区的内容保存至原文件时,该缓冲区对应的自动保存文件会被自动删除(若将变量 delete-auto-save-files 设为 nil ,可禁止此删除操作)。若通过 C-x C-w 或 set-visited-file-name 命令修改缓冲区关联的原文件名,对应的自动保存文件也会同步重命名,与新的原文件名匹配。
默认情况下,关闭缓冲区不会删除其对应的自动保存文件。若将变量 kill-buffer-delete-auto-save-files 设为非nil值,当你关闭一个存在自动保存文件的缓冲区时,Emacs 会向你发起确认提示,询问是否删除该自动保存文件(若 delete-auto-save-files 设为 nil ,则不会出现此提示)。
20.6.2. 自动保存控制
当你打开任意文件时,若变量 auto-save-default 设为非nil值,该文件对应的缓冲区会开启自动保存功能(批处理模式下除外,参见「初始选项」相关内容)。该变量的默认值为 t ,因此文件关联缓冲区默认都会启用自动保存。若要切换当前缓冲区的自动保存状态,可执行命令 M-x auto-save-mode 。自动保存模式是一个 缓冲区局部次要模式 (参见「次要模式」章节)。
Emacs 会根据你自上次自动保存后输入的字符数,定期执行自动保存操作。变量 auto-save-interval 用于指定两次自动保存之间的字符输入阈值,默认值为 300 。该变量不支持设置过小的数值:若你将其自定义为小于 20 的数,Emacs 会自动按 20 的阈值执行。
当你停止输入一段时间后,Emacs 也会触发自动保存。默认情况下,闲置 30 秒后会执行该操作(此时 Emacs 可能同时进行垃圾回收,参见《Emacs Lisp 参考手册》中的「垃圾回收」章节)。你可通过自定义变量 auto-save-timeout 修改这一闲置阈值。若当前缓冲区内容较长,实际触发自动保存的闲置时间会相应延长;这是一项启发式设计,目的是在你编辑大缓冲区时减少干扰 —— 因为大缓冲区的自动保存会消耗可观的时间。闲置时的自动保存能实现两个目的:一是若你离开终端一段时间,确保所有编辑内容都已保存;二是能在你实际输入的过程中,减少自动保存的触发次数。
当 auto-save-visited-mode 模式启用时,文件关联缓冲区会在闲置 5 秒后触发自动保存,你可通过自定义变量 auto-save-visited-interval 修改该闲置阈值。
当 Emacs 遭遇致命错误时,也会执行自动保存操作。这类情况包括通过 'kill %emacs' 等 Shell 命令终止 Emacs 进程,或是电话线路、网络连接断开等场景。
你也可通过执行命令 M-x do-auto-save ,显式触发一次自动保存。
20.6.3. 从自动保存文件恢复数据
你可以使用命令 M-x recover-file RET 文件名 RET ,借助自动保存文件的内容恢复丢失的数据。该命令会先打开指定文件,经你确认后,从其对应的自动保存文件 #file# 中恢复内容,之后你可按下 C-x C-s ,将恢复的文本保存至原文件中。例如,要从自动保存文件 '#foo.c#' 恢复文件 'foo.c' ,操作步骤如下:
M-x recover-file RET foo.c RET yes RET C-x C-s
在请求确认前, M-x recover-file 会显示目录列表,展示指定文件与对应自动保存文件的相关信息,你可借此对比两者的大小和修改时间;若自动保存文件的修改时间更早,该命令将不会提供读取此文件的选项。
当 M-x recover-file 请求确认时,若你输入 diff 或 = 作为回应,命令会展示原文件与自动保存文件 '#file#' 之间的内容差异,随后再次向你请求恢复确认。
若 Emacs 程序或计算机发生崩溃,你可使用命令 M-x recover-session ,从自动保存文件中恢复所有此前正在编辑的文件。该命令会先为你展示已记录的所有中断会话列表,将光标移至你选择的会话处,按下 C-c C-c 即可进入后续步骤。
接着, recover-session 会针对该会话中曾编辑的每一个文件逐一确认,询问是否恢复对应文件;若你回答 'y' ,命令会调用 recover-file 并按其常规逻辑执行,展示原文件与自动保存文件的修改时间,且会再次确认是否恢复该文件。
recover-session 执行完成后,你选择恢复的所有文件都会出现在 Emacs 的缓冲区中, 此时需手动保存这些缓冲区 ,只有执行保存操作,才能将恢复的内容更新至原文件本身。
Emacs 会将中断会话的相关信息,记录在 '/.emacs.d/auto-save-list/' 目录下、命名格式为 '.saves-进程号-主机名' 的文件中,该存储目录由变量 auto-save-list-file-prefix 指定;若将此变量设为 nil ,Emacs 将不再记录会话信息,也就无法进行会话恢复操作。
20.7. 文件名别名(File Name Aliases)
符号链接与硬链接均可实现多个文件名指向同一个文件。 硬链接 是直接指向文件的替代名称,所有名称的有效性完全相同,不存在优先级之分。与之不同的是, 符号链接 是一种显式定义的别名:若 'foo' 是指向 'bar' 的符号链接,你可通过任一名称访问该文件,但 'bar' 为实际的原名称,而 'foo' 仅为别名。当符号链接指向目录时,会出现更为复杂的情况。
默认情况下,若你打开的文件,Emacs 已通过其他名称打开过,Emacs 会在回显区显示提示信息,并直接使用已打开该文件的现有缓冲区。这种情况会出现在多种场景:支持硬链接或符号链接的系统、会截断长文件名的系统中使用长文件名、或是在不区分大小写的文件系统中操作。你可将变量 find-file-suppress-same-file-warnings 设为非nil值,关闭该提示信息;若将变量 find-file-existing-other-name 设为 nil ,则会完全禁用此功能 —— 此时若通过两个不同名称打开同一个文件,Emacs 会为每个文件名分别创建独立的缓冲区。
若变量 find-file-visit-truename 设为非nil值,缓冲区中记录的文件名会是文件的 真实名称 (将所有符号链接替换为其指向的目标名称后得到的名称),而非你输入的原始名称。设置该变量的同时,也会等效启用 find-file-existing-other-name 变量的功能。
有时,某个目录通常通过符号链接进行访问,而你希望 Emacs 优先显示其链接名称,此时可自定义变量 directory-abbrev-alist 实现该需求。该列表中的每个元素均为 (from源匹配串 . to目标替换串) 的形式,表示当目录名中出现源匹配串时,将其替换为目标替换串。其中 源匹配串为正则表达式 (参见《正则表达式的语法》章节),会从目录名的第一个字符开始匹配,且需以 '\`' 作为起始(以兼容包含换行符的目录名,避免 '^' 匹配符失效); 目标替换串 需为指向同一目录的普通绝对目录名,且不可在其中使用 '~' 代表主目录(Emacs 会单独处理此类路径替换)。以下是一个示例,适用于 '/home/fsf' 目录通常通过符号链接 '/fsf' 访问的系统:
(("\\`/home/fsf" . "/fsf"))
20.8. 文件目录(File Directories)
文件系统会将文件归类至不同目录中, directory listing目录列表 即某一目录下的所有文件清单。Emacs 提供了创建、删除目录的命令,也可生成 简洁格式 (仅显示文件名)和 详细格式 (包含大小、日期及其他属性)的目录列表。Emacs 还内置了名为 Dired 的目录浏览功能,可通过快捷键 C-x d 调用,相关用法参见《目录编辑器 Dired》章节。
C-x C-d 目录/匹配模式 RET- 显示简洁格式的目录列表 (
list-directory) 。 C-u C-x C-d 目录/匹配模式 RET- 显示详细格式的目录列表。
M-x make-directory RET 目录名 RET- 创建指定名称的新目录。
M-x delete-directory RET 目录名 RET- 删除指定名称的目录;若目录非空,会询问是否递归删除该目录。
用于显示目录列表的核心命令为 C-x C-d (list-directory) ,该命令会通过迷你缓冲区读取一个文件名 —— 此文件名可以是待列出的目录,也可以是包含通配符、用于匹配目标文件的模式。例如:
C-x C-d /u2/emacs/etc RET
会列出 /u2/emacs/etc 目录下的所有文件。以下是指定文件名称匹配模式的示例:
C-x C-d /u2/emacs/src/*.c RET
默认情况下, C-x C-d 显示的简洁目录列表仅包含文件名;若执行命令时带上数字参数(无论参数具体值是多少),则会生成详细目录列表,包含文件大小、修改日期、所有者等信息(效果类似系统命令 ls -l )。
目录列表的内容,主要通过在子进程中执行 ls 命令获取。有两个 Emacs 变量用于控制传递给 ls 命令的参数: list-directory-brief-switches 为简洁列表的参数字符串(默认值为 "-CF"), list-directory-verbose-switches 为详细列表的参数字符串(默认值为 "-l")。
在详细格式的目录列表中,Emacs 还会额外显示该目录所在磁盘的剩余存储空间信息。
M-x delete-directory 命令会通过迷你缓冲区提示输入待删除的目录名,若目录为空则直接删除;若目录非空,会询问是否递归删除。在支持「Trash回收站」(或 "Recycle Bin废纸篓")功能的系统中,若将变量 delete-by-moving-to-trash 设为 t ,该命令会将指定目录移至回收站,而非直接永久删除。关于回收站的更多使用方法,参见《杂项文件操作》章节。
20.9. 文件比较
命令 M-x diff 会通过迷你缓冲区提示输入两个文件名,随后在名为 *diff* 的缓冲区中显示这两个文件的内容差异。该命令的实现依赖于调用系统的 diff 程序,执行时会使用变量 diff-switches 中配置的参数。此变量的值为字符串类型,默认值是 "-u" ,表示生成 统一上下文格式 的差异对比结果。关于 diff 程序的更多用法,参见《文件的对比与合并》中的「差异对比」章节。
diff 命令的输出结果会在 差异模式 (Diff mode)下展示,该模式为一种主模式,相关用法参见「差异模式」章节。
另一种功能更完善的替代方案是命令 M-x ediff ,相关用法参见《Ediff 手册》中的「Ediff」章节。
命令 M-x diff-backup 用于对比指定文件与其最新的备份文件;若你输入的是备份文件的名称,该命令则会对比此备份文件与其对应的源文件。除上述逻辑外,该命令的其他行为均与 M-x diff 一致。
命令 M-x diff-buffer-with-file 用于对比指定缓冲区与其对应的本地文件,通过该命令可查看:若保存此缓冲区,会对原文件做出哪些修改。
命令 M-x diff-buffers 用于对比两个指定缓冲区的内容。
命令 M-x compare-windows 用于对比当前窗口与 上一个选中的窗口 中的文本(关于 Emacs 的窗口相关知识,参见「多窗口」章节)。对比操作会从两个窗口的光标位置开始,且执行前会将两个缓冲区的初始光标位置分别压入各自的标记环(参见「标记环」章节);随后光标会在两个窗口中同步逐字符向后移动,直至找到不匹配的字符,命令随即退出。
若命令执行时,两个窗口的光标位置后紧跟的就是不匹配的文本, M-x compare-windows 会通过启发式算法,尝试在两个窗口中向后定位至匹配的文本位置,之后便退出。因此,若你重复执行该命令(参见「重复执行命令」章节),每次执行要么会跳过一段匹配的文本区域,要么会找到下一段匹配区域的起始位置。
若执行该命令时带上数字参数,对比过程会忽略空白字符的变化;若变量 compare-ignore-case 设为非nil值,对比还会忽略大小写的差异。若变量 compare-ignore-whitespace 设为非nil值, compare-windows 默认会忽略空白字符的变化,而带上前缀参数时,本次命令执行会临时关闭该忽略规则。
你可执行 M-x smerge-mode 开启 合并模式 (Smerge mode),这是一款用于编辑 diff3 程序输出结果的次要模式。 diff3 的输出通常出现在以下场景:在版本控制系统外执行更新操作时,因文件存在冲突的修改导致合并失败。合并模式提供了专用命令,可通过选择特定的修改内容来解决文件合并冲突。
关于文件合并的高级工具 Emerge ,可参见《使用 Emerge 合并文件》章节,该工具为文件合并提供了功能强大的操作界面。
20.10. 差异模式
差异模式(Diff mode)是一种主模式,专用于展示 M-x diff 及其他同类命令的输出结果。这类输出内容被称为 patch补丁文件 ,因为可将其传入系统的 patch 命令,实现指定修改的自动应用。若要手动启用差异模式,执行命令 M-x diff-mode 即可。
补丁文件中定义的修改会被划分为多个 修改块 (hunk),每个修改块是包含一行或多行变更内容的连续文本段,通常还会附带未修改的文本,为变更提供上下文参考。每个修改块前都会有 hunk header修改块头 ,用于指定该块修改在原文件和新文件中对应的行号。差异模式会对所有修改块头进行高亮,使其与修改块的实际内容区分开。
补丁文件中的第一个修改块前会有 文件头 ,展示文件新旧版本的名称及其时间戳。若一个补丁文件包含对多个文件的修改,每个文件的第一个修改块前都会配有对应的文件头。
你可像编辑普通缓冲区一样编辑差异模式的缓冲区(若缓冲区为只读状态,需先将其设为可写,参见《缓冲区的杂项操作》)。每当你编辑某个修改块时,差异模式会自动校正该块头中的行号,确保补丁文件始终有效,仍可被 patch 命令正常应用。若要关闭行号自动校正功能,将变量 diff-update-on-the-fly 设为 nil 即可。
差异模式会将修改块识别为编译器错误信息,让 M-g M-n 及其他处理错误信息的命令可对其生效(参见《编译模式》)。因此,你可使用编译模式的相关命令,跳转到修改对应的源码位置。
此外,差异模式还提供了以下命令,用于补丁文件的导航、操作与修改应用:
补丁导航命令
M-n跳转到下一个修改块的起始位置 (
diff-hunk-next) 。带上数字参数n时,向前跳至第n个修改块。默认情况下,Emacs 展示补丁时会自动细化所有修改块,以更精细的粒度高亮变更内容。若将变量
diff-refine设为navigation,则差异模式仅会对通过本命令或diff-hunk-prev跳转至的修改块进行细化处理。M-p- 跳转到上一个修改块的起始位置 (
diff-hunk-prev) 。带上数字参数n时,向后跳至第n个修改块。与M-n相同,若diff-refine设为navigation,本命令会对跳转至的修改块做细化处理。 M-}- 在多文件补丁中,跳转到下一个文件的起始位置 (
diff-file-next) 。带上数字参数n时,向前跳至第n个文件的起始位置。 M-{- 在多文件补丁中,跳转到上一个文件的起始位置 (
diff-file-prev) 。带上数字参数n时,向后跳至第n个文件的起始位置。
补丁内容删除命令
M-k- 删除光标所在位置的修改块 (
diff-hunk-kill) 。 M-K- 在多文件补丁中,删除当前文件对应的所有补丁内容 (
diff-file-kill) 。
补丁应用命令
C-c C-a- 将当前修改块应用至其目标文件 (
diff-apply-hunk) 。带上前缀参数C-u时,撤销该修改块的变更,即执行反向应用,将文件的 "new" 还原为 "old" 版本 。若变量diff-jump-to-old-file设为非nil,则该命令会将修改块应用至文件的「旧版本」而非新版本。 C-c RET a- 将缓冲区中所有修改块应用至对应文件 (
diff-apply-buffer) 。若所有差异均应用成功,会自动保存被修改的缓冲区。
补丁细化与查看命令
C-c C-b以更精细的粒度高亮光标所在修改块的变更内容 (
diff-refine-hunk) ,可清晰查看变更行中具体哪些字符发生了修改。由于差异模式默认会自动细化所有修改块,该命令主要在你将
diff-refine设为非默认值时使用。C-c C-c- 跳转到当前修改块对应的源码文件及行号 (
diff-goto-source) 。默认跳转到文件的「新版本」(文件头中首个展示的版本),带上前缀参数时则跳转到「旧版本」。若diff-jump-to-old-file设为非nil,命令默认跳转到「旧版本」,前缀参数的作用则会反转。若前缀参数为大于 8 的数字(如执行C-u C-u C-c C-c),该命令还会为下一次调用设置diff-jump-to-old-file的取值。若源码文件受版本控制系统管理(参见《版本控制》),命令默认跳转到工作区文件;带上前缀参数时,若光标位于旧版本的行号处,会跳转到文件的「旧版本修订版」(参见《查看与对比旧修订版》),否则跳转到「新版本修订版」。 C-c C-n- 将视图限定为当前修改块 (
diff-restrict-view,参见《内容窄化》)。带上前缀参数时,在多文件补丁中将视图限定为当前文件的补丁内容。执行C-x n w(widen) 可恢复完整视图。
补丁编辑与转换命令
C-c C-e- 基于当前补丁启动 Ediff 会话 (
diff-ediff-patch,参见《Ediff 手册》中的 Ediff 章节)。 C-c C-r- 反转整个缓冲区的对比方向 (
diff-reverse-direction) 。带上前缀参数时,仅反转当前区域内的对比方向(参见《标记与区域》)。对比方向反转,指修改块和文件头会被重新调整,生成可将文件「新版本」还原为「旧版本」的补丁。 C-c C-s- 将光标所在的修改块拆分为两个独立的修改块 (
diff-split-hunk) ,该命令会插入新的修改块头并修改原块头的信息。本命令适用于手动编辑补丁,且仅对diff程序通过-u或--unified选项生成的统一格式差异文件生效。若需拆分diff程序通过-c或--context选项生成的上下文格式差异文件,需先执行C-c C-u将缓冲区转换为统一格式。 C-c C-d- 将整个缓冲区转换为 上下文格式差异文件 (
diff-unified->context) 。带上前缀参数时,仅转换区域内的修改块。 C-c C-u- 将整个缓冲区转换为 统一格式差异文件 (
diff-context->unified) ;带上前缀参数时,将统一格式转换为上下文格式。若标记处于激活状态,仅转换区域内的修改块。 C-c C-l- 重新生成当前修改块 (
diff-refresh-hunk) 。 C-c C-w- 重新生成当前修改块,忽略空白字符的变更;带上非nil的前缀参数时,重新生成所有修改块 (
diff-ignore-whitespace-hunk) 。该命令会通过diff-ignore-whitespace-switches调用diff-command,该变量默认值为 '-b' ,即仅忽略空白字符的变更。
其他实用命令
C-x 4 A- 为每个修改块生成一份 ChangeLog 日志条目 (
diff-add-change-log-entries-other-window) ,效果类似C-x 4 a(参见《变更日志》),会创建一份变更日志的骨架,你可后续补充具体的变更描述。在差异模式中,C-x 4 a本身会针对当前修改块对应的文件生成日志,且从补丁文件中提取函数名,适用于为被补丁删除的函数创建日志条目。
补丁中的空白字符处理
补丁文件有时会在变更行中包含尾随空白字符,这属于无意且不必要的变更,可通过两种方式处理:
- 在差异缓冲区中启用空白字符模式(Whitespace mode)(参见《无用的空白字符》),模式会自动高亮变更行中的尾随空白字符;
- 执行命令
M-x diff-delete-trailing-whitespace,该命令会搜索补丁中所有变更行的尾随空白字符,并同时删除补丁文件和被补丁修改的源码文件中的这些空白字符。该命令不会自动保存所做的修改,你可自行决定是否保存(被修改的文件列表会在回显区显示);带上前缀参数时,命令会尝试修改原始的「旧版本」源码文件,而非被补丁修改后的「新版本」源码文件。
若变量 diff-font-lock-syntax 设为非nil,修改块中的源码片段会根据对应的主模式进行语法高亮。
20.11. 文件的复制、命名与重命名
Emacs 提供了多条用于文件复制、命名和重命名的命令。这类命令均会通过迷你缓冲区读取两个文件名 —— old 原文件名(或目标文件名)与 new 新文件名,再据此执行文件复制或文件名调整操作; 此类命令均不支持带通配符的文件名 。
在所有这类命令中,若传入的 new 参数仅为一个目录名(参见《Emacs Lisp 参考手册》中的「目录名」章节),则实际的 new新文件名会是该目录下、与 old原文件拥有相同非目录部分的名称。例如,执行命令 M-x rename-file RET ~/foo RET /tmp/ RET 会将 '~/foo' 重命名为 '/tmp/foo' 。在 GNU 及其他类 POSIX 系统中,目录名均以 '/' 结尾。
当新文件名已存在时,所有这类命令都会先请求用户确认,再执行后续操作。
M-x copy-file :将原文件的内容复制到新文件中。
M-x copy-directory :复制目录,功能与 Shell 命令 cp -r 类似。若 new 为目录名,该命令会创建 old 原目录的副本,并将其放入该目录中;若 new 非目录名,则会把 old 原目录的所有内容复制到一个以 new 新参数命名的新目录中。若变量 copy-directory-create-symlink 设为非 nil 且原目录为符号链接,该命令会直接复制此符号链接;若设为 nil (默认值),则会跟随符号链接,复制链接指向的实际内容。
M-x rename-file :将 old 原文件重命名为 new 新文件名。若 new 新文件名已存在,你必须输入 yes 确认,否则重命名操作不会执行;这是因为重命名会导致 new 新文件名原本对应的文件关联被覆盖。若原文件和新文件所在的文件系统不同,该命令会先将原文件复制到新位置,再删除原文件。
若文件受版本控制系统管理(参见《版本控制》章节),应使用 M-x vc-rename-file 而非 M-x rename-file 执行重命名操作,具体参见《删除和重命名受版本控制的文件》章节。
M-x add-name-to-file :为已有文件添加一个额外的文件名,且保留原文件名不变。该命令会通过为现有文件创建 硬链接 的方式生成新名称,新文件名必须与原文件位于 同一文件系统 中。在微软视窗系统中,该命令仅在文件存储于 NTFS 文件系统时生效;在 MS-DOS 系统及部分远程系统中,该命令会通过 复制文件 的方式实现多文件名关联。
M-x make-symbolic-link :创建一个名为新文件名的 符号链接 ,该链接指向指定的目标文件 / 目录。创建后,后续尝试打开该符号链接时,会访问其指向的目标文件 / 目录;若此时目标不存在,则会触发错误。该命令 不会展开 目标参数的路径,因此你可以指定相对路径作为链接的目标;但该命令会展开目标参数中开头的 '' (方便你指定主目录),并剔除开头的 '=/:=' (方便你指定以字面量 '' 或 '/:' 开头的相对路径),具体参见《带引用的文件名》章节。在微软视窗系统中,该命令仅在 Vista 及更高版本中生效;若符号链接的新文件名指向远程位置,命令是否生效取决于对应的远程系统类型。
20.12. 各类文件操作
Emacs 提供了执行各类其他文件操作的命令,这类命令均仅对单个文件生效, 不支持带通配符的文件名 。
M-x delete-file :会提示输入文件名并将其删除。若需删除同一目录下的多个文件,使用 Dired 目录编辑器会比该命令更便捷,参见《使用 Dired 删除文件》章节。
M-x move-file-to-trash :将文件移至系统 Trash回收站(或 Recycle Bin废纸篓)。该功能在多数操作系统中均支持,移至回收站的文件若后续反悔,可进行恢复(回收站文件的恢复方式因系统而异)。
默认情况下,Emacs 的删除类命令不会使用回收站功能。若希望常用删除命令在回收站可用时优先使用该功能,可将变量 delete-by-moving-to-trash 设为 t 。该设置会对 M-x delete-file 、 M-x delete-directory (参见《文件目录》章节),以及 Dired 中的所有删除命令(参见《使用 Dired 删除文件》章节)生效。为 M-x delete-file 或 M-x delete-directory 添加前缀参数时,无论 delete-by-moving-to-trash 如何设置,都会直接永久删除文件,而非移至回收站。
若你已开启 delete-by-moving-to-trash ,且希望在 Emacs 中手动删除回收站目录中的文件,使用 D (dired-do-delete) 这类命令的效果会很差(该操作仅会为文件重命名,无法实现实际删除)。若需要实现该需求,可在回收站目录中创建一个 .dir-locals.el 文件,并在其中写入以下内容:
((dired-mode . ((delete-by-moving-to-trash . nil))))
但需注意,若使用系统的「清空回收站」命令,该 .dir-locals.el 文件也可能被一并删除,因此该方法仅适用于手动删除回收站文件的场景。
若变量 remote-file-name-inhibit-delete-by-moving-to-trash 设为非nil值,远程文件将永远不会被移至回收站,而是直接被删除。
若文件受版本控制系统管理(参见《版本控制》章节),应使用 M-x vc-delete-file 而非 M-x delete-file 执行删除操作,参见《删除和重命名受版本控制的文件》章节。
M-x insert-file (快捷键亦为 C-x i ):将指定文件的内容副本插入到当前缓冲区的光标位置,光标会保留在插入内容的前方不变。插入内容的末尾位置会被添加到标记环中,且不会激活标记(参见《标记环》章节)。
M-x insert-file-literally :功能与 M-x insert-file 类似,区别在于该命令会 以字面形式插入文件内容 —— 将文件视为纯 ASCII 字符序列,不进行任何特殊的编码转换,与 M-x find-file-literally 命令的处理方式一致(参见《访问文件》章节)。
M-x write-region :是 M-x insert-file 的反向操作,会将缓冲区中选定区域的内容复制到指定文件中。 M-x append-to-file :将选定区域的文本追加到指定文件的末尾,参见《文本累积》章节。变量 write-region-inhibit-fsync 对这两个命令及文件保存操作均生效,参见《自定义文件保存方式》章节。
M-x set-file-modes :会先提示输入文件名,再提示输入文件权限模式,并将该模式应用到指定文件上。文件模式也被称为文件权限,用于决定文件的读、写、执行权限归属。该命令读取的文件模式,支持 chmod 系统命令的符号型或八进制型格式,例如 'u+x' 表示为文件的所有者添加执行权限。该命令在不支持文件模式的操作系统中不产生任何效果。 chmod 是该函数的便捷别名,可直接使用
20.13. 访问压缩文件
你在打开压缩文件时,Emacs 会自动对其进行解压缩;若你修改并保存该类文件,Emacs 会自动将其重新压缩。Emacs 通过 文件名 识别压缩文件,以 '.gz' 结尾的文件表示由 gzip 工具压缩的文件,其他后缀则对应其他压缩程序。
自动解压缩与重新压缩功能,适用于 Emacs 中所有涉及读取文件内容的操作,包括打开文件、保存文件、将文件内容插入缓冲区、加载文件以及字节编译文件。
若要关闭该功能,可执行命令 M-x auto-compression-mode ;若需永久禁用,可对变量 auto-compression-mode 进行自定义设置。
20.14. 文件归档包
文件名以 '.tar' 结尾的文件,通常是由 tar 程序创建的归档文件。Emacs 会以一种特殊的 Tar 模式 打开这类文件,该模式会以类 Dired 的形式展示归档内的内容(参见《目录编辑器 Dired》章节)。你可以像在 Dired 中一样浏览该列表,也能打开归档中包含的子文件,但并非所有 Dired 命令都能在 Tar 模式中使用。
若自动压缩模式处于启用状态(参见《访问压缩文件》章节),Tar 模式也会适用于压缩归档文件 —— 即扩展名是=.tgz= 、 .tar.Z 和 .tar.gz 的文件。
按下 e 、 f 键或 RET 回车键,均可将归档中的单个子文件提取并在独立缓冲区中打开。你可在该缓冲区中编辑文件,若保存此缓冲区,修改后的版本会替换 Tar 模式缓冲区中对应的原文件。用鼠标点击 Tar 模式缓冲区中的文件名,也能实现相同的提取编辑操作。按下 v 键会将文件提取到 视图模式 的缓冲区中打开(参见《视图模式》章节);按下 o 键会提取文件并在另一个窗口中展示,方便你同时编辑子文件和操作归档文件。
按下 I 键可向归档中添加一个新的普通文件,该文件初始状态为空,你可通过上述命令对其进行编辑。该命令会将新文件插入到当前光标所在文件的前方:在 Tar 模式缓冲区的首行执行此命令,新文件会成为归档中的第一个文件;在缓冲区末尾执行此命令,新文件则会成为归档的最后一个文件。
与 Dired 中相同,按下 d 键可为文件标记删除标识,后续按下 x 键即可执行删除;按下 u 键可取消文件的标记。按下 C 键可将归档中的文件复制到本地磁盘;按下 R 键可重命名归档内的文件。按下 g 键可根据磁盘上的原归档文件,恢复 Tar 模式缓冲区的内容。按下 M 、 G 、 O 键,可分别修改归档内文件的权限位、所属用户组和文件所有者。
保存 Tar 模式缓冲区时,Emacs 会根据你对归档内子文件所做的修改,在磁盘上生成一份新的归档文件。
使用 Tar 模式无需依赖 tar 程序 ——Emacs 会直接读取归档文件,但访问压缩归档文件时,仍需要对应的解压缩程序支持。
Emacs 还提供了一个独立但功能相近的 归档模式 (Archive mode),适用于 arc 、 jar 、 lzh 、 zip 、 rar 、 7z 、 zoo 格式的归档文件,同时也支持自解压可执行文件(exe 格式)。
归档模式的按键绑定与 Tar 模式基本一致,额外增加了 m 键(为文件标记标识,以便执行后续批量操作)和 M-DEL 键(取消所有已标记文件的标识)。此外,对于部分无法在单行内完整展示详情的归档格式,按下 a 键可切换是否显示文件的详细信息。需要注意的是,重命名子文件、修改文件权限或所有者这类操作,仅对部分归档格式提供支持。
与 Tar 模式不同,归档模式会调用对应的归档程序来完成归档的解包和重新打包操作。不过仅查看归档的目录内容时,无需依赖这些程序,只有提取或操作归档内的子文件时才会用到。相关程序的名称及其执行参数,可在 'Archive' 自定义组中进行设置(参见《自定义组》章节)。
20.15. 远程文件
你可通过一种特殊的文件名语法,引用其他机器上的文件,语法格式如下:
/method:host:filename /method:user@host:filename /method:user@host#port:filename
为执行远程文件访问请求,Emacs 会调用 ssh 等远程登录程序。在文件名中,你必须明确指定使用的访问方式 —— 例如 /ssh:user@host:filename 表示使用 ssh 方式访问。若在文件名中指定伪访问方式用 '-',Emacs 会按以下规则自动选择访问方式:
- 若主机名以 'ftp.' (带句点)开头,使用
FTP方式; - 若用户名设为 'ftp' 或 'anonymous' ,使用
FTP方式; - 若变量
tramp-default-method设为ftp,使用FTP方式; - 若
ssh-agent进程正在运行,使用scp方式; - 其他情况,默认使用
ssh方式。
执行命令 M-x inhibit-remote-files 可彻底关闭远程文件名功能;也可在文件名前添加 '/:' 进行引用(参见《带引用的文件名》),单独禁用某一次的远程文件访问。
通过 FTP 方式的远程文件访问,由 Ange-FTP 包处理,相关用法见下文说明;其他访问方式的远程文件操作,均由 Tramp 包处理,该包有独立的使用手册,参见《Tramp 手册》。
使用 Ange-FTP 包访问远程文件时,若远程文件名中指定了用户名 user ,Emacs 会使用该用户名通过 FTP 登录;若未指定用户名,默认使用本地系统的用户名登录。若将变量 ange-ftp-default-user 设为某个字符串,Emacs 会使用该字符串作为默认登录用户名。登录过程中,Emacs 可能会提示你输入密码。
出于性能考虑,Emacs 默认不会为通过 FTP 访问的远程文件创建备份文件。若需要开启该功能,将变量 ange-ftp-make-backup-files 设为非nil值即可。
远程文件的自动保存文件,默认会创建在本地机器的临时文件目录中,具体由变量 auto-save-file-name-transforms 指定,参见《自动保存文件》章节。
访问支持匿名 FTP 的远程文件时,需使用专用用户名 anonymous 或 ftp ,这类用户名的密码会做特殊处理,具体由变量 ange-ftp-generate-anonymous-password 控制:
- 若该变量值为一个字符串,直接使用该字符串作为密码;
- 若该变量值为非nil(默认值),使用
user-mail-address变量的值作为密码; - 若该变量值为
nil,Emacs 会像常规操作一样,提示你手动输入密码(参见《输入密码》)。
有时因中间的防火墙出于安全考虑拦截了连接,你无法直接访问远程机器上的文件。若你能 登录某台网关机器 ,且该机器可访问目标文件、其 FTP 服务器支持网关功能,仍可正常使用远程文件名访问 —— 只需将变量 ange-ftp-gateway-host 设为该网关机器的名称,并将 ange-ftp-smart-gateway 设为 t 即可。若上述条件不满足,也可通过其他复杂步骤实现远程文件访问,执行命令 M-x finder-commentary RET ange-ftp RET 可查看详细操作说明。
20.16. 带引用的文件名
你可以对绝对文件名进行 quote引用处理 ,避免其中的特殊字符和语法触发相应的特殊效果,具体方法是在文件名开头添加 '/:' 。
例如,若某个本地文件名的格式看似远程文件路径,可通过引用将其标记为本地文件,防止 Emacs 将其当作远程文件名处理。比如你有一个名为 /foo: 的目录,其中包含文件 'bar' ,在 Emacs 中可通过 '/:/foo:/bar' 引用该文件。
若仅需对远程文件名的本地路径部分中的特殊字符进行引用,可仅为该部分添加引用标识。例如 /ssh:baz:/:/foo:/bar ,表示访问主机 baz 上 /foo: 目录中的 bar 文件。
'/:' 也能阻止 '~' 被解析为用户主目录的特殊标识。例如 /:/tmp/~hack ,指向的是 /tmp 目录下一个名为 ~hack 的文件。
在迷你缓冲区中输入包含 '$' 的文件名时,也可通过 '/:' 进行引用,但需 注意 '/:' 必须置于迷你缓冲区输入内容的开头(也可将每个 '$' 重复输入一次来实现相同效果,参见《含 $ 的文件名》章节)。
访问文件时,还能通过 '/:' 对通配符进行引用。例如输入 /:/tmp/foo*bar ,会直接访问 /tmp 目录下的 foo*bar 文件(而非将 '*' 当作通配符匹配)。
实现上述效果还有另一种方法:输入 /tmp/foo[*]bar ,这种通配符写法仅会匹配 /tmp/foo*bar 这一个文件。不过在很多情况下,无需对通配符进行引用,因为即便不引用,也能得到预期结果。例如,若 /tmp 目录中唯一以 foo 开头、以 bar 结尾的文件就是 foo*bar ,那么直接输入 /tmp/foo*bar ,Emacs 也会仅访问该文件。
20.17. 文件名缓存
你可以使用 file name cache文件名缓存 功能,轻松通过文件名定位文件,而无需准确记住文件的存放位置。在迷你缓冲区中输入文件名时,按下 C-TAB (file-cache-minibuffer-complete) 可通过文件名缓存完成补全。重复按下 C-TAB ,会循环展示你初始输入内容的所有可能补全结果。(请注意,在大多数文本终端中无法输入 C-TAB 组合键。)
文件名缓存不会自动填充内容,需通过以下命令将文件名手动加载至缓存中:
M-x file-cache-add-directory RET 目录名 RET- 将指定目录下的所有文件名添加至文件名缓存。
M-x file-cache-add-directory-using-find RET 目录名 RET- 将指定目录及其所有嵌套子目录下的所有文件名添加至文件名缓存。
M-x file-cache-add-directory-using-locate RET 目录名 RET- 通过
locate命令查找指定目录及其所有嵌套子目录下的所有文件,并将这些文件名添加至文件名缓存。 M-x file-cache-add-directory-list RET 变量名 RET- 将指定变量中列出的所有目录下的文件名,全部添加至文件名缓存。该变量需为 Lisp 变量,且其值为目录列表,类似
load-path变量的格式。 M-x file-cache-clear-cache RET- 清空缓存,即移除缓存中的所有文件名。
文件名缓存不具备持久性:其内容仅在当前 Emacs 会话期间保留和维护。你可通过 file-cache-display 命令查看缓存中的内容。
20.18. 查找文件的便捷功能
本节将介绍一些实用工具,可用于查找近期打开的文件、从缓冲区中读取文件名。
执行 M-x recentf-mode 启用 最近文件模式 后,Emacs 会自动维护一份近期打开的文件列表。要从该列表中打开文件,可使用 M-x recentf-open 命令。开启此模式后,'File' 菜单中会新增一个子菜单,你可通过该子菜单快速访问列表中的文件。 M-x recentf-save-list 命令可将当前的最近文件列表(recentf-list) 保存至文件中, M-x recentf-edit-list 命令则可对该列表进行编辑。
若你使用远程文件,可自定义变量 remote-file-name-access-timeout ,该变量用于设置超时秒数 —— 超过此时间后,Emacs 会停止检查是否将该远程文件加入最近文件列表,以此避免程序出现阻塞。
M-x ffap 命令是 find-file 命令的增强版,配备了更智能的启发式默认规则(参见《访问光标位置处的文件和网址》),其匹配逻辑通常基于光标所在位置的文本内容。 部分补全模式 提供了其他扩展 find-file 的功能,可与 ffap 命令配合使用,相关说明参见《补全选项》。
20.19. 查看图像文件
打开图像文件时,Emacs 会自动启用 Image mode图像模式 。在该主模式下,按下 C-c C-c (image-toggle-display) 可在两种显示状态间切换:一是在 Emacs 缓冲区中以图像形式展示文件,二是展示文件的底层文本(或原始字节)格式。此外,按下 C-c C-x (image-toggle-hex-display) ,可在图像显示与十六进制格式显示间切换。 仅当 Emacs 编译时启用了对应图像的显示支持 ,才能以图像形式展示文件。
若显示的图像宽或高超过所在窗口尺寸,使用常规的光标移动按键( C-f 、 C-p 等)可切换显示图像的不同区域。不过 Emacs 默认会自动调整图像大小以适配窗口,因此仅当你通过 image-auto-resize 和 image-auto-resize-on-window-resize 选项自定义了默认行为后,才需要手动移动查看。
你可通过以下命令手动调整图像尺寸:
- 按下
s w执行image-transform-fit-to-window,将图像同时适配窗口的高度和宽度; - 按下
s p执行image-transform-set-percent,按原始尺寸的百分比缩放图像; - 按下
s s执行image-transform-set-scale,指定缩放系数调整图像大小; - 按下
s 0执行image-transform-reset-to-initial,或按下s o执行image-transform-reset-to-original,将所有图像变换恢复至初始状态。
按下 n (image-next-file) 和 p (image-previous-file) ,可分别打开同一目录中的下一个和上一个图像文件。这两个命令会通过 父级 Dired 缓冲区 确定对应的图像文件,在从归档文件(如 zip 、 tar 文件)中打开图像时也能生效,此时会读取归档模式缓冲区的信息;若未找到归档或 Dired 父级缓冲区,Emacs 会自动打开一个 Dired 缓冲区。
浏览图像时,有时需要标记文件以便后续处理(例如选择一组图像复制到其他位置):按下 m (image-mode-mark-file) ,会在所有显示该文件所在目录的 Dired 缓冲区中标记当前文件;若未打开相关 Dired 缓冲区,会在新缓冲区中打开该目录并完成标记。按下 u (image-mode-unmark-file) 可取消文件标记。按下 w (image-mode-copy-file-name-as-kill) ,可将当前缓冲区对应的文件名复制到杀环中。
若图像支持动画效果,按下 RET (image-toggle-animation) 可启动或停止动画;默认情况下动画仅播放一次,若将 image-animate-loop 设为非nil值则会循环播放。按下 f (image-next-frame) 和 b (image-previous-frame) 可逐帧浏览动画,为命令添加数字前缀可一次跳过多帧;按下 F (image-goto-frame) 可跳转到指定帧(帧的索引从 1 开始)。按下 + (image-increase-speed) 加快动画播放速度,按下 - (image-decrease-speed) 减慢速度,按下 r (image-reverse-speed) 反转播放方向,按下 a 0 (image-reset-speed) 将速度恢复为默认值。
除上述图像模式专属的按键绑定外,当光标位于 Emacs 任意缓冲区中的图像上或图像内部时,还可使用以下专用按键:
i +- 将图像放大 20% (
image-increase-size) ,数字前缀可自定义放大比例 —— 前缀为n时,图像尺寸会乘以1 + n/10,例如C-u 5 i +表示将图像放大 50%。 i -- 将图像缩小 20% (
image-decrease-size) ,数字前缀可自定义缩小比例 —— 前缀为n时,图像尺寸会乘以1 - n/10,例如C-u 3 i -表示将图像缩小 30%。 i r- 将图像顺时针旋转 90 度 (
image-rotate) ,添加前缀参数则为逆时针旋转 90 度(该命令对分片图像无效)。 i h- 将图像水平翻转 (
image-flip-horizontally) ,效果等同于通过垂直镜面反射图像(该命令对分片图像无效)。 i v- 将图像垂直翻转 (
image-flip-vertically) ,效果等同于通过水平镜面反射图像(该命令对分片图像无效)。 i o- 将图像保存至文件 (
image-save) ,命令会提示输入保存的文件名。 i c- 裁剪图像 (
image-crop) , 仅当系统安装了可用于图像裁剪的外部程序 时该命令才可用,image-crop-crop-command变量用于指定裁剪程序,默认使用ImageMagick的convert程序。执行该命令后,图像上会叠加一个矩形裁剪框,可通过鼠标移动和调整裁剪框大小;按下m可将鼠标操作从调整尺寸切换为移动裁剪框,按下s可将裁剪框锁定为正方形;调整满意后按下RET回车键完成裁剪,按下q则退出不执行裁剪,裁剪后的图像可通过i o或M-x image-save保存。 i x- 从图像中裁剪出矩形区域并删除 (
image-cut) ,该命令的使用条件与image-crop一致(需通过image-crop-cut-command指定外部程序),与裁剪不同的是,该命令会删除裁剪框内的图像区域,并将该区域填充为image-cut-color指定的颜色;添加前缀参数时,命令会提示自定义填充颜色。
上述图像尺寸调整和旋转命令支持 连续操作 ,即首次按下 i 前缀后,后续可直接按 + 、 - 、 r 等键继续调整,无需重复输入 i 。
若 Emacs 编译时启用了 ImageMagick 库 支持,可通过该库渲染多种格式的图像。 imagemagick-enabled-types 变量列出了 Emacs 可使用 ImageMagick 渲染的图像类型,列表中的每个元素为 ImageMagick 内部的图像类型名(可为符号或对应字符串,例如 BMP 对应 .bmp 格式);将该变量设为 t ,可让 Emacs 对所有支持的图像类型均使用 ImageMagick 渲染。 imagemagick-types-inhibit 变量列出了 禁止 使用 ImageMagick 渲染的图像类型(不受 imagemagick-enabled-types 取值影响),默认列表包含 C、HTML 等类型 —— 这类类型虽能被 ImageMagick 渲染为图像,但 Emacs 不应做此处理;将该变量设为 t ,可彻底禁用 ImageMagick 的图像渲染功能。
若 Emacs 本身不支持某类图像格式,且 image-use-external-converter 设为非nil值,Emacs 会尝试查找外部工具,将该格式图像转换为 PNG 格式后再进行显示,目前支持的转换工具包括 GraphicsMagick、ImageMagick 和 ffmpeg。
此外,你可为特定图像格式添加自定义处理程序,通过 image-converter-add-handler 函数即可实现。例如,要将 Krita 文件以普通图像形式查看,可执行以下代码:
(image-converter-add-handler "kra" (lambda (file data-p) (if data-p (error "Can't decode non-files") (call-process "unzip" nil t nil "-qq" "-c" "-x" file "mergedimage.png"))))
该函数接收两个参数:第一个为文件后缀名,第二个为执行转换的处理函数。处理函数同样接收两个参数:第一个为文件名或图像数据字符串,第二个为布尔值(标识第一个参数是数据还是文件名),处理函数需在当前缓冲区中输出 image-convert-to-format 格式的图像数据。
你还可使用Image-Dired 包以缩略图形式查看图像,详见《在 Dired 中查看图像缩略图》章节。
20.20. 文件集(Filesets)
若你需要定期编辑某一组特定文件,可将其定义为一个 fileset文件集 ,通过文件集可一次性对所有文件执行打开、查询替换、执行 Shell 命令等操作。要使用文件集功能,需先在初始化文件中添加表达式 (filesets-init) (参见《Emacs 初始化文件》章节),该操作会在菜单栏的 'File' 菜单下添加一个 'Filesets' 子菜单。
定义文件集最简便的方式是逐个向其中添加文件:打开目标文件后,执行 M-x filesets-add-buffer RET 文件名 RET ,即可将当前文件添加至指定名称的文件集;若该名称的文件集尚未存在,会自动创建一个新文件集,且初始仅包含当前文件。执行 M-x filesets-remove-buffer 命令,可将当前文件从对应文件集中移除。
你也可通过 M-x filesets-edit 命令(或从「Filesets」菜单中选择「Edit Filesets」选项)直接编辑文件集列表,编辑操作会在自定义缓冲区中完成(参见《简易自定义界面》章节)。默认情况下,文件集是一个简单的文件列表,你也可将文件集定义为 匹配文件名的正则表达式 ,自定义缓冲区中会展示这类复杂文件集的示例。若希望在后续的 Emacs 会话中继续使用这些文件集,记得选择「Save for future sessions保存供后续会话使用」。
执行 M-x filesets-open 命令可打开某个文件集中的所有文件, M-x filesets-close 命令可关闭文件集中的所有文件;执行 M-x filesets-run-cmd 命令,可对某个文件集中的所有文件执行指定的 Shell 命令。这些命令也可通过「Filesets」菜单调用,菜单中会为每个已存在的文件集单独创建一个子菜单。
另有一种不同概念的文件集可参见《版本控制》章节,这类文件集是为执行版本控制操作而组合的文件组, 无命名且不会在 Emacs 会话间持久保存 。
21. 多缓冲区的使用
你在 Emacs 中编辑的文本,会存储在一个名为 buffer缓冲区 的对象中。每次打开文件时,Emacs 都会创建一个缓冲区来存放该文件的文本;每次调用 Dired 目录编辑器时,会生成一个缓冲区来展示目录列表;使用 C-x m 发送消息时,消息文本也会由一个缓冲区承载;查询命令的帮助文档时,文档内容会显示在名为 *Help* 的缓冲区中。
缓冲区会在使用期间一直存在,当不再需要时,会由用户(参见《删除缓冲区》)或 Emacs 自身删除(即「销毁」,例如退出 Emacs 时,参见《退出 Emacs》)。
每个缓冲区都有一个 唯一的名称 ,名称可包含任意字符且无长度限制。当缓冲区在窗口中显示时,其名称会出现在模式行中(参见《模式行》)。缓冲区名称区分大小写,大部分缓冲区由打开文件创建,名称也派生自对应文件名;你也可以创建一个自定义名称的空缓冲区。刚启动的 Emacs 会自带多个缓冲区,其中包含一个名为 *scratch* 的临时缓冲区,可用于执行 Lisp 表达式,该缓冲区不关联任何文件(参见《Lisp 交互缓冲区》)。
在任意时刻,Emacs 中 有且仅有一个缓冲区处于选中状态 ,该缓冲区被称为 current buffer当前缓冲区 。我们常说某条命令「作用于缓冲区」,实际指该命令作用于当前缓冲区。当 Emacs 中只有一个窗口时,该窗口显示的缓冲区即为当前缓冲区;当存在多个窗口时, select window选中窗口 中显示的缓冲区为当前缓冲区(参见《多窗口》)。
缓冲区的内容由一串字符组成,每个字符可选择性地附带一组 文本属性 (参见《文本属性》),用于记录该字符的额外信息。
除文本内容外,每个缓冲区还会记录多项相关信息,包括其关联的文件(若有)、是否被修改、当前启用的 major mode主模式 和 minor mode次要模式 (参见《主模式与次要模式》)等。这些信息都存储在 缓冲区局部变量 中 —— 这类变量可在不同缓冲区中拥有不同的值(参见《局部变量》)。
缓冲区的大小存在上限,该上限由 Emacs 整数类型所能表示的最大缓冲区位置决定,因为 Emacs 会通过该数据类型跟踪缓冲区位置。对于常见的 64 位系统,缓冲区的最大容量为 2^{61} - 2 字节,约 2 艾字节(EiB);对于常见的 32 位系统,最大容量通常为 2^{29} - 2 字节,约 512 兆字节(MiB)。此外,缓冲区的实际大小还会受系统内存容量的限制。
21.1. 创建与选择缓冲区
C-x b buffer RET- 选中或创建指定名称的缓冲区 (
switch-to-buffer) 。 C-x 4 b buffer RET- 功能同上,在另一个窗口中选中该缓冲区 (
switch-to-buffer-other-window) 。 C-x 5 b buffer RET- 功能同上,在独立的框架中选中该缓冲区 (
switch-to-buffer-other-frame) 。 C-x LEFT- 选中缓冲区列表中的上一个缓冲区 (
previous-buffer) 。 C-x RIGHT- 选中缓冲区列表中的下一个缓冲区 (
next-buffer) 。 C-u M-g M-gC-u M-g g- 读取数字
n,在另一个窗口中,将光标移至 除当前缓冲区外最近选中的缓冲区 的第n行。
C-x b (switch-to-buffer) 命令会通过迷你缓冲区读取缓冲区名称,随后将该缓冲区设为当前缓冲区,并在当前选中的窗口中展示。若输入为空,则会选中 当前未在任何窗口中显示、且最近一次被设为当前缓冲区 的那个缓冲区。
输入缓冲区名称时,可使用常规的补全和历史记录命令(参见《迷你缓冲区》)。注意, C-x b 及相关命令在迷你缓冲区补全时,采用 permissive completion with confirmation 带确认的宽松补全 规则:若按下 RET 回车时,迷你缓冲区中的文本对应的是不存在的缓冲区,Emacs 会打印 '[Confirm]',你需要再次按下 RET 回车,确认创建该名称的缓冲区。详细规则参见《补全的退出方式》,其他补全选项与功能参见《补全选项》。
若指定的缓冲区不存在, C-x b 会创建一个 未关联任何文件的新空缓冲区 ,并将其选中供编辑使用。新缓冲区的主模式由变量 major-mode 的默认值决定,默认为主基础模式(Fundamental mode)(参见《主模式》)。创建新缓冲区的常见用途之一,是记录临时笔记。若你尝试保存该缓冲区,Emacs 会提示输入文件名,且会根据该文件名重新确定缓冲区的主模式(参见《文件模式的选择》)。
若需要在少数几个缓冲区之间快速切换,可使用 C-x LEFT 和 C-x RIGHT 命令。 C-x 左方向键 (previous-buffer) 会选中 上一个缓冲区 (按当前框架中最近的选中顺序), C-x 右方向键 (next-buffer) 则按相反顺序切换缓冲区。两个命令均支持数字前缀参数,用于指定切换的次数。
若要在 非当前窗口 中选中缓冲区(参见《多窗口》),可输入 C-x 4 b (switch-to-buffer-other-window) 。该命令会通过迷你缓冲区提示输入缓冲区名称,在另一个窗口中展示该缓冲区,并将该窗口设为选中状态。
类似地, C-x 5 b (switch-to-buffer-other-frame) 会提示输入缓冲区名称,在另一个框架中展示该缓冲区(参见《框架与图形化显示》),并选中该框架。若该缓冲区已在其他框架的某个窗口中显示,Emacs 会直接选中该窗口和框架,而非创建新框架。
关于 C-x 4 b 和 C-x 5 b 命令如何选择展示缓冲区的窗口和 / 或框架,参见《在窗口中展示缓冲区》。
此外, C-x C-f 及其他所有打开文件的命令,也可用于切换至已存在的、关联该文件的缓冲区(参见《访问文件》)。
C-u M-g M-g (即带普通前缀参数的 goto-line 命令),会通过迷你缓冲区读取数字 n ,在另一个窗口中选中 除当前缓冲区外最近选中的缓冲区 ,并将光标移至该缓冲区的第 n 行开头。该命令主要适用于 某一缓冲区中引用了另一缓冲区行号 的场景:若光标位于某个数字上,或紧邻数字后方, goto-line 会将该数字作为 n 的默认值。注意,非单纯 C-u 的前缀参数,执行效果会不同:例如 C-u 4 M-g M-g ,会直接将光标移至 当前缓冲区 的第 4 行,而不会从迷你缓冲区读取数字。(注意,无前缀参数的 M-g M-g ,会读取数字 n 并将光标移至当前缓冲区的第 n 行,参见《移动光标位置》。)
Emacs 会将 以空格开头 的缓冲区名用于内部操作,这类缓冲区会被做一些细微的特殊处理 —— 例如,默认不会记录撤销信息。建议你避免使用此类名称命名自己的缓冲区。
21.2. 列出已有缓冲区
C-x C-b- 列出所有已存在的缓冲区 (
list-buffers) 。
键入 C-x C-b 可展示所有已存在的缓冲区列表,该操作会在名为 *Buffer List* 的缓冲区中弹出一个缓冲区菜单。列表中的每一行对应一个缓冲区,展示其缓冲区名、大小、主模式以及关联的文件。缓冲区按 最近成为当前缓冲区的顺序 排列,最近使用的缓冲区会显示在最前方。本节介绍缓冲区列表的展示形式及列表中各类标识的含义; *Buffer List* 缓冲区的专用模式及可使用的相关命令,详见《对多个缓冲区执行操作》章节。
列表每行第一个字段中的 '.' 表示该缓冲区为 当前缓冲区 , '%' 表示该缓冲区为 只读缓冲区 , '*' 表示该缓冲区 已被修改 。若有多个缓冲区标记为已修改,便是时候使用 C-x s 命令保存部分缓冲区了(参见《文件保存相关命令》)。以下是缓冲区列表的示例:
CRM Buffer Size Mode File
. * .emacs 3294 ELisp/l ~/.emacs
% *Help* 101 Help
search.c 86055 C ~/cvs/emacs/src/search.c
% src 20959 Dired by name ~/cvs/emacs/src/
* *mail* 42 Mail
% HELLO 1607 Fundamental ~/cvs/emacs/etc/HELLO
% NEWS 481184 Outline ~/cvs/emacs/etc/NEWS
*scratch* 191 Lisp Interaction
* *Messages* 1554 Messages
*Help* 缓冲区由帮助查询操作创建(参见《帮助功能》),该缓冲区未关联任何文件; src 缓冲区是对目录 ~/cvs/emacs/src/ 执行 Dired 目录浏览操作后生成的。为该命令添加前缀参数(如执行 C-u C-x C-b ),可仅列出 关联了文件 的缓冲区。
默认情况下, list-buffers 命令会 忽略名称以空格开头 的缓冲区(关联了文件的除外),这类缓冲区为 Emacs 的内部使用缓冲区(可通过 I 命令取消该限制,详见《对多个缓冲区执行操作》章节)。
21.3. 缓冲区的各类杂项操作
C-x C-q- 切换缓冲区的只读状态 (
read-only-mode) 。 C-x x r RET 新缓冲区名 RET- 修改当前缓冲区的名称 (
rename-buffer) 。 C-x x u- 为当前缓冲区重命名,在原名称后添加数字后缀以保证唯一性 (
rename-uniquely) 。 M-x view-buffer RET buffer RET- 滚动浏览指定缓冲区的内容(参见「视图模式」章节)。
缓冲区可设置为 read-only只读状态 ,处于该状态时,无法执行插入或删除文本的相关命令。(但部分其他命令如 C-x RET f ,仍可将其标记为已修改,参见「为文件文本指定编码系统」章节。)模式行的左侧边缘会显示 '%%' 或 '%*' 标识,以此表示该缓冲区为只读缓冲区(参见「模式行」章节)。只读缓冲区通常由 Dired、Rmail 这类子系统创建,这类子系统会提供专门的命令对缓冲区文本进行操作;若打开的文件在系统权限中被设置为不可写,对应的缓冲区也会被设为只读。
执行 C-x C-q (read-only-mode) ,可将只读缓冲区切换为可写状态,也可将可写缓冲区设为只读状态。该命令的实现原理是修改 buffer-read-only 变量 —— 该变量为缓冲区局部变量,若其值为非nil,则对应缓冲区为只读。若将 view-read-only 选项设为非nil,通过 C-x C-q 将缓冲区设为只读时,会同时为该缓冲区启用视图模式(参见「视图模式」章节)。
执行 C-x x r (rename-buffer) 命令可修改当前缓冲区的名称,需在迷你缓冲区中输入新名称,该命令无默认名称可选;若指定的新名称已被其他缓冲区使用,会触发错误且重命名操作失败。
执行 C-x x u (rename-uniquely) 命令会为当前缓冲区生成一个相似的唯一名称,具体方式是在原名称后添加数字后缀,该命令无需传入参数。此命令适用于创建多个 Shell 缓冲区的场景:若先为 *shell* 缓冲区重命名,再执行 M-x shell 命令,Emacs 会创建一个新的 *shell* 缓冲区,而原 Shell 缓冲区会以新名称继续存在。该方法也适用于邮件缓冲区、编译缓冲区,以及 Emacs 中大多数会创建特定名称专用缓冲区的功能。(对于部分此类功能,如 M-x compile 、 M-x grep ,再次执行命令前需先切换至其他缓冲区,否则即便原缓冲区已重命名,该功能仍会复用当前缓冲区。)
执行 M-x append-to-buffer 和 C-x x i (insert-buffer) 命令,也可实现将文本从一个缓冲区复制至另一个缓冲区的操作(参见「文本累积」章节)。
21.4. 关闭缓冲区
若 Emacs 会话持续运行一段时间,可能会累积大量缓冲区,此时删除不再需要的缓冲区会让操作更便捷(部分其他编辑器将此操作称为 “close”,会提及 “closing the buffer” 或 “closing the file”)。在大多数操作系统中,删除缓冲区会将 Emacs 为该缓冲区占用的内存释放给系统,供其他程序使用。以下是用于删除缓冲区的相关命令:
C-x k buffer RET- 删除指定缓冲区 (
kill-buffer) 。 M-x kill-some-buffers- 逐个确认并删除缓冲区。
M-x kill-matching-buffers- 确认并删除所有名称匹配指定正则表达式的缓冲区。
M-x kill-matching-buffers-no-ask- 功能与
kill-matching-buffers类似,无需确认直接删除。
C-x k (kill-buffer) 命令用于删除单个缓冲区,需在迷你缓冲区中指定缓冲区名称;若直接在迷你缓冲区中按 RET 回车,默认删除 当前缓冲区 。若删除的是当前缓冲区,Emacs 会将另一个缓冲区设为当前缓冲区 —— 该缓冲区为近期曾作为当前缓冲区、且当前未在任何窗口中显示的缓冲区。若要删除的是关联了文件且已被修改的缓冲区,必须输入 yes 确认后,删除操作才会执行。
M-x kill-some-buffers 命令会逐个对所有缓冲区进行删除确认,输入 yes 即表示删除该缓冲区,效果与 kill-buffer 命令一致。该命令会忽略 Emacs 内部使用的、名称以空格开头的缓冲区。
M-x kill-matching-buffers 命令会先提示输入一个正则表达式,随后删除所有名称匹配该表达式的缓冲区(参见《正则表达式的语法》)。与 kill-some-buffers 命令相同,该命令在删除每个缓冲区前都会要求确认,且默认忽略 Emacs 内部使用的、名称以空格开头的缓冲区;若为该命令添加前缀参数,则会一并删除内部缓冲区。 M-x kill-matching-buffers-no-ask 命令的功能与 kill-matching-buffers 一致,区别是删除匹配的缓冲区时 无需逐次确认 。
使用缓冲区菜单功能也能便捷地删除多个缓冲区,详见《对多个缓冲区执行操作》章节。
若希望在每次删除缓冲区时执行自定义操作,可向 kill-buffer-hook 钩子中添加钩子函数(参见《钩子》章节)。
许多用户会让 Emacs 会话连续运行数日,此时会话中会堆积数天前使用过的缓冲区, M-x clean-buffer-list 命令是清理这类缓冲区的便捷方式 —— 该命令会删除所有长期未使用且未被修改的缓冲区。默认情况下,若某个普通缓冲区已 3 天未被显示,就会被该命令删除;你也可以指定某些缓冲区为 禁止自动删除 ,同时将另一些缓冲区的自动删除阈值设为仅一小时未使用。该命令的这些默认规则及其他行为,均可通过自定义 clean-buffer-list 文档字符串中描述的相关选项来调整。
你也可以启用 Midnight mode 午夜模式 ,让系统每日自动执行缓冲区清理操作。午夜模式会在每日午夜触发,执行 clean-buffer-list 命令,或你添加到 midnight-hook 普通钩子中的其他自定义函数(参见《钩子》章节)。启用该模式的方法是,在自定义缓冲区中将变量 midnight-mode 设为 t (参见《简易自定义界面》章节)。
21.5. 对多个缓冲区执行批量操作
M-x buffer-menu- 打开缓冲区菜单,可编辑 Emacs 所有缓冲区的列表。
M-x buffer-menu-other-window- 功能同上,在另一个窗口中打开缓冲区菜单。
通过 C-x C-b 打开的缓冲区菜单(参见《列出已有缓冲区》)并非仅用于罗列缓冲区,还可通过类 Dired 的操作界面(参见《目录编辑器 Dired》),对缓冲区执行保存、删除、显示等各类操作。
使用缓冲区菜单的操作方法:按下 C-x C-b 后,切换至显示 *Buffer List* 缓冲区的窗口即可;也可直接输入 M-x buffer-menu ,在当前选中的窗口中打开缓冲区菜单;此外, M-x buffer-menu-other-window 命令会在另一个窗口中打开缓冲区菜单并自动选中该窗口。
缓冲区菜单为只读缓冲区,仅可通过本节介绍的专用命令进行操作,常规的光标移动命令在该缓冲区中均适用。以下命令均作用于光标所在行对应的缓冲区:
d- 为缓冲区标记删除(killing)标识,随后将光标移至下一行 (
Buffer-menu-delete) 。标记后,该行缓冲区名称前会显示字符 'D' 作为删除标识,实际删除操作仅在按下x命令后执行(见下文)。 C-d- 功能与
d命令一致,区别是标记后将光标向上移动 (Buffer-menu-delete-backwards) 。 s- 为缓冲区标记保存标识 (
Buffer-menu-save) 。标记后,该行缓冲区名称前会显示字符S作为保存标识,实际保存操作仅在按下x命令后执行。可对同一个缓冲区同时标记保存和删除标识。 x- 执行所有已标记的删除和保存操作 (
Buffer-menu-execute) 。 u- 清除光标所在行的所有标识,随后将光标向下移动 (
Buffer-menu-unmark) 。若添加数字前缀参数,清除标识后光标会向上移动。 DEL- 将光标移至上一行,并清除该行的所有标识 (
Buffer-menu-backup-unmark) 。 M-DEL- 清除所有行中指定类型的标识 (
Buffer-menu-unmark-all-buffers) 。按下该命令后会提示输入一个字符,随后清除所有以该字符为标识的标记;直接按RET回车键则清除所有标识。 U- 清除所有行的所有标识 (
Buffer-menu-unmark-all) 。
上述的标识清除命令、 d 命令和 C-d 命令,均支持数字前缀参数作为重复执行的次数。
以下命令会立即对光标所在行对应的缓冲区执行操作,且均支持数字前缀参数作为重复执行的次数:
~- 将缓冲区标记为未修改状态 (
Buffer-menu-not-modified) ,参见《文件保存相关命令》。 %- 切换缓冲区的只读状态 (
Buffer-menu-toggle-read-only) ,参见《缓冲区的杂项操作》。 t- 将该缓冲区作为标签表打开 (
Buffer-menu-visit-tags-table) ,参见《选择标签表》。
以下命令用于选中单个或多个其他缓冲区:
q- 退出缓冲区菜单 (
quit-window) ,原窗口会显示最近一次可见的缓冲区。 RETf- 选中光标所在行的缓冲区,替换当前窗口中的
*Buffer List*缓冲区 (Buffer-menu-this-window) 。 o- 在另一个窗口中选中光标所在行的缓冲区,效果等同于执行
C-x 4 b命令,保留*Buffer List*缓冲区可见 (Buffer-menu-other-window) 。 C-o- 在另一个窗口中显示光标所在行的缓冲区,不选中该窗口 (
Buffer-menu-switch-other-window) 。 1- 在占满整个框架的窗口中选中光标所在行的缓冲区 (
Buffer-menu-1-window) 。 2- 在当前框架中拆分出两个窗口,一个窗口选中光标所在行的缓冲区,另一个窗口显示此前的当前缓冲区(
*Buffer List*除外) (Buffer-menu-2-window) 。 b- 将光标所在行的缓冲区置为未激活状态 (
Buffer-menu-bury) ,即移至缓冲区列表的末尾。 m- 为缓冲区标记显示标识,若按下
v命令退出菜单,该缓冲区会在另一个窗口中显示 (Buffer-menu-mark) 。标记后,该行开头会显示字符 '>' 作为显示标识(一个缓冲区不可同时标记删除和显示标识)。 v- 选中光标所在行的缓冲区,同时在其他窗口中显示所有带
m标记的缓冲区 (Buffer-menu-select) 。若未标记任何带显示标识的缓冲区,该命令的效果与1命令一致。
以下命令作用于整个缓冲区列表:
S- 根据光标所在列的属性对缓冲区菜单的条目进行排序 (
tabulated-list-sort) 。若添加数字前缀参数n,则根据第n列的属性进行排序。 }- 将当前列的宽度增加
n个字符(n为数字前缀参数)。 {- 将当前列的宽度减少
n个字符(n为数字前缀参数)。 T- 隐藏或重新显示非文件类缓冲区对应的行 (
Buffer-menu-toggle-files-only) ,该命令可切换缓冲区列表中是否包含此类缓冲区。 I- 切换是否显示内部缓冲区(名称以空格开头的缓冲区)。
默认情况下,当创建或删除缓冲区时, *Buffer List* 缓冲区不会自动更新,其内容仅为静态文本。若执行了缓冲区的创建、删除或重命名操作,按下 g 键 (revert-buffer) 即可更新 *Buffer List* 的内容,展示最新的缓冲区状态。若该缓冲区未被标记为已修改,在其中启用自动恢复模式后,缓冲区会按照 auto-revert-interval 变量设定的秒数定期自动更新。全局自动恢复模式仅在 global-auto-revert-non-file-buffers 变量值为非 nil 时,才对 *Buffer List* 缓冲区生效,具体细节参见 global-auto-revert-non-file-buffers 变量的说明。
21.6. 间接缓冲区
indirect buffer间接缓冲区 共享另一缓冲区的文本内容,该缓冲区被称为此间接缓冲区的 base buffer基缓冲区 。在某种程度上,它相当于文件系统中符号链接在缓冲区上的对应实现。
相关命令
M-x make-indirect-buffer RET base-buffer RET indirect-name RET- 创建以指定基缓冲区为文本源、并命名为指定名称的间接缓冲区。
M-x clone-indirect-buffer 回车- 创建当前缓冲区的副本间接缓冲区(与原缓冲区完全联动)。
C-x 4 c- 创建当前缓冲区的副本间接缓冲区,并在新窗口中选中该间接缓冲区 (
clone-indirect-buffer-other-window) 。
核心特性
间接缓冲区的文本始终与基缓冲区 完全一致 :对任意一方的编辑修改,都会立即在另一方中显示。此处的 "Text" 包含字符本身及其所有 文本属性 。
但在其他所有方面,间接缓冲区与基缓冲区是 完全独立 的,二者可拥有:不同的缓冲区名称、不同的光标位置、不同的内容窄化范围、不同的标记、不同的叠加层、不同的主模式,以及不同的局部变量。
文件操作与缓冲区生命周期
- 间接缓冲区 无法直接访问文件 ,但其基缓冲区可以;若尝试保存间接缓冲区,实际会执行基缓冲区的保存操作。
- 删除基缓冲区会 连带失效 其所有间接缓冲区;而删除间接缓冲区,对其基缓冲区无任何影响。
典型用法
间接缓冲区的常用场景之一是为大纲文档创建 多视图展示 ,具体可参考《在多视图中查看单个大纲》章节。
快速创建方式
使用快捷键 C-x 4 c 是创建间接缓冲区最快捷的方式,该命令会以当前缓冲区为基缓冲区,直接生成副本间接缓冲区并在新窗口打开。若带数字参数执行该命令,会提示用户输入间接缓冲区的自定义名称;无参数时,默认以原缓冲区名称加后缀 '<n>' (n 为数字)命名。
通用创建方式
使用命令 M-x make-indirect-buffer 可创建自定义的间接缓冲区,该命令会通过迷你缓冲区依次提示用户输入 基缓冲区名 和 间接缓冲区名 ,灵活性更高。
相关钩子函数
创建间接缓冲区的所有函数,都会在缓冲区创建完成后执行钩子函数 clone-indirect-buffer-hook 。该钩子执行时,新创建的间接缓冲区会成为当前缓冲区。
注意事项
对缓冲区文本进行修改时, 修改钩子仅会在基缓冲区中执行 —— 原因是这些钩子上的大部分函数并未适配间接缓冲区的运行环境,无法在其中正常工作。因此,若需要在间接缓冲区中使用修改钩子函数,需手动将该函数添加到 基缓冲区 的对应钩子中,并让函数在目标间接缓冲区中执行相关操作。
21.7. 缓冲区操作的便捷功能与相关定制
本节介绍多款可提升缓冲区切换效率的模式与功能。
21.7.1. 让缓冲区名称唯一化
当多个缓冲区访问文件名相同的文件时,Emacs 必须为这些缓冲区分配互不重复的名称。默认命名方式会 根据文件所在的目录名称添加后缀 ,以此区分。例如,若同时访问文件 /foo/bar/mumble/name 和 /baz/quux/mumble/name ,对应的缓冲区会分别命名为 'name<bar/mumble>' 和 'name<quux/mumble>' 。Emacs 会自动添加足够多的目录层级,确保缓冲区名称唯一。
你可以通过自定义选项 uniquify-buffer-name-style ,选择多种不同的 缓冲区唯一名称构建规则 。
'forward' 正向命名法 会将文件目录的部分名称加在缓冲区名称 开头 :例如访问 /u/rms/tmp/Makefile 和 /usr/projects/zaphod/Makefile 时,缓冲区会被命名为 'tmp/Makefile' 和 'zaphod/Makefile' 。
与之相对, 'post-forward' 后置正向命名法 会将目录名加在缓冲区名称后方,格式为 '文件名|目录名' ,上述两个文件对应的缓冲区会被命名为 'Makefile|tmp' 和 'Makefile|zaphod' 。默认使用的 post-forward-angle-brackets 后置正向尖括号命名法 与后置正向命名法逻辑一致,仅将唯一路径部分用尖括号包裹,也是前文示例中使用的命名方式。
reverse 反向命名法 会将目录名加在缓冲区名称后方,格式为 '文件名\目录名' ,上述示例会被命名为 'Makefile\tmp' 和 'Makefile\zaphod' 。后置正向命名法与反向命名法的核心区别体现在 单个目录名不足以区分文件 的场景:此时反向命名法会将目录层级 逆序拼接 ,例如文件 /top/middle/file 会被命名为 'file\middle\top' ;而后置正向命名法会将目录层级 正序拼接 在文件名后,格式为 'file|top/middle' 。
若将 uniquify-buffer-name-style 设为 nil ,Emacs 会采用最简命名规则,仅在缓冲区名称后依次追加 '<2>' 、 '<3>' 等数字后缀以保证唯一。
uniquify-buffer-name-style 的取值也可以是 自定义函数 ,该函数需接收两个参数: base (字符串类型,为缓冲区基础名称)和 extra-strings (字符串列表类型,为用于区分的目录片段)。例如,后置正向尖括号命名法的默认实现可自定义为如下函数:
(defun my-post-forward-angle-brackets (base extra-string) (concat base \"<\" (mapconcat #'identity extra-string \"/\") \">\"))
如果在输入缓冲区名称前,你会先查看所有缓冲区的命名再选择,那么采用哪种目录名拼接规则其实影响不大。但对于熟练的 Emacs 用户而言,若熟知所使用的命名规则,无需查看即可直接输入缓冲区名称,此时你会发现某一种规则会更便于记忆和快速使用。
21.7.2. 迷你缓冲区快速选择(方式)
补全预览模式(Icomplete mode)为在迷你缓冲区中从候选补全项里快速选择内容提供了便捷方式。启用该模式后,在迷你缓冲区中输入内容时,会 实时显示 所有与已输入字符串匹配的候选补全项列表。
在输入过程中的任意时刻,可按下 C-j 选中列表中的 首个补全项 。因此,选中特定补全项的核心思路,是将其调整为列表中的首个选项,具体有两种实现方式:
- 继续输入补全项的更多字符, 缩小候选范围 ,将目标项上方的无关补全项过滤掉;
- 使用快捷键
C-.和C-,轮换候选列表 ,直至目标缓冲区 / 补全项出现在列表首位。
按下 M-TAB 同样会选中列表中的首个补全项,与 C-j 的区别是 不会退出迷你缓冲区 ,可对选中的补全项继续编辑。该快捷键在输入文件名时尤为常用,多次按下可逐级匹配目录层级,快速定位目标路径。
若要为迷你缓冲区启用补全预览模式,可执行命令 M-x icomplete-mode ,或将配置变量 icomplete-mode 自定义设为 t (参见简易自定义界面)。
此外,还可通过将配置变量 icomplete-in-buffer 设为 t ,为快捷键 C-M-i (completion-at-point ,即点处补全)额外启用补全预览模式。针对缓冲区内的补全操作,配置变量 completion-auto-help 用于控制补全预览模式的 候选补全项显示时机 ,其默认值 t 表示首次按下 C-M-i 时,即显示候选补全项列表。
默认情况下,按下 C-M-i 时,补全预览模式的缓冲区内置候选补全项显示,与 *Completions* 补全缓冲区会 同时出现 。若启用了缓冲区内置的补全预览功能( icomplete-in-buffer ),可隐藏自动弹出的 *Completions* 缓冲区,只需在 Emacs 初始化文件中添加以下配置(参见 Emacs 初始化文件相关说明):
(advice-add 'completion-at-point :after #'minibuffer-hide-completions)
补全预览模式的替代方案是 Fido 补全模式(Fido mode),该模式与补全预览模式功能高度相似,同时保留了热门扩展 Ido 模式的部分核心功能(其名称正是由 “Fake Ido” 衍生而来)。除基础补全功能外, Fido 补全模式还有以下特性:
- 可使用
C-s和C-r快捷键轮换候选补全项列表; - 可通过
C-k在候选列表中直接删除文件、关闭缓冲区; - 默认采用 flex柔性匹配 作为补全样式(参见补全候选项的选择规则)。
若要修改该模式的默认补全样式,可在初始化文件中添加以下配置:
(defun my-icomplete-styles () (setq-local completion-styles '(initials flex))) (add-hook 'icomplete-minibuffer-setup-hook 'my-icomplete-styles)
启用 Fido 补全模式的方式为:执行命令 M-x fido-mode ,或将配置变量 fido-mode 自定义设为 t (参见简易自定义界面)。
补全预览模式与 Fido 补全模式,默认会在与提示语同一行显示候选补全项。若要在提示语下方 垂直展示 所有补全候选项,可执行命令 M-x icomplete-vertical-mode ,或将配置变量 icomplete-vertical-mode 自定义设为 t (参见简易自定义界面)。
21.7.3. 定制缓冲区菜单
M-x bs-show- 生成缓冲区列表,功能与
M-x list-buffers类似,且支持自定义配置。 M-x ibuffer- 生成缓冲区列表,并可通过类 Dired 的操作方式对缓冲区进行管理。
M-x bs-show 会弹出一个缓冲区列表,与 C-x C-b 默认显示的列表功能一致,但 支持更灵活的自定义显示规则 。例如,你可以指定要展示的缓冲区属性项、缓冲区名称列的最小和最大宽度、用于匹配 永不显示 的缓冲区名称正则表达式,以及 始终显示 的缓冲区名称正则表达式等。若你更偏好该缓冲区列表,可将此命令绑定到 C-x C-b 快捷键上。如需自定义该缓冲区列表,可使用 bs 自定义组(参见简易自定义界面),或直接调用 bs-customize 命令进行配置。
MSB 全局次要模式(MSB 为 "mouse select buffer" 鼠标选择缓冲区的英文缩写)提供了一套风格不同且支持自定义的鼠标缓冲区菜单,你可根据喜好启用。该模式会用自身的命令,替换原本绑定在 C-Down-mouse-1 和 C-F10 上的 mouse-buffer-menu 相关命令,同时也会修改菜单栏中的缓冲区菜单。你可在msb自定义组中对该菜单进行个性化配置。
IBuffer 是一款专门用于查看缓冲区列表的 主模式 ,它支持以类 Dired 的方式(参见《目录编辑器 Dired》章节)对缓冲区进行操作,包括缓冲区过滤、标记、多种方式排序,以及对标记的缓冲区执行批量操作等。
22. 多窗口
Emacs 可将一个框架拆分为两个或多个窗口。多个窗口既可以显示不同缓冲区的内容,也可显示同一缓冲区的不同部分。多框架必然对应多窗口,因为每个框架都有其独立的窗口集,且每个窗口仅归属于一个框架。
22.1. Emacs 窗口的概念
每个 Emacs 窗口在任一时刻仅显示一个 Emacs 缓冲区。单个缓冲区可同时显示在 多个窗口 中:若缓冲区内容发生修改,所有显示该缓冲区的窗口都会实时同步更新修改内容。但这些窗口可展示缓冲区的不同部分,因为 每个窗口都有独立的光标位置 。
任一时刻,仅有一个 Emacs 窗口为 选中窗口 ,该窗口当前显示的缓冲区即为 当前缓冲区 。在图形化显示界面中,选中窗口的光标为 实心闪烁光标 ,未选中窗口的光标为空心方框;在文本终端中,光标仅会显示在选中窗口内。相关细节参见《光标显示》章节。
光标移动类命令 仅会改变选中窗口 的光标位置,不会影响其他 Emacs 窗口的光标位置 —— 即便这些窗口显示的是同一个缓冲区。缓冲区切换类命令(如 C-x b )的行为同理,完全不会对其他窗口产生影响。不过存在部分特殊命令(如 C-x 4 b ),可选中其他窗口并在其中切换缓冲区。此外,所有在窗口中展示信息的命令(例如 C-h f (descibe-function) 、 C-x C-b (list-buffers) ),通常都会在 未选中窗口 中展示对应缓冲区内容,且不会改变当前的选中窗口。
当多个窗口显示同一个缓冲区时,各窗口可拥有 不同的选区 ,原因是各窗口的光标位置相互独立;但所有窗口共享同一个 标记位置 ,因为每个缓冲区仅存在一个标记位。
每个窗口都配有独立的 模式行 ,用于显示该窗口当前所展示缓冲区的名称、修改状态,以及该缓冲区的主模式和次要模式。选中窗口的模式行会以 不同颜色 显示,相关细节参见《模式行》章节。
22.2. 拆分窗口
C-x 2- 将选中的窗口拆分为两个上下排列的窗口 (
split-window-below) 。 C-x 3- 将选中的窗口拆分为两个左右并排的窗口 (
split-window-right) 。 C-mouse-2- 在某一窗口的模式行上点击,拆分该窗口。
C-x 2 (split-window-below) 会将选中窗口拆分为两个上下排布的窗口,拆分后原窗口为上方的选中窗口,新拆分出的窗口位于下方。两个窗口会保留拆分前的光标位置,且显示缓冲区的同一部分内容(或尽可能接近的区域);若有需要,窗口会自动滚动以保证光标处于可视区域。默认情况下,拆分后的两个窗口各占原窗口高度的一半。带正数字参数执行该命令,可指定上方窗口的行数;带负数字参数,则指定下方窗口的行数。
若将变量 split-window-keep-point 设为 nil ,执行 C-x 2 时,Emacs 会调整两个窗口显示的缓冲区内容范围,同时修改各窗口的光标位置,尽可能保持屏幕上的文本与拆分前一致;此外,若拆分前光标位于原窗口的下半区域,拆分后会选中下方的窗口,而非上方窗口。
C-x 3 (split-window-right) 会将选中窗口拆分为两个左右并排的窗口,左侧为选中窗口,右侧窗口显示同一缓冲区的同一部分内容,且光标位置与原窗口一致。带正数字参数执行该命令,可指定左侧窗口的列数;带负数字参数,则指定右侧窗口的列数。
使用 C-x 3 拆分窗口后,每个窗口的宽度都会小于框架的完整宽度。若窗口过窄,且文本启用了折行显示,缓冲区内容会难以阅读(参见折行显示章节)。因此,当窗口宽度小于 50 列时,Emacs 会自动切换为行截断模式。该自动截断行为不受变量 truncate-lines 控制(参见行截断章节),而是由变量 truncate-partial-width-windows 管理:若该变量值为正整数(默认值为 50),则表示非全宽窗口的最小宽度阈值,低于此值即触发自动行截断;若值为 nil ,则禁用自动行截断;若为其他非nil值,则所有非全宽窗口无论宽度多少,均会启用行截断。窗口的总宽度以 window-total-width 函数返回的列数为单位(参见《Emacs Lisp 参考手册》的窗口大小章节),包含边缘区域、折行与截断符号、边距和滚动条的宽度。
在文本终端中,左右并排的窗口之间会显示一个垂直分隔线,该分隔线使用 vertical-border 面属性绘制。
在某一窗口的模式行上点击 C-mouse-2 ,会拆分该窗口,且在点击位置生成垂直分隔线。根据 Emacs 的编译版本,也可在滚动条上点击 C-mouse-2 2拆分窗口,此时会在点击位置生成水平分隔线(该功能在 Emacs 使用 GTK + 滚动条时无效)。
默认情况下,拆分窗口时,Emacs 会为拆分后的每个窗口分配 框架默认字体大小整数倍 的尺寸,这可能导致屏幕空间在各窗口间分配不均。若将变量 window-resize-pixelwise 设为非nil值,Emacs 会为每个窗口分配相同的像素数(若初始尺寸为奇数像素,可能会相差 1 个像素)。注意,若框架的像素尺寸并非字符尺寸的整数倍,即便该选项设为 nil ,至少有一个窗口也会按像素单位调整大小
22.3. 使用其他窗口
C-x o- 选中另一窗口 (
other-window) 。 C-M-v- 向上滚动下一个窗口的内容 (
scroll-other-window) 。 C-M-S-v- 向下滚动下一个窗口的内容 (
scroll-other-window-down) 。 C-M-S-l- 重定下个窗口的内容居中显示 (
recenter-other-window) 。 mouse-1- 在窗口的文本区域点击鼠标左键,会选中该窗口并将光标移动至点击位置;在模式行上点击则仅选中窗口,不会改变其中的光标位置。
使用键盘可通过输入 C-x o (other-window) 来切换窗口,此处为字母 o (代表 “other”,即其他),并非数字 0。当窗口数量多于两个时,该命令会按 循环顺序 遍历所有窗口,一般遵循从上到下、从左到右的次序;在选中最右侧、最下方的窗口后,会重新回到左上角的第一个窗口。带 数字参数 执行该命令,可按循环顺序连续切换指定次数的窗口;带 负参数 则按相反的循环方向切换。当迷你缓冲区处于激活状态时,迷你缓冲区窗口会作为循环中的最后一个窗口;你可从迷你缓冲区窗口切换至其他任意窗口,后续可再次切回并继续输入迷你缓冲区所需的参数。详见《在迷你缓冲区中编辑》章节。
other-window 命令默认仅在 当前框架 内切换至下一个窗口(除非另行配置)。若你在多框架环境下工作,希望将所有框架中的窗口都纳入循环切换范围,可将 C-x o 重新绑定至 next-window-any-frame 命令(关于如何重新绑定命令,详见《交互式修改按键绑定》章节)。
常规的滚动命令(详见《控制显示效果》章节)仅对 选中窗口 生效,Emacs 也提供了专门用于滚动下一个窗口的命令。 C-M-v (scroll-other-window) 会滚动 C-x o 即将选中的那个窗口,该命令的其他行为与 C-v 一致:二者均会让缓冲区文本相对窗口向上滚动,且均可接收正、负数字参数。(在迷你缓冲区中, C-M-v 不会滚动标准循环顺序中的下一个窗口,而是滚动与迷你缓冲区关联的帮助窗口(若存在),详见《在迷你缓冲区中编辑》章节。) C-M-S-v (scroll-other-window-down) 以类似方式实现下一个窗口的向下滚动;同理, C-M-S-l (recenter-other-window) 的作用,等同于在次窗口中执行 C-l (recenter-top-bottom) 命令。
若将变量 mouse-autoselect-window 设为非nil值,将鼠标移至其他窗口上方时,会自动选中该窗口,该功能默认处于关闭状态。
22.4. 在另一窗口中显示内容
C-x 4 是一组命令的前缀键,这类命令会在 另一窗口 中切换至指定缓冲区 —— 该窗口可以是已存在的其他窗口,也可以是通过拆分当前选中窗口新建的窗口。Emacs 如何选择或创建目标窗口的规则,详见《display-buffer 函数的工作机制》章节。
C-x 4 b bufname RET- 在另一窗口中选中指定缓冲区 (
switch-to-buffer-other-window) ,详见《创建与选择缓冲区》章节。 C-x 4 C-o bufname RET- 在某一窗口中显示指定缓冲区, 不尝试选中 该窗口 (
display-buffer) 。窗口的选择规则细节,详见《在窗口中显示缓冲区》章节。 C-x 4 f finename RET- 访问指定文件,并在另一窗口中选中其对应的缓冲区 (
find-file-other-window) ,详见《访问文件》章节。 C-x 4 d directory RET- 在另一窗口中为指定目录选中 Dired 缓冲区 (
dired-other-window) ,详见《目录编辑器 Dired》章节。 C-x 4 m- 开始撰写邮件,功能与
C-x m一致(详见《发送邮件》章节),区别是在另一窗口中执行 (compose-mail-other-window) 。 C-x 4 .- 查找标识符的定义,功能与
M-.一致(详见《查找标识符引用》章节),区别是在另一窗口中执行 (xref-find-definitions-other-window) 。 C-x 4 r filename RET- 以只读方式访问指定文件,并在另一窗口中选中其对应的缓冲区 (
find-file-read-only-other-window) ,详见《访问文件》章节。 C-x 4 4- 一个更通用的前缀命令,会影响紧随其后执行的命令所显示的缓冲区 (
other-window-prefix) ,该前缀会要求后续命令将其要显示的缓冲区展示在另一窗口中。 C-x 4 1- 该通用前缀命令则要求后续执行的命令,将其对应的缓冲区 显示在当前同一窗口中 。
22.5. 删除与调整窗口大小
C-x 0- 删除选中的窗口 (
delete-window) 。 C-x 1- 删除选中框架中除选中窗口外的所有窗口 (
delete-other-windows) 。 C-x 4 0- 删除选中的窗口并
kill关闭其在缓冲区显示 (kill-buffer-and-window,该快捷键最后一个字符为数字 0) 。 C-x w 0 RET buffer RET- 删除所有显示指定缓冲区的窗口。
C-x ^- 增大选中窗口的高度 (
enlarge-window) 。 C-x }- 增大选中窗口的宽度 (
enlarge-window-horizontally) 。 C-x {- 缩小选中窗口的宽度 (
shrink-window-horizontally) 。 C-x -- 若缓冲区无需当前行数,缩小该窗口尺寸 (
shrink-window-if-larger-than-buffer) 。 C-x +- 平衡选中框架中所有窗口的尺寸 (
balance-windows) 。
删除选中窗口可按下 C-x 0 (注意是数字 0)。窗口被删除后,其占用的空间会分配给相邻的窗口(迷你缓冲区窗口除外,即便其处于激活状态也不会分配)。删除窗口不会对其原本显示的缓冲区产生任何影响,该缓冲区会继续存在,你仍可通过 C-x b 切换至该缓冲区。配置项 delete-window-choose-selected 用于控制选择哪个窗口作为新的选中窗口(详见《Emacs Lisp 参考手册》的 “删除窗口” 章节)。
C-x 4 0 是比 C-x 0 功能更强的命令,它会先 kill关闭 当前缓冲区,再删除选中的窗口。
C-x 1 会删除选中框架中除选中窗口外的所有窗口,选中窗口会自动扩展至占据整个框架的空间(该命令无法在迷你缓冲区窗口激活时使用,强行使用会触发错误)。
执行 M-x delete-windows-on 可删除所有显示指定缓冲区的窗口,该命令会提示输入目标缓冲区,默认值为当前缓冲区。若带上数字 0 前缀参数( C-u 0 ),该命令仅会删除当前显示器所属框架中显示该缓冲区的窗口。
C-x ^ 命令会将选中窗口的高度增加一行,空间从垂直方向的相邻窗口中获取,且不会改变框架的整体高度。带上正数字参数时,窗口高度会按该数值增加;带上负数字参数时,窗口高度会按该数值减少。若当前窗口无垂直方向的相邻窗口(即窗口已占满框架全部高度),执行该命令会触发错误;若尝试将任意窗口的高度缩小至低于配置项 window-min-height 指定的最小行数(默认值为 4 行),同样会触发错误。
同理, C-x } 会增大选中窗口的宽度, C-x { 会缩小选中窗口的宽度。若尝试将任意窗口的宽度缩小至低于配置项 window-min-width 指定的最小列数(默认值为 10 列),执行这两个命令会触发错误。
在模式行(参见《模式行的鼠标命令》)或窗口分隔线(参见《窗口分隔线》)上点击鼠标,也可实现窗口高度调整、窗口拆分或删除操作。
C-x - 命令会检测选中窗口的高度,若其超出显示对应缓冲区全部文本所需的行数,会自动缩小窗口高度,并将多余的行空间分配给框架中的其他窗口。
你也可使用 C-x + 平衡选中框架中所有窗口的尺寸(迷你缓冲区窗口除外,参见《迷你缓冲区》章节)。该命令会让所有水平相邻的窗口高度一致,所有垂直相邻的窗口宽度一致。
22.6. 在窗口中显示缓冲区
响应用户命令来显示或调出某个缓冲区,是 Emacs 的常规操作。命令实现该功能的方式有多种。
许多命令(如 C-x C-f (find-file) )默认会 占用当前选中的窗口 来显示缓冲区,这是因为这类命令预期用户的注意力会转移至该缓冲区。
部分命令会采用更智能的显示方式,尽量不占用选中的窗口 —— 例如,拆分出一个新窗口并在其中显示目标缓冲区。这类命令包括各类帮助命令(参见帮助相关章节),其内部均通过调用 display-buffer 函数实现该功能,具体细节参见《display-buffer 的工作机制》小节。
另有一些命令的行为与 display-buffer 一致,且会额外选中用于显示的窗口,方便用户直接开始编辑该缓冲区。命令 M-g M-n (next-error) 就是典型示例(参见编译模式章节),这类命令内部通过调用 pop-to-buffer 函数实现功能,具体可参见《Emacs Lisp 参考手册》中的《在窗口中切换至缓冲区》章节。
名称以 -other-window 结尾的命令,行为与 display-buffer 类似, 唯一区别 是绝不会在选中的窗口中显示缓冲区。这类命令中有多个被绑定在 C-x 4 前缀键下(参见《在另一窗口中显示内容》章节)。
名称以 -other-frame 结尾的命令,同样以 display-buffer 的行为为基础,同时满足两个规则:① 绝不会在选中的窗口中显示缓冲区;② 优先创建新框架,或使用其他框架中的窗口来显示目标缓冲区。这类命令中有多个被绑定在 C-x 5 前缀键下。
有时,某个窗口会被 dedicated专用于 其当前显示的缓冲区(详见《Emacs Lisp 参考手册》中的《专用窗口》章节)。 display-buffer 函数在绝大多数情况下,都会避免复用专用窗口。窗口的专用属性会在模式行中以字符 'd' 标识(参见模式行相关章节);此外,窗口还可设置为 强专用模式 ,该模式下窗口显示的缓冲区无法被任何方式替换,其模式行中会以字符 'D' 标识。
专用窗口通常用于显示特定用途的缓冲区,不过该属性在交互式操作中也能发挥作用。例如,使用 M-g M-n (next-error) 查看错误信息时,新显示的源代码缓冲区可能会覆盖你需要参考的某个缓冲区。若将该窗口设置为对应缓冲区的专用窗口,上述命令(通过 display-buffer 函数)会自动选择其他窗口来显示新内容。
你可使用命令 C-x w d (toggle-window-dedicated) ,来切换选中窗口是否专用于当前缓冲区。若带上前缀参数执行该命令,会将窗口设置为强专用模式。
22.6.1. display-buffer 函数的工作原理
display-buffer 命令(以及所有内部调用该函数的命令)会按照下述步骤选择用于显示缓冲区的窗口。若需修改该步骤执行顺序,详见《Emacs Lisp 参考手册》中的《为显示缓冲区选择窗口》章节。
若无论其他条件如何,该缓冲区都需在选中窗口中显示,则复用当前选中的窗口。此步骤默认会被跳过,若要取消跳过,需将 匹配该缓冲区名称的正则表达式 ,与
display-buffer-same-window动作函数的引用一起,添加至配置项display-buffer-alist中(动作函数相关说明详见《Emacs Lisp 参考手册》中的《缓冲区显示的动作函数》章节,配置项相关说明详见《Emacs Lisp 参考手册》中的《为显示缓冲区选择窗口》章节)。例如,要让*scratch*缓冲区优先在选中窗口中显示,可写入以下配置:(setopt display-buffer-alist '(("\\*scratch\\*" (display-buffer-same-window))))
display-buffer-alist的默认值为nil。- 若该缓冲区已在某个现有窗口中显示,则复用该窗口。默认情况下,仅会检索当前选中框架中的窗口;若在动作关联列表中配置了对应的
reusable-frames项(详见《Emacs Lisp 参考手册》中的《缓冲区显示的动作关联列表》章节),其他框架中的窗口也可被复用。具体配置方式可参考下一步的示例。 可选创建一个新框架,并在其中显示该缓冲区。此步骤默认被跳过。若要启用该功能,需按如下方式修改配置项
display-buffer-base-action的值(详见《Emacs Lisp 参考手册》中的《为显示缓冲区选择窗口》章节):(setopt display-buffer-base-action '((display-buffer-reuse-window display-buffer-pop-up-frame) (reusable-frames . 0)))该自定义配置同时会让上一步的检索范围,扩展至所有可见或最小化的框架中的可复用窗口。
尝试在当前选中的框架中拆分某个窗口,创建新窗口并在其中显示该缓冲区。
拆分方式可为垂直或水平,具体由变量
split-height-threshold和split-width-threshold控制,这两个变量均需设置为整数值。若split-height-threshold小于目标窗口的高度,新窗口会被拆分为下方窗口;若该条件不满足,但split-width-threshold小于目标窗口的宽度,新窗口会被拆分为右侧窗口;若两个条件均不满足,Emacs 会尝试将新窗口拆分为下方窗口 —— 但该行为仅在目标窗口未被拆分过的情况下生效(避免过度拆分)。- 在之前显示过该缓冲区的窗口中重新显示。默认情况下,仅会检索当前选中框架中的窗口;若配置了合适的
reusable-frames动作关联列表项(见上文),也可使用其他框架中的对应窗口。 - 在当前选中框架的某个现有窗口中显示该缓冲区。
- 若因任何原因,上述所有方式均失败,则创建一个新框架,并在其中显示该缓冲区。
22.6.2. 显示不可编辑缓冲区
部分缓冲区在窗口中展示仅作查阅之用,而非用于编辑。帮助类命令(参见帮助章节)通常会借助名为 *Help* 的缓冲区实现此功能,迷你缓冲区补全(参见补全章节)会用到名为 *Completions* 的缓冲区,诸如此类的缓冲区通常仅临时显示一小段时间。
默认情况下,Emacs 会通过 display-buffer 函数为这类临时展示的缓冲区选择显示窗口,具体规则见上一小节。但 *Completions* 缓冲区是例外,它通常会固定显示在 选中框架底部 的窗口中,不受该框架当前已打开的窗口数量影响。
若你希望 Emacs 以其他方式显示临时缓冲区,可对配置项 display-buffer-alist 进行相应的自定义配置(详见《Emacs Lisp 参考手册》中的《为显示缓冲区选择窗口》章节)。例如,要让 *Completions* 缓冲区始终显示在选中窗口的下方,可在初始化文件中添加以下配置(参见《Emacs 初始化文件》章节):
(setopt display-buffer-alist '(("\\*Completions\\*" display-buffer-below-selected)))
*Completions* 缓冲区还有一个特殊之处:Emacs 通常会自动调整其窗口大小,使其 恰好能完整显示所有内容 。若要让其他临时展示的缓冲区(如 *Help* 缓冲区)的窗口也实现自动调整大小,可启用次要模式 temp-buffer-resize-mode (参见次要模式章节,该模式详情见《Emacs Lisp 参考手册》中的《临时展示》章节)。
由 temp-buffer-resize-mode 模式调整大小的窗口,其最大尺寸可通过配置项 temp-buffer-max-height 和 temp-buffer-max-width 自定义控制(详见《Emacs Lisp 参考手册》中的《临时展示》章节),且窗口最大尺寸不会超过所属框架的大小。
用于展示警告信息的缓冲区(如字节编译警告,详见《Emacs Lisp 参考手册》中的《字节编译函数》章节),默认也会显示在选中框架底部的窗口中。你可通过变量 warning-display-at-bottom 控制该行为:若将其设为 nil ,Emacs 会改用 display-buffer 函数的默认逻辑为其选择窗口(参见 display-buffer 函数的工作机制),你也可通过 display-buffer-alist 对该逻辑进行自定义。
22.7. 窗口操作的便捷功能
窗口配置恢复模式(Winner mode)是一款全局次要模式,它会记录 窗口配置的变更记录 (即框架拆分为多个窗口的布局方式),让你可以撤销这些变更。可通过 M-x winner-mode 切换该模式的开启与关闭,也可通过自定义变量 winner-mode 完成设置。启用该模式后,按下 C-c LEFT (winner-undo) 可撤销上一次的窗口配置变更;若在撤销操作后改变想法,可使用 C-c right (M-x winner-redo) 重做已撤销的配置变更。若不想让窗口配置恢复模式绑定 C-c left 和 C-c right ,可将变量 winner-dont-bind-my-keys 设为非nil值。默认情况下,该模式为每个框架最多存储 200 条窗口配置记录,你可通过修改变量 winner-ring-size 调整这一上限。若存在部分缓冲区,你不希望该模式恢复其对应的窗口布局,可将这些缓冲区的名称添加至列表变量 winner-boring-buffers ,或匹配至正则表达式变量 winner-boring-buffers-regexp 中。
跟随模式( M-x follow-mode )可让显示同一缓冲区的多个窗口实现同步,使它们始终展示该缓冲区中 相邻的内容区域 。详见「跟随模式」章节。
Windmove 包定义了在一个框架中 按方向切换相邻窗口 的命令。 M-x windmove-right 会选中当前选中窗口右侧紧邻的窗口, windmove-left 、 windmove-up 、 windmove-down 命令则分别对应左、上、下方向的窗口切换。执行 windmove-default-keybindings 可将这些命令绑定至 S-right Shift+右方向键等组合键;该绑定操作会禁用这些按键原本的移位选择功能(详见「移位选择」章节)。与为按方向选窗的命令绑定快捷键的方式相同,你可使用 windmove-display-default-keybindings ,为「指定下一条命令的缓冲区在哪个方向的窗口中显示」的相关命令绑定快捷键。此外, windmove-delete-default-keybindings 可用于为按方向删除窗口的命令绑定快捷键, windmove-swap-states-default-keybindings 则能为「将选中窗口与指定方向窗口的内容互换」的命令绑定快捷键。
执行 M-x compare-windows 命令,可对比不同窗口中显示的文本内容。详见「文件对比」章节。
全部滚动模式( M-x scroll-all-mode )是一款全局次要模式,启用后,滚动类命令和光标移动类命令会作用于所有窗口。
22.8. 窗口标签栏
global-tab-line-mode 命令用于切换是否在每个窗口的顶部显示 tab line标签行 。标签行会为该窗口中曾显示过的每个缓冲区显示专属按钮("tabs"),点击对应按钮即可切换至相应缓冲区。点击 + 图标可在该窗口的本地缓冲区标签行中新增一个缓冲区,点击某一标签的 x 图标则可将该标签移除。在标签行上滚动鼠标滚轮,可实现标签的水平滚动。
触摸屏输入(参见触摸屏输入与虚拟键盘)也可用于与 "tab line" 交互。长按(参见在触摸屏上使用 Emacs)某一标签,会弹出一个上下文菜单,其中包含对该被按标签的操作选项;轻点标签本身,即可切换至该标签对应的缓冲区;轻点标签行上的按钮,效果等同于使用 mouse-1 鼠标左键点击该按钮。
选中窗口本地的上一个标签,等同于按下 C-x LEFT (previous-buffer) ;选中下一个标签,等同于按下 C-x RIGHT (next-buffer) 。这两个命令均支持将数字前缀参数作为重复执行的次数。
你可自定义变量 tab-line-tabs-function ,来定义标签行的首选显示内容。默认情况下,该标签行会显示前文所述的、该窗口中曾访问过的所有缓冲区。但你也可将其设置为显示与当前缓冲区主模式相同的所有缓冲区列表,或按主模式对缓冲区进行分组显示 —— 此时点击首个标签中的模式名称,会弹出所有主模式的列表,你可从中选择另一组缓冲区进行查看。
请注意, Tab Line标签行与 Tab Bar标签栏并非同一功能 (参见标签栏)。标签栏位于每个框架的顶部,其标签用于在包含多个缓冲区窗口的窗口配置之间进行切换;而标签行位于每个窗口的顶部,其标签仅用于在当前窗口内的不同缓冲区之间切换。
另请注意,标签行与窗口工具栏会占用 同一显示区域 ,因此任一时刻只能显示二者中的一个;除非你在 Lisp 代码中自定义 tab-line-format 的值,向其中添加 (:eval (tab-line-format)) 来调整布局(详见《Emacs Lisp 参考手册》中的模式行格式章节)。
22.9. 窗口工具栏
global-window-tool-bar-mode 命令用于切换是否在每个窗口的顶部显示工具栏。启用该功能后,多个窗口可同时显示各自的专属工具栏。为节省显示空间,若某一窗口的工具栏无任何按钮可展示(即 tool-bar-map 变量值为 nil ),该窗口的工具栏会自动隐藏。
若你希望仅为部分缓冲区切换窗口工具栏的显示状态,可在这些缓冲区中执行 window-tool-bar-mode 命令。该用法在模式钩子中尤为实用。例如,若你希望仅为 非文件类缓冲区 且配有自定义工具栏的缓冲区显示窗口工具栏,可在初始化文件中添加以下代码(参见《Emacs 初始化文件》章节):
(add-hook 'special-mode-hook 'window-tool-bar-mode)
Emacs 也支持在框架顶部显示一个 全局统一的工具栏 (参见《工具栏》章节)。
请注意,窗口工具栏与标签行会占用 同一显示区域 ,因此任一时刻只能显示二者中的一个;除非你自定义 tab-line-format 变量的值,向其中添加 (:eval (window-tool-bar-string)) 来调整布局(详见《Emacs Lisp 参考手册》中的《模式行格式》章节)。
23. 框架与图形界面
当 Emacs 在图形化显示环境中启动时(如 X 窗口系统),它会占用一块系统级的图形化显示区域。在本手册中,该区域被称为 frame框架 ,而 "window窗口" 一词专指框架中用于显示缓冲区的区域。一个框架初始时仅包含一个窗口,也可将其拆分为多个窗口(参见多窗口章节)。框架通常还包含菜单栏、工具栏和回显区。
你也可以创建额外的框架(参见创建框架章节)。在同一个 Emacs 会话中创建的所有框架,均可访问相同的底层缓冲区及其他数据。例如,若某个缓冲区同时在多个框架中显示,在其中一个框架中对该缓冲区做出的任何修改,都会立即同步显示在其他所有框架中。
按下 C-x C-c 会关闭当前显示器上的所有框架;若 Emacs 在其他所有显示器上均无打开的框架,该操作会直接结束 Emacs 会话(参见退出 Emacs章节)。若仅需关闭当前选中的框架,按下 C-x 5 0 即可(此处为数字 0,非字母 o)。
本章介绍 Emacs 专用于图形化显示的功能(尤其是鼠标命令),以及管理多框架的相关功能。在文本终端中,这些功能有许多无法使用。不过,仍可在文本终端中创建多个框架,这类框架会逐个显示,并占满整个终端屏幕(参见文本终端章节)。部分文本终端也支持鼠标操作(在 GNU 和 Unix 系统上的使用方法参见文本终端中的鼠标操作章节;在 MS-DOS 系统上的使用方法参见 MS-DOS 中的鼠标使用章节)。所有文本终端均支持菜单功能。
23.1. 编辑相关的鼠标命令
mouse-1- 将光标移动至点击位置 (
mouse-set-point) 。 Drag-mouse-1- 拖动鼠标左键。拖动选中的文本激活选区,并将该文本放入主选择区 (
mouse-set-region) 。 mouse-2- 鼠标中键。将光标移动至点击位置,并在该位置插入主选择区的内容 (
mouse-yank-primary) 。 mouse-3- 鼠标右键。若选区已激活,将选区较近的一端移动至点击位置;若选区未激活,将标记位设为当前光标位置,再将光标移至点击位置。将最终的选区内容存入删除环;再次点击则删除该选区内容 (
mouse-save-then-kill) 。 C-M-mouse-1- 为拖动选中的文本激活矩形选区,详见「矩形操作」章节。
最基础的鼠标命令是 mouse-set-point ,在窗口的文本区域点击鼠标左键,即可调用该命令,它会将光标移动到点击的位置。若点击的窗口并非选中窗口,该窗口会成为新的选中窗口。你也可通过双击 mouse-1 鼠标左键激活选区,详见「单词与行的鼠标命令」章节。
默认情况下,若点击的框架并非选中框架,除了选中对应窗口并设置光标位置外,该框架会被设为选中框架。在 X 窗口系统中,可将变量 x-mouse-click-focus-ignore-position 设为 t 来更改此行为,此时点击未选中的框架,首次点击仅会选中该框架,不执行其他操作;再次点击才会选中窗口并设置光标位置。
按住 mouse-1 鼠标左键并在一段文本上拖动,会为该文本激活选区 (mouse-set-region) ,标记位会落在按住鼠标键的起始位置,光标则在松开鼠标键的结束位置,详见「标记与选区」章节。同时,选区内的文本会成为 主选择区 的内容,详见「与其他窗口程序的剪切粘贴」章节。
若将变量 mouse-drag-copy-region 设为非nil值,拖动鼠标选中文本时,该文本也会被添加至 删除环 ,该变量默认值为 nil 。
若该变量设为 non-empty ,则仅当选区非空时,才会将内容复制到删除环。例如,若鼠标拖动的区域不足半个字符,原本会向删除环中存入空字符串,而设为该值后,这种短距离拖动不会对删除环产生任何影响。
拖动鼠标时,若将鼠标移出窗口的顶部或底部,窗口会以稳定的速率自动滚动,直至将鼠标移回窗口内。通过这种方式,你可以选中无法完整显示在屏幕上的文本区域。每一步的滚动行数,取决于鼠标离窗口边缘的距离;变量 mouse-scroll-min-lines 规定了滚动的最小步长。
若启用选项 mouse-drag-mode-line-buffer ,且窗口系统支持文件拖动,那么在模式行的缓冲区名称区域拖动鼠标,可将该缓冲区对应的文件拖至其他程序或 Emacs 框架中。
点击 mouse-2 鼠标中键,会将光标移至点击位置,并在该位置插入主选择区的内容 (mouse-yank-primary) ,详见「与其他窗口程序的剪切粘贴」章节,该行为与其他 X 应用程序保持一致。你也可将 mouse-2 鼠标中键重新绑定至 mouse-yank-at-click 命令,该命令会在点击位置直接粘贴内容。
若将变量 mouse-yank-at-point 设为非nil值,点击 mouse-2 鼠标中键时不会移动光标,无论点击框架中的哪个位置、哪个窗口,都会在 当前光标位置 插入内容。该变量会同时作用于 mouse-yank-primary 和 mouse-yank-at-click 两个命令。
点击 mouse-3 鼠标右键会执行 mouse-save-then-kill 命令,该命令的行为会根据点击位置和选区的状态发生变化,具体规则如下:
- 若选区未激活,点击鼠标右键会激活选区,标记位为原光标位置,光标移至点击位置;
- 若选区已激活,点击鼠标右键会调整选区较近的一端,将其移至点击位置,调整后的选区内容会被复制到删除环;若原选区的内容已在删除环中,则会替换原有内容;
- 若最初通过双击或三击鼠标左键选中选区(即选区为整词或整行,详见「单词与行的鼠标命令」章节),那么通过鼠标右键调整选区时,也会按整词或整行的规则进行;
- 在同一位置连续第二次点击鼠标右键,会删除已选中的选区内容。
因此,使用鼠标删除文本的最简方式为:在文本一端点击鼠标左键,在另一端连续双击鼠标右键。若只想将文本复制到删除环、而不从缓冲区中删除,只需单击一次鼠标右键—— 或直接用鼠标左键拖动选中文本即可,后续可通过粘贴操作将其复制到其他位置。
mouse-save-then-kill 命令同样遵循变量 mouse-drag-copy-region 的设置(前文已述)。若该变量值为非nil,则每当该命令创建或调整激活的选区时,选区内的文本都会被添加至删除环;若删除环的最新条目是通过相同方式添加的,则会替换该条目,而非新建条目。
无论通过上述哪种鼠标命令设置选区,除了常规的选区取消方式外,后续执行 无移位修饰的光标移动命令 ,都会自动取消标记的激活状态,详见「移位选择」章节。
部分鼠标配有滚轮,可用于滚动页面。Emacs 在大多数图形化显示环境中,默认支持通过鼠标滚轮滚动窗口,可使用 M-x mouse-wheel-mode 切换该功能的开启与关闭。变量 mouse-wheel-follow-mouse 和 mouse-wheel-scroll-amount 决定了滚动的目标窗口和滚动步长;变量 mouse-wheel-progressive-speed 决定了滚动速度是否与滚轮的转动速度相关。
该模式还支持调整字体大小,默认将 Ctrl 键配合滚轮绑定为字体缩放的快捷键。启用该模式后,鼠标滚轮会触发 wheel-up (滚轮上滚)和 wheel-down (滚轮下滚)等特殊事件(部分旧系统会将其识别为 mouse-4 和 mouse-5 )。若鼠标配有水平滚轮,还会触发 wheel-left (滚轮左滚)和 wheel-right (滚轮右滚)事件。
Emacs 还支持 Shift 键配合滚轮实现水平滚动。在开始水平滚动前输入数字前缀参数(如 M-5 ),可修改由用户选项 mouse-wheel-scroll-amount-horizontal 定义的水平滚动步长。
若你的鼠标滚轮支持侧倾,或触控板支持该操作,可将变量 mouse-wheel-tilt-scrolxl 设为非nil值,启用滚轮侧倾的水平滚动功能。默认情况下,侧倾鼠标滚轮会让窗口视图向侧倾方向水平滚动:例如,向右倾滚轮会让窗口向右滚动,窗口中显示的文本则会向左水平移动。若想反转水平滚动的方向,可将变量 mouse-wheel-flip-direction 设为非nil值。
在图像模式下,当鼠标指针置于图片上方时(详见「查看图像文件」章节),Ctrl 键配合滚轮会缩放鼠标指针下方的图片,Shift 键配合滚轮则会让图片水平滚动。
23.2. 针对单词与行的鼠标命令
mouse-1 鼠标左键的这些操作变体可一次选中整词或整行文本。Emacs 会为选中的文本激活选区,同时将文本复制至删除环。
Double-mouse-1双击鼠标左键。选中点击位置所在的整词或字符周边的文本。
若在符号语法的字符(如 C 模式中的下划线)上双击,会选中该字符所在的整个符号;若在左 / 右括号语法的字符上双击,会选中该字符所起始或终止的括号匹配内容;若在字符串分隔符语法的字符(如 C 模式中的单引号、双引号)上双击,会选中整个字符串常量(Emacs 会通过启发式规则判断该字符为字符串的起始还是终止符)。
若在括号匹配内容的起始处或字符串分隔符的起始符上双击,光标会移至选区末尾,必要时会向前滚动缓冲区内容以显示光标的新位置;若在括号匹配内容的终止处或字符串分隔符的终止符上双击,默认光标会停留在选区末尾,若选区起始位置超出窗口上边界则会无法显示;将用户选项
mouse-select-region-move-to-beginning设为非nil值,可改为将光标移至选区起始位置,必要时向后滚动显示内容。Double-Drag-mouse-1- 双击并拖动鼠标左键。以整词为单位,选中拖动范围内的所有文本。
Triple-mouse-1- 三击鼠标左键。选中点击位置所在的整行文本。
Triple-Drag-mouse-1- 三击并拖动鼠标左键。以整行为单位,选中拖动范围内的所有文本。
23.3. 用鼠标跟随引用链接
部分 Emacs 缓冲区中会包含 buttons按钮 或 hyperlins超链接 :这类文本片段在被激活(如点击)时会执行特定操作(例如跳转到对应引用位置)。通常,按钮文本会有视觉高亮效果:或以下划线标注,或在文本外围绘制边框。将鼠标移至按钮上方时,鼠标光标的形状会发生变化,且按钮会亮起。若将变量 mouse-highlight 设为 nil ,Emacs 会关闭该高亮功能。
激活按钮的方式有两种:将光标移至按钮处并按下回车键( RET ),或在按钮上点击鼠标左键( mouse-1 )/ 鼠标中键( mouse-2 )。例如,在 Dired 缓冲区中,每个文件名都是一个按钮,激活该按钮会让 Emacs 打开对应文件(参见《Dired:目录编辑器》章节);在 *Compilation* 编译缓冲区中,每条错误提示信息都是一个按钮,激活后会跳转到该错误对应的源代码位置(参见《在 Emacs 中运行编译操作》章节)。
尽管在按钮上单击鼠标左键通常会激活按钮,但如果按住鼠标左键一段时间后再松开(具体为超过 450 毫秒),Emacs 只会将光标移至点击位置,而不会激活按钮。通过这种方式,可在不激活按钮的前提下,用鼠标将光标移至按钮上方。在按钮上或向按钮区域拖动鼠标时,会执行常规的选区设置操作,不会激活按钮。
可通过自定义变量 mouse-1-click-follows-link ,修改鼠标左键对按钮的作用方式:
- 若该值为正整数,该数值即为取消按钮激活所需按住鼠标左键的时长(单位:毫秒),默认值为前文所述的 450;
- 若该值设为
nil,鼠标左键仅会将光标移至点击位置,不会激活按钮; - 若该值设为
double,双击鼠标左键会激活按钮,单击仅会移动光标。
默认情况下,即便按钮处于未选中的窗口中,在其上点击 mouse-1 鼠标左键仍会激活该按钮。若将变量 mouse-1-click-in-non-selected-windows 设为 nil ,在未选中窗口的按钮上点击 mouse-1 鼠标左键,只会将光标移至点击位置并选中该窗口,而不会激活按钮
23.4. 菜单相关的鼠标点击操作
结合 Ctrl 和 Shift 修饰键的多款鼠标点击操作可调出对应菜单。
C-mouse-1该菜单用于选择缓冲区。
MSB(即 “mouse select buffer鼠标选择缓冲区”)全局次要模式可让此菜单的功能更智能、且支持更多自定义设置,详见《自定义缓冲区菜单》章节。
C-mouse-2- 该菜单包含用于查看和设置外观属性及其他文本属性的菜单项,其中文本属性设置功能主要在编辑富文本时使用(详见《富文本》章节)。
C-mouse-3- 该菜单为模式专属菜单:
- 若开启菜单栏模式,对于大多数主模式,此菜单会整合该模式下菜单栏中的所有专属菜单项;部分主模式可为该操作自定义不同的菜单内容。
- 若关闭菜单栏模式,此菜单会包含菜单栏中的 所有菜单项 (并非仅模式专属项),无需显示菜单栏即可调用全部菜单功能。
S-mouse-1- 该菜单用于修改当前窗口缓冲区的默认面属性,详见《文本缩放》章节。
多数图形界面应用会将鼠标右键( mouse-3 )设为调出上下文菜单的快捷键,此类菜单会根据鼠标点击的位置和上下文,提供相关的设置项与操作选项。Emacs 中鼠标右键的默认功能为绑定 mouse-save-then-kill 命令(详见《编辑相关的鼠标命令》章节),若你更习惯将其用作上下文菜单,可启用 context-menu-mode (上下文菜单模式):启用后,点击 mouse-3 鼠标右键即可调出上下文菜单,菜单的具体内容会根据当前主模式、以及鼠标点击位置附近的缓冲区内容动态变化。
你可通过变量 context-menu-functions 自定义上下文菜单的内容(详见《Emacs Lisp 参考手册》中的《主模式约定》章节),也可通过按下 S-F10 快捷键调出上下文菜单。
23.5. 模式行鼠标命令
可通过点击窗口的模式行,实现窗口的选择与各类操作。
模式行的部分区域(如缓冲区名称、主模式与次要模式名称处)配有专属的鼠标绑定命令。将鼠标悬停在这些区域时,区域会高亮显示,同时相关绑定命令的说明信息也会弹出(参见工具提示相关内容)。本节介绍的命令不适用于这些特殊区域。
mouse-1- 在模式行上点击
mouse-1鼠标左键,会选中该模式行所属的窗口。在模式行上按住mouse-1鼠标左键并拖动,可移动模式行的位置,进而调整其上下方窗口的高度。通过这种方式用鼠标调整窗口高度时,绝不会删除任何窗口,仅会限制窗口高度,使其不小于最小高度值。 mouse-2- 在模式行上点击
mouse-2鼠标中键,会将该模式行所属的窗口最大化,占满其所在的整个框架。 mouse-3- 在模式行上点击
mouse-3鼠标右键,会关闭该模式行所属的窗口。若该框架中仅有一个窗口,此操作将无任何效果。 C-mouse-2- 在模式行上按住 Ctrl 并点击鼠标中键,会拆分该模式行所属的窗口,生成两个左右并排的窗口,窗口的分隔线会落在鼠标点击的位置(参见窗口拆分相关内容)。
此外,在两个左右并排窗口的模式行分隔处,按住 mouse-1 鼠标左键并拖动,可左右移动窗口的垂直分隔线,调整两侧窗口的宽度。
请注意,窗口的调整操作会受 window-resize-pixelwise 变量的取值影响,相关说明参见窗口拆分章节。
23.6. 创建框架
前缀键 C-x 5 与 C-x 4 的功能逻辑相似: C-x 4 系列命令会在选中框架的新窗口中弹出指定缓冲区(参见《在另一窗口中显示内容》章节),而 C-x 5 系列命令则会在新的框架中完成该操作。若目标缓冲区已显示在某个可见或最小化的框架中(“最小化” 亦作图标化,参见《Emacs Lisp 参考手册》中《框架的可见性》章节),该框架会被前置并还原为正常显示状态(取消最小化);若未找到对应框架,则会在当前显示终端中新建一个框架。
C-x 5 下的各类命令,核心区别在于 查找或创建待选中缓冲区的方式 ,具体命令如下:
C-x 5 2- 使用默认框架参数创建一个新框架 (
make-frame-command) 。 C-x 5 c- 沿用当前框架的窗口配置与框架参数,克隆一个新框架 (
clone-frame) 。 C-x 5 b bufname RET- 在另一个框架中选中指定名称的缓冲区,对应命令
switch-to-buffer-other-frame。 C-x 5 f filename RET- 打开指定文件,并在另一个框架中选中该文件对应的缓冲区,对应命令
find-file-other-frame(参见《打开文件》章节)。 C-x 5 d directory RET- 在另一个框架中为指定目录打开 Dired 缓冲区,对应命令
dired-other-frame(参见《Dired:目录编辑器》章节)。 C-x 5 m- 在另一个框架中开始撰写邮件,对应命令
compose-mail-other-frame,是C-x m命令的跨框架版本(参见《发送邮件》章节)。 C-x 5 .- 在另一个框架中查找标识符的定义,对应命令
xref-find-definitions-other-frame,是M-.命令的多框架版本(参见《查找标识符引用》章节)。 C-x 5 r filename RET- 以只读方式打开指定文件,并在另一个框架中选中该缓冲区,对应命令find-file-read-only-other-frame(参见《打开文件》章节)。
C-x 5 5- 一个更通用的前缀命令 (
other-frame-prefix) ,作用于紧随其后执行的命令:该前缀会让后续命令所显示的缓冲区,在另一个框架中展示。
你可以通过 指定框架参数 ,控制新建框架的外观与行为,相关说明参见《框架参数》章节。
23.7. 框架命令
下述命令用于框架的删除与各类操作:
C-x 5 0- 删除当前选中的框架 (
delete-frame) 。若当前仅存在一个框架,执行此命令会触发错误提示。 C-x 5 u- 当启用
undelete-frame-mode(框架恢复模式)时,可恢复最近删除的 16 个框架中的一个。无前缀参数时,恢复 最近刚删除 的框架;若传入 1 到 16 之间的数字前缀参数(数字 1 对应最近删除的框架),则恢复对应序号的已删除框架。 C-z- 将当前选中的 Emacs 框架最小化(图标化) (
suspend-frame) ,详见《退出 Emacs》章节。 C-x 5 o- 选中并前置另一个框架。重复执行此命令,会在当前终端的所有框架间循环切换。
C-x 5 1- 删除当前终端上除选中框架外的所有其他框架。
M-F10- 切换当前框架的最大化状态,框架最大化时会占满整个屏幕。
F11- 切换当前框架的全屏模式。(全屏与最大化的区别通常在于:全屏模式会隐藏窗口管理器的装饰栏,为 Emacs 本身让出更多的屏幕空间。)
请注意,在部分窗口管理器环境下,若要让框架实现真正的最大化或全屏,需将变量 frame-resize-pixelwise 自定义设为非nil值。当该变量为非nil时,框架整体支持 像素级精度 调整大小,而非仅以行和列为单位的整数倍调整。
C-x 5 0 (delete-frame) 命令用于删除选中的框架,但为防止用户失去与 Emacs 会话的交互能力,该命令会拒绝删除 Emacs 会话中的最后一个框架。需注意,当 Emacs 以守护进程模式运行时(详见《将 Emacs 用作服务器》章节),即便所有常规的交互式框架都被删除,仍会保留一个虚拟框架;此种情况下, C-x 5 0 可删除最后一个交互式框架,用户可通过 emacsclient 重新连接至该 Emacs 会话。
C-x 5 1 (delete-other-frames) 命令仅删除 当前终端 上的其他所有框架(此处的终端指图形化显示器或文本终端,详见《文本终端》章节);若 Emacs 会话在其他图形化显示器或文本终端上还打开了框架,这些框架不会被删除。
C-x 5 o (other-frame) 命令用于选中当前终端的下一个框架。若在 X 窗口系统中使用 Emacs,且所用的窗口管理器会自动选中(或赋予焦点)鼠标光标悬停的任意框架,需将变量 focus-follows-mouse 设为 t ,此命令才能正常工作。设置后,执行 C-x 5 o 还会将鼠标光标 移动至 选中的框架处。
23.8. 字体设置
默认情况下,Emacs 在图形化显示界面中使用 10 号等宽字体 显示文本,且可通过交互方式调整字体大小(参见《文本缩放》章节)。
指定自定义字体的方式有以下几种:
- 在 'Options选项' 菜单中点击 'Set Default Font设置默认字体' ,所选字体会成为所有现有图形化框架的默认字体;若要将该设置保存至后续会话,可在 'Options选项' 菜单中点击 'Save Options保存选项' 。
在初始化文件中添加一行代码,修改
default-frame-alist变量以指定字体参数(参见《框架参数》章节),示例如下:(add-to-list 'default-frame-alist '(font . "DejaVu Sans Mono-10"))重启 Emacs 后,所有新建的图形化框架都会使用该字体作为默认字体。
在 X 资源文件中添加 'emacs.font' 的 X 资源配置,示例如下:
emacs.font: DejaVu Sans Mono-12
需重启 X 窗口系统或执行
xrdb命令,才能让 X 资源文件的配置生效(参见《X 资源》章节)。注意:在 X 资源文件中指定字体名时无需加引号。- 若在 GNOME 桌面或 Haiku 系统中运行 Emacs,可将变量
font-use-system-font设为t(默认值为nil),让 Emacs 跟随系统默认字体的变更,自动调整框架的默认字体。该功能生效的前提是,Emacs 编译时启用了 Gsettings(或旧版 Gconf)支持。(具体使用的 Gsettings 配置项为 'org.gnome.desktop.interface monospace-font-name' 和 'org.gnome.desktop.interface font-name' 。) - 使用命令行选项 '
-fn' (或 '--font' )指定字体(参见《字体指定选项》章节)。
若要查看当前使用的字体,可使用 C-u C-x = 命令,该命令会显示光标处字符的详细信息,同时标注其渲染所用的字体。
字体名的四种表示方式
方式一:Fontconfig 模式
Fontconfig 模式的字体名格式如下:
fontname[-fontsize][:name1=values1][:name2=values2]...
格式中所有方括号内的元素均可省略。其中:
fontname为字体家族名,如Monospace(等宽体)、DejaVu Sans Mono;fontsize为字体的号数(1 印刷点约等于 1/72 英寸);- 'name=values' 项用于指定字体的倾斜、字重等属性,values可为单个值,也可为逗号分隔的多个值;
- 部分属性值仅对应一种属性名,此种情况下可省略 'name=' 部分。
常用字体属性:
- 'slant'
- (倾斜)。italic(斜体)、oblique(伪斜体)、roman(正体)三者之一;
- 'weight'
- (字重)。light(细体)、medium(常规)、demibold(半粗)、bold(粗体)、black(特粗)五者之一;
- 'style'
- (样式)。部分字体会定义融合了倾斜和字重的特殊样式,例如Dejavu Sans的book样式,该样式会覆盖倾斜和字重的单独设置;
- 'width'
- (字宽)。condensed(紧缩)、normal(常规)、expanded(加宽)三者之一;
- 'spacing'
- (字符间距)。monospace(等宽)、proportional(比例间距)、dual-width(双宽)、charcell(字符格)四者之一。
Fontconfig 模式示例:
Monospace Monospace-12 Monospace-12:bold DejaVu Sans Mono:bold:italic Monospace-12:weight=bold:slant=italic
关于 Fontconfig 模式的详细说明,可参考 Fontconfig 手册(随 Fontconfig 分发,在线地址:https://fontconfig.org/fontconfig-user.html)。
注意:在 Windows 系统中,所有字体仅支持 fontname[-fontsize] 的简化格式,完整的 Fontconfig 模式并非对所有字体生效。
方式二:GTK 字体模式
GTK 字体模式的语法如下:
fontname [properties] [fontsize]
其中fontname为字体家族名,properties为空格分隔的属性值列表,fontsize为字体号数。
GTK 支持的字体属性:
- 倾斜属性:Italic(斜体)、Oblique(伪斜体),省略时默认为正体;
- 字重属性:Bold(粗体)、Book(书体)、Light(细体)、Medium(常规)、Semi-bold(半粗)、Ultra-light(超轻),省略时默认为Medium;
- 字宽属性:Semi-Condensed(半紧缩)、Condensed(紧缩),省略时使用默认字宽。
GTK 字体模式示例:
Monospace 12 Monospace Bold Italic 12
注意:在 Windows 系统中,仅支持fontname的最简格式。
方式三:XLFD(X 逻辑字体描述)
这是 X 窗口系统下指定字体的传统方式,Windows 系统也支持该格式。每个 XLFD 由 14 个以连字符分隔的单词或数字组成,示例如下:
-misc-fixed-medium-r-semicondensed--13-*-*-*-c-60-iso8859-1
XLFD 中的通配符 '*' 可匹配任意字符序列(包括空序列), '?' 可匹配单个字符;但匹配规则由具体实现决定,当通配符匹配长名称中的连字符时,结果可能不准确。为保证匹配可靠,需保留全部 14 个连字符,且仅在单个字段内使用通配符。XLFD 的大小写不敏感,完整语法如下:
-maker-family-weight-slant-widthtype-style… …-pixels-height-horiz-vert-spacing-width-registry-encoding
各字段含义:
- maker
- 字体制造商名称;
- family
- 字体家族名(如courier);
- weight
- 字重,通常为bold(粗)、medium(常规)、light(细),部分字体支持其他值;
- slant
- 倾斜样式,通常为r(正体)、i(斜体)、o(伪斜体)、ri(反向斜体)、ot(其他),部分字体支持其他值;
- widthtype
- 字宽,通常为normal(常规)、condensed(紧缩)、semicondensed(半紧缩)、extended(加宽),部分字体支持其他值;
- style
- 可选的附加样式名,通常为空(此时该位置会出现两个连续的连字符);也可指定 ISO-639 双字母语言代码(如ja日语、ko韩语),部分支持中日韩文字的字体会在此字段标注相关信息;
- pixels
- 字体高度(单位:像素);
- height
- 屏幕上的字体高度(单位:0.1 印刷点),即字体号数乘以 10;在固定垂直分辨率下,该值与pixels成正比,因此通常只需指定其中一个,另一个用*替代;
- horiz
- 字体适配的屏幕水平分辨率(单位:像素 / 英寸);
- vert
- 字体适配的屏幕垂直分辨率(单位:像素 / 英寸);系统字体的分辨率通常与当前屏幕匹配,因此这两个字段一般均设为*;
- spacing
- 字符间距类型,m(等宽)、p(比例间距)、c(字符格)三者之一;
- width
- 平均字符宽度(单位:0.1 像素);
- registry
- encoding
- 字体对应的 X 字体字符集(与 Emacs 字符集不同,但相似),可通过xfontsel程序查看可选值,通常注册表设为 'iso8859' 、编码设为 '1' 。
方式四:字体别名
部分字体会有简短的别名,可替代完整的字体规范表示,例如6x13等价于:
-misc-fixed-medium-r-semicondensed--13-*-*-*-c-60-iso8859-1
注意:该方式在 Windows 系统中不被支持。
X 窗口系统下的 Emacs 字体类型
在 X 系统中,Emacs 支持两种字体:
- 客户端字体:由 Xft 和 Fontconfig 库提供,支持抗锯齿、亚像素微调等高级字体特性;
- 服务端字体:由 X 服务器自身提供,不支持上述高级特性。
注意:Fontconfig 模式和 GTK 模式仅能匹配客户端字体。
等宽字体的查询与预览
Emacs 的默认字体建议使用等宽字体(所有字符宽度相同),不同类型字体的查询方式如下:
Xft/Fontconfig 客户端等宽字体:使用fc-list命令查询
fc-list :spacing=mono fc-list :spacing=charcell
X 服务端等宽字体:使用xlsfonts程序查询
xlsfonts -fn '*x*' | grep -E '^[0-9]+x[0-9]+' xlsfonts -fn '*-*-*-*-*-*-*-*-*-*-*-m*' xlsfonts -fn '*-*-*-*-*-*-*-*-*-*-*-c*'
XLFD 格式中, 间距字段为 'm' 或 'c' 的字体均为等宽字体。可使用xfd命令预览指定字体的样式,示例如下:
xfd -fn 6x13
该命令会显示 '6x13' 字体的所有字符。
补充说明
在 Emacs 运行过程中,还可为 特定类型的文本 单独设置字体(参见《文本面属性》章节),或为 单个框架 自定义字体(参见《框架参数》章节)。
23.9. 速览栏框架
speedbar速览栏 是一个特殊框架,用于便捷地在另一框架中进行导航或执行操作。速览栏一旦创建,始终与某个特定框架相关联,该框架被称为其 attached frame关联框架 ;所有速览栏操作均作用于这个关联框架。
键入 M-x speedbar 可创建速览栏,并将其与当前框架相关联。要关闭速览栏,可再次键入 M-x speedbar ,或选中速览栏后键入 q (也可像删除其他 Emacs 框架一样直接删除速览栏框架)。若要将速览栏关联至其他框架,需先关闭现有速览栏,再在目标框架中执行 M-x speedbar 命令。
速览栏支持多种工作模式, various modes文件显示模式 为其默认模式:该模式下,速览栏会按行显示关联框架选中窗口当前目录下的所有文件。点击非目录文件,会在关联框架的选中窗口中打开该文件(参见《用鼠标跟随引用链接》章节);点击目录,则会在速览栏中展示该目录的内容。每一行内容旁都有一个 '[+]' 或 '<+>' 的方框,点击该方框可 展开 对应项目的内容:展开目录会将该目录下的内容添加至速览栏中对应目录行的下方;展开普通文件则会在速览栏中列出该文件内的所有标签,点击标签名可跳转到关联框架选中窗口中该标签的对应位置。当文件或目录被展开后,旁侧的 '[+]' 会变为 '[-]' ,点击该方框可 折叠 项目,隐藏其展开的内容。
也可通过 键盘 操作速览栏进行导航:光标停在速览栏某一行时键入回车键( RET ),效果等同于点击该行的项目;按下空格键( SPC )可展开或折叠对应项目;按下 U 会显示当前目录的上级目录。要复制、删除或重命名光标所在行的文件,可分别键入 C 、 D 、 R ;按下 M 可创建新目录。
速览栏的另一通用模式为 Buffer Display mode缓冲区显示模式 ,该模式下速览栏会列出所有 Emacs 缓冲区。在速览栏中键入 b 可切换至该模式,键入 f 则可恢复为文件显示模式。也可在速览栏窗口的任意位置点击鼠标右键( mouse-3 )(或在模式行点击鼠标左键 mouse-1 ),在弹出的菜单中选择 显示方式 来切换显示模式。
部分主模式(包括 Rmail 邮件模式、Info 信息模式和 GUD 调试模式)为速览栏提供了 专属适配功能 ,会将该模式下的常用项目载入速览栏供用户选择。例如,在 Rmail 模式下,速览栏会列出所有 Rmail 邮件文件,点击文件旁的 '<M>' 方框,可将当前邮件移动至该 Rmail 文件中。
关于速览栏的使用与编程开发细节,详见《速览栏手册》中的速览栏相关章节。
23.10. 多显示器支持
单个 Emacs 程序可连接至多个 X 显示器。Emacs 启动时默认仅使用一个显示器 —— 即通过 DISPLAY 环境变量或 '–display' 选项指定的显示器(参见《启动选项》章节)。若要连接至其他显示器,可使用 make-frame-on-display 命令:
M-x make-frame-on-display RET 显示器名 RET- 在指定显示器上创建一个新框架。
单个 X 服务器可管理多个屏幕。当在同一服务器的两个屏幕上打开 Emacs 框架时,Emacs 会识别出这些屏幕共享一个键盘,并将来自这些屏幕的所有命令视作单一的输入流进行处理。
若在 不同 X 服务器 的显示器上打开 Emacs 框架,Emacs 会为每个服务器创建独立的输入流,且每个服务器各自拥有一个选中框架。通过某一特定 X 服务器输入的命令,仅会作用于该服务器对应的选中框架。
在多显示器显示环境中,还可使用 make-frame-on-monitor 命令:
M-x make-frame-on-monitor RET 显示器名 RET- 在指定的显示器上创建新框架,该显示器的屏幕区域为当前显示设备的一部分。
23.11. 框架参数
可通过在变量 default-frame-alist 中指定 框架参数默认列表 ,控制所有框架的默认外观与行为。该变量的值为一个参数条目列表,每个条目均指定一个参数名及其对应取值,这些条目会在 Emacs 创建新框架(包括初始框架)时生效。
例如,可在初始化文件中添加以下代码(参见《Emacs 初始化文件》章节),将默认框架宽度设为 90 字符列、默认框架高度设为 40 字符行,并将默认字体设为等宽字体 10 号( 'Monospace-10' ):
(add-to-list 'default-frame-alist '(width . 90)) (add-to-list 'default-frame-alist '(height . 40)) (add-to-list 'default-frame-alist '(font . "Monospace-10"))
关于框架参数的完整列表及其作用,详见《Emacs Lisp 参考手册》中的《框架参数》章节。
也可通过自定义变量 initial-frame-alist ,指定仅对 初始框架 生效的框架参数列表。
若 Emacs 编译时启用了 X 工具包支持,用于指定颜色和字体的框架参数将 不会作用于菜单和菜单栏 ,因为这类界面元素由 X 工具包绘制,而非 Emacs 直接渲染。
框架的外观与行为也可通过X 资源进行自定义(参见《X 选项与资源》章节),且 X 资源的配置会覆盖初始化文件中为初始框架指定的参数。
请注意,若使用桌面库保存和恢复 Emacs 会话,待恢复的框架及其参数会一同记录在桌面文件中。恢复这些框架时,文件中记录的参数会 优先于 初始化文件里 default-frame-alist 和 initial-frame-alist 所指定的框架参数。如何避免该情况,详见《保存 Emacs 会话》章节。
23.12. 滚动条
在图形化显示界面中,Emacs 的每个窗口侧边都会显示一个 垂直滚动条 。点击滚动条的上下箭头按钮,窗口会每次滚动一行(部分工具包支持自定义滚动条,可隐藏此类箭头按钮);在滚动条滑块的上方或下方点击 mouse-1 鼠标左键,窗口会分别向上、向下滚动近乎一整屏的内容,效果等同于快捷键 M-v 和 C-v (参见《移动光标位置》章节)(该行为在部分工具包中也可自定义);拖动滑块则可实现窗口的连续滚动。
若 Emacs 在 X 窗口系统中编译时未启用 X 工具包支持,滚动条的操作行为会有所不同:在滚动条的任意位置点击 mouse-1 鼠标左键,窗口会像按下 C-v 一样向下滚动;点击 mouse-3 鼠标右键,窗口会像按下 M-v 一样向上滚动;点击 mouse-2 鼠标中键并拖动,可上下移动滚动条滑块。
键入 M-x scroll-bar-mode 可 切换垂直滚动条的启用状态 ,该命令对所有框架生效,包括尚未创建的框架;若仅需切换当前选中框架的垂直滚动条状态,可使用命令 M-x toggle-scroll-bar 。
若要在 Emacs 启动时就控制垂直滚动条的启用状态,可自定义变量 scroll-bar-mode (参见《自定义配置》章节)。该变量的取值有三种: right (将滚动条置于窗口右侧)、 left (将滚动条置于窗口左侧)、 nil (禁用垂直滚动条)。默认情况下,若 Emacs 在 X 窗口系统中编译时启用了 GTK+ 支持,或运行在 Windows、macOS 系统中,滚动条会默认显示在窗口右侧;若 Emacs 在 X 窗口系统中编译时未启用 GTK+ 支持,滚动条会默认显示在窗口左侧(遵循 X 应用程序的旧有惯例)。
也可通过 X 资源项 'verticalScrollBars' 启用或禁用滚动条(参见《X 资源》章节);若要调整滚动条的宽度,可修改框架参数 scroll-bar-width (参见《Emacs Lisp 参考手册》中的《框架参数》章节)。
若在 X 窗口系统中使用 Emacs(启用 GTK+ 或 Motif 支持),可自定义变量 scroll-bar-adjust-thumb-portion ,控制滚动条的 超滚动 行为(即即便缓冲区末尾已显示在窗口中,仍可向下拖动滑块)。当该变量取值为非nil时,即便缓冲区末尾可见,仍可向下拖动滚动条滑块;取值为 nil 时,若缓冲区末尾已显示,滑块会固定在滚动条最下方。当整个缓冲区内容均可在窗口中显示时,无法触发超滚动行为。
滚动条的 视觉样式 由 scroll-bar 滚动条外观属性控制(部分工具包如 GTK+、Windows 会忽略该面属性,其滚动条样式仅能通过系统全局设置自定义,GTK + 的相关配置可参见《GTK + 资源》章节)。
在图形化框架中,垂直滚动条还会在 视觉上分隔左右并排的窗口 。当垂直滚动条被禁用时,Emacs 会默认使用 1 像素宽的垂直边框分隔这类窗口。该边框会占据右侧窗口的第一列像素,可能会遮挡窗口中显示的字符最左侧的像素。若这些像素包含重要信息,可启用 窗口分隔线 使其显示(参见《窗口分隔线》章节);若要还原垂直边框的视觉效果,可将框架的 right-divider-width 参数设为 1,并让窗口分隔线的面属性继承垂直边框的面属性(详见《Emacs Lisp 参考手册》中的《窗口分隔线》章节)。
在启用了工具包支持的图形化显示界面中,Emacs 还可为每个窗口的底部添加 horizontal scroll bar水平滚动条 。点击水平滚动条的左右箭头按钮,窗口会每次水平滚动一列;(注意:部分工具包支持自定义滚动条,可隐藏此类箭头按钮);在水平滚动条滑块的左侧或右侧点击鼠标左键,窗口会分别向左、向右滚动四列;拖动滑块则可实现窗口的连续水平滚动。
需注意,水平滚动可能会导致 光标位置移出窗口的左右可视区域 。此时键入字符插入文本,或使用键盘命令移动光标,光标通常会重新回到可视区域内。
键入 M-x horizontal-scroll-bar-mode 可 切换水平滚动条的启用状态 ,该命令对所有框架生效,包括尚未创建的框架;若仅需切换当前选中框架的水平滚动条状态,可使用命令 M-x toggle-horizontal-scroll-bar 。
若要在 Emacs 启动时控制水平滚动条的启用状态,可自定义变量 horizontal-scroll-bar-mode 。
也可通过 X 资源项 'horizontalScrollBars' 启用或禁用水平滚动条(参见《X 资源》章节);若要调整水平滚动条的高度,可修改框架参数 scroll-bar-height (参见《Emacs Lisp 参考手册》中的《框架参数》章节)。
23.13. 窗口分隔线
在图形化显示界面中,可启用 window dividers 窗口分隔线实现窗口的视觉分隔。窗口分隔线为可通过鼠标拖动的条状区域,借助它能轻松调整相邻窗口的尺寸。
键入命令 M-x window-divider-mode ,可切换窗口分隔线的显示状态。
如需自定义窗口分隔线的显示位置,可配置选项 window-divider-default-places ,其取值有三种: bottom-only (仅在窗口底部显示分隔线)、 right-only (仅在窗口右侧显示分隔线)、 t (在窗口底部和右侧均显示分隔线)。
如需调整该模式下窗口分隔线的宽度,可分别配置选项 window-divider-default-bottom-width (底部分隔线宽度)和 window-divider-default-right-width (右侧分隔线宽度)。
当垂直滚动条被禁用时,窗口分隔线还能发挥实用作用 —— 让窗口的第一列像素保持可见。若不启用分隔线,这一列像素会被用于分隔左右并排窗口的垂直边框遮挡(参见《滚动条》章节)。
关于窗口分隔线的更多细节,详见《Emacs Lisp 参考手册》中的窗口分隔线相关章节
23.14. 拖放功能
在大多数图形化桌面环境中,Emacs 支持基础的拖放操作。例如,将文本拖放至 Emacs 框架中,文本会被插入到拖放的位置;将文件拖放至 Emacs 框架中,Emacs 会打开该文件。一个特殊场景为:若将文件拖放至 Dired 缓冲区,该文件会根据源应用的操作规则,被移动或复制到该缓冲区所显示的目录中。
将文件拖放至 Emacs 时,默认会在拖放目标窗口中打开该文件。若希望此类场景下在新窗口中打开文件,可自定义变量 dnd-open-file-other-window 。
Emacs 目前支持 XDND、Motif 拖放协议,以及旧版的 KDE 1.x 拖放协议。
在将文本拖向 Emacs 窗口的过程中,若要滚动窗口或确定拖放文本的插入位置,操作起来会较为不便。将选项 dnd-indicate-insertion-point 设为非nil值后,拖放过程中鼠标在窗口内移动时,光标会自动跳至文本即将被插入的位置;将 dnd-scroll-margin 设为整数值后,拖放时若鼠标移动至窗口顶部或底部的该数值行范围内,窗口会自动滚动。
Emacs 也可通过鼠标将选区拖放至当前缓冲区或其他缓冲区的任意位置,该功能需将变量 mouse-drag-and-drop-region 设为非nil值方可启用。默认规则为:若拖放的目标缓冲区与源缓冲区为同一个,文本会被移动(即剪切并粘贴);若拖放至其他缓冲区,则仅复制文本。若将该变量设为某一修饰键名称(如'shift'、'control'或'alt'),则拖放时按住该修饰键,即便拖放至源缓冲区,文本也会被复制而非剪切。
若希望即便源缓冲区与目标缓冲区不同,拖放时仍执行剪切操作,可将选项 mouse-drag-and-drop-region-cut-when-buffers-differ 设为非nil值。默认情况下,在图形化显示界面中,拖放过程中选中的文本会在工具提示中显示,且光标会随鼠标指针一同移动。若要关闭该行为,可将选项 mouse-drag-and-drop-region-show-tooltip 或 mouse-drag-and-drop-region-show-cursor 设为nil。
若要将文本从 Emacs 拖放至其他程序,需将选项 mouse-drag-and-drop-region-cross-program 设为非nil值。
在 X 窗口系统中,部分程序可将文件拖放至 Emacs,期望 Emacs 对其进行保存。默认情况下,Emacs 会先提示用户输入保存的文件名,完成保存后再打开该文件;可通过修改变量 x-dnd-direct-save-function 更改此行为。更多细节详见《Emacs Lisp 参考手册》中的拖放操作相关章节。
23.15. 菜单栏
可使用 M-x menu-bar-mode 命令切换菜单栏的启用状态。不带参数执行该命令时,会对 菜单栏模式 (一款全局次要模式)进行开关切换;带参数执行时,若参数为正数则开启菜单栏模式,非正数则关闭。若要在 Emacs 启动时控制菜单栏的启用状态,可自定义变量 menu-bar-mode 。
资深用户通常会关闭菜单栏,在文本终端中尤为如此 —— 关闭后能多腾出一行空间用于显示文本。若菜单栏已关闭,在支持弹出菜单的显示界面中,可通过 C-mouse-3 调出包含菜单栏全部内容的弹出菜单;也可启用上下文菜单模式,并自定义变量 context-menu-functions ,通过点击鼠标右键(mouse-3)调出上下文菜单,相关操作详见《菜单的鼠标点击操作》章节。
关于如何通过菜单栏调用命令的相关说明,参见《菜单栏》章节;关于如何自定义菜单栏及菜单项视觉样式的方法,参见《X 选项与资源》章节。
23.16. 工具栏
在图形化显示界面中,Emacs 会在每个框架的顶部、菜单栏正下方显示一个工具栏。工具栏是一排带有图标的按钮,点击这些按钮可调用各类对应命令。Emacs 也可根据需要,在每个窗口的顶部单独显示工具栏(参见《窗口工具栏》章节)。
全局(默认)工具栏包含各类通用命令,部分主模式也会定义专属工具栏:当当前缓冲区使用这类带专属工具栏的主模式时,该主模式的工具栏会替换全局工具栏。若要阻止这种替换行为,可自定义变量 tool-bar-always-show-default 。
键入 M-x tool-bar-mode 可切换工具栏的启用状态,该命令对所有框架生效,包括尚未创建的框架。若要在 Emacs 启动时控制工具栏的启用状态,可自定义变量 tool-bar-mode 。
当 Emacs 编译时启用了 GTK+ 支持,每个工具栏项可仅显示图片、仅显示文本标签,或同时显示两者。默认情况下,Emacs 会遵循 GNOME 桌面的工具栏样式设置;若未定义相关设置,则仅以图片形式显示工具栏项。若要强制使用特定的工具栏样式,可自定义变量 tool-bar-style 。
对于 GTK+ 工具栏,还可通过框架参数 tool-bar-position 控制其显示位置,详见《Emacs Lisp 参考手册》中的《框架参数》章节。
NS 版本的 Emacs 将工具栏视作 窗口装饰元素 ,因此当窗口取消装饰时,工具栏也会随之隐藏,相关说明详见《Emacs Lisp 参考手册》中的《框架参数》章节。在 macOS 系统中,当框架进入全屏模式时工具栏会被隐藏,将鼠标指针移至屏幕顶部即可重新显示工具栏。
部分键盘会缺少 Emacs 用户可能需要用到的修饰键(参见《修饰键》章节),导致用户无法或难以输入带这些修饰键的按键序列。例如,许多键盘无 Hyper 和 Super 修饰键,智能手机的虚拟键盘通常也没有 Ctrl 和 Alt 修饰键。针对此情况,Emacs 可选择性显示一个由修饰键按钮组成的额外工具栏,即 modifier bar修饰键栏 。点击修饰键栏中的按钮后,Emacs 会将该按钮对应的修饰键应用到接下来读取的一次键盘操作中。启用全局次要模式 modifier-bar-mode 即可显示修饰键栏,键入 M-x modifier-bar-mode 可完成该模式的开关切换。
23.17. 标签栏
在图形化显示界面和文本终端中,Emacs 可根据需要在每个框架的顶部显示 Tab Bar标签栏 ,其位置位于菜单栏下方(参见《菜单栏》)、工具栏上方或下方(参见《工具栏》),具体由变量 tab-bar-position 控制。标签栏是一排标签按钮,点击即可在不同的窗口配置间切换。
标签栏中的每个标签,代表其所属框架的一个 命名持久化窗口配置 ,即该框架被划分为多个窗口的布局方式,以及每个窗口中显示的对应缓冲区。标签名称由该窗口配置下各窗口所显示的缓冲区名称组合而成。点击标签会切换至该标签记录的窗口配置,该配置是此框架中该标签作为当前标签时曾使用过的窗口与缓冲区布局。
若你使用桌面库保存和恢复 Emacs 会话(参见《保存 Emacs 会话》),标签栏中的所有标签会与其关联的窗口配置一同记录在桌面文件中,恢复会话后这些标签仍可使用。
请注意, 标签栏 与 标签行 (参见《窗口标签行》)并非同一功能:窗口顶部标签行的标签,用于在当前窗口的不同缓冲区间切换;而框架顶部标签栏的标签,用于在包含多个窗口、显示一个或多个缓冲区的不同窗口配置间切换。
键入 M-x tab-bar-mode 可切换标签栏的启用状态,该命令对所有框架生效,包括尚未创建的框架。若要在 Emacs 启动时控制标签栏的启用状态,可自定义变量 tab-bar-mode 并保存该配置。
变量 tab-bar-show 用于控制标签栏模式是否自动开启:
- 若取值为
t,执行创建新标签的命令时会自动启用标签栏模式; - 若取值为
1,当标签栏仅有一个标签时会自动隐藏,创建更多标签后则重新显示; - 更通用的规则为,若取值为非负整数,仅当标签数量大于该数值时,标签栏才会显示;
- 若取值为
nil,标签栏会始终隐藏,此时仍可通过M-x tab-next、M-x tab-switcher等支持标签名补全的命令,在不显示标签栏的情况下切换命名窗口配置,也可通过M-x tab-new、M-x tab-close等命令完成标签的创建与关闭。
需注意,若 tab-bar-show 设为数值,标签栏可能会在部分框架中显示、另一部分框架中隐藏,具体取决于各框架中创建的标签数量。
若仅需切换当前选中框架的标签栏启用状态,可键入 M-x toggle-frame-tab-bar 。该命令可实现对不同框架的标签栏进行选择性启用,不受 tab-bar-mode 和 tab-bar-show 取值的影响。
前缀键 C-x t 的功能逻辑与 C-x 5 相似: C-x 5 系列命令会在不同框架中打开缓冲区(参见《创建框架》),而 C-x t 系列命令会在选中框架的不同标签中,以不同的窗口配置打开缓冲区。
C-x t 下的各类命令,核心区别在于查找或创建待选中缓冲区的方式,以下命令可在新标签中选中指定缓冲区:
C-x t 2- 新建一个标签 (
tab-new) 。可通过自定义变量tab-bar-new-tab-choice,控制新标签中默认显示的缓冲区;也可通过自定义变量tab-bar-tab-name-function,设置新标签的默认命名规则。 C-x t bbufname RET=- 在另一个标签中选中指定名称的缓冲区,对应命令
switch-to-buffer-other-tab。 C-x t ffilename RET=- 打开指定文件(参见《打开文件》),并在另一个标签中选中该文件对应的缓冲区,对应命令
find-file-other-tab。 C-x t ddirectory RET=- 在另一个标签中编辑指定目录(参见《Dired:目录编辑器》),对应命令
dired-other-tab。 C-x t t- 一个前缀命令 (
other-tab-prefix) ,作用于紧随其后执行的命令:该前缀会让后续命令所显示的缓冲区,在新的标签中展示。
默认情况下,新建标签会显示执行新建命令前的当前缓冲区。若要让新标签默认显示其他缓冲区,可自定义变量 tab-bar-new-tab-choice 。
变量 tab-bar-new-tab-to 用于控制新标签的位置,默认情况下,新标签会添加在当前标签的右侧。
标签的删除命令
C-x t 0- 关闭当前选中的标签 (
tab-close) 。若标签栏中仅有一个标签,该操作默认无效果,除非将变量tab-bar-close-last-tab-choice自定义为非默认值。 C-x t 1- 关闭当前选中框架中,除当前标签外的所有其他标签。
变量 tab-bar-close-tab-select 用于控制关闭当前标签后选中的目标标签,默认会选中最近使用过的标签。
命令 tab-undo 可恢复最后一个被关闭的标签。
标签的切换命令
C-x t oC-TAB- 切换至下一个标签 (
tab-next) 。重复执行该命令,会在当前选中框架的所有标签间循环切换。带正数值前缀参数n时,切换至第n个下一个标签;带负数值前缀参数 - n 时,切换至第 n 个上一个标签。 S-C-TAB- 切换至上一个标签 (
tab-previous) 。带正数值前缀参数n时,切换至第n个上一个标签;带负数值前缀参数-n时,切换至第n个下一个标签。 C-x t RET tabname RET- 根据标签名切换标签 (
tab-switch) ,支持所有标签名的补全功能。标签名的默认候选列表和 “future history历史未来项” 会按最近使用顺序排序,因此可使用M-n(next-history-element) 快速选择最后一个、倒数第二个曾访问的标签名。 modifier-tab-number修饰键 + 标签数字根据标签的序号切换标签 (tab-select) 。先自定义变量tab-bar-select-tab-modifiers,指定一个或多个修饰键后,即可通过 指定修饰键 + 标签数字 的组合键,按序号选中对应标签;数字 9 可用于选中最后一个标签。Emacs 支持的所有修饰键均可配置(参见《修饰键》)。若要在标签名称旁显示标签序号,可自定义变量tab-bar-tab-hints,便于你快速确定选中对应标签所需按下的数字键。modifier-9修饰键 + 9切换至最后一个标签 (tab-last) ,组合键为tab-bar-select-tab-modifiers定义的修饰键 + 数字 9。带数值前缀参数n时,切换至倒数第n个标签。modifier-0修饰键 + 0切换至最近使用的标签 (tab-recent) ,组合键为tab-bar-select-tab-modifiers定义的修饰键 + 数字 0。带数值前缀参数n时,切换至第n个最近使用的标签。
标签的操作命令
C-x t r tabname RET- 将当前标签重命名为指定名称 (
tab-rename) 。 C-x t m- 将当前标签向右移动一个位置 (
tab-move) 。带正数值前缀参数n时,向右移动n个位置;带负数值前缀参数-n时,向左移动n个位置。
标签的鼠标操作
点击鼠标中键( mouse-2 )可关闭对应标签;点击鼠标右键( mouse-3 )会弹出上下文菜单,包含对该标签的各类操作选项;按住鼠标左键( mouse-1 )拖动标签,可将其移至标签栏的其他位置;滚动鼠标滚轮,可在标签间切换至上一个或下一个;滚动滚轮时按住 Shift 键,可将当前标签向左或向右移动。
标签的触摸屏操作
也可通过触摸屏输入操作标签(参见《触摸屏输入与虚拟键盘》): 长按 (参见《在触摸屏上使用 Emacs》)某个标签,会弹出对该标签的操作上下文菜单;长按标签栏本身,会弹出可创建和删除标签的上下文菜单;轻点某个标签,即可选中该标签对应的窗口配置;轻点标签栏上的按钮,效果等同于用 mouse-1 鼠标左键点击该按钮。
可启用 tab-bar-history-mode 模式,让 Emacs 记录每个标签使用过的所有窗口配置,后续可恢复这些配置:
M-x tab-bar-history-back- 恢复当前标签曾使用过的上一个窗口配置,可在窗口配置的历史记录中向后回溯;
M-x tab-bar-history-forward- 撤销上一次的窗口配置恢复操作,可在窗口配置的历史记录中向前前进。
可通过用户选项 tab-bar-format ,自定义标签栏上显示的各类元素。
23.18. 使用对话框
dialog box对话框 是一种特殊的交互窗口,用于向用户询问 yes-or-no 是否 类问题或其他特定问题。若你通过鼠标调用触发该问题的 Emacs 命令,许多 Emacs 命令都会通过对话框提出是否类的询问。
若要禁用对话框,可将变量 use-dialog-box 设为 nil 。此时 Emacs 将始终通过回显区和键盘输入的方式,发起 yes-or-no 是否 类的提示询问。该变量同时也控制是否启用文件选择窗口(但并非所有平台都支持此功能)。
文件选择窗口是用于询问文件名的专用对话框。即便你希望保留其他类型的对话框,也可自定义变量 use-file-dialog 来禁用文件选择窗口。若你已通过变量 use-dialog-box 禁用了所有对话框,此变量将不再生效。
当 Emacs 编译时启用了 GTK+ 支持,其会调用 GTK+ 文件选择对话框。Emacs 为该对话框新增了一个切换按钮,可通过此按钮在对话框中显示或隐藏隐藏文件(以 . 句点开头的文件)。若希望该切换按钮默认处于启用状态,可将变量 x-gtk-show-hidden-files 设为 t 。此外,Emacs 还为 GTK+ 文件选择对话框添加了帮助文本;若要禁用该帮助文本,可将变量 x-gtk-file-dialog-help-text 设为 nil 。
23.19. 工具提示
Tooltips工具提示 是一类小型专用框架,会在当前鼠标位置显示文本信息。当鼠标在窗口中的重要文本区域、模式行,或是 Emacs 框架的其他区域(如工具栏按钮、菜单项)上停留不动时,工具提示便会激活。
可通过命令 M-x tooltip-mode 切换工具提示的启用状态。禁用工具提示模式后,帮助文本将转而在回显区显示。若要控制启动时工具提示的启用状态,可自定义变量 tooltip-mode 。
以下变量为工具提示的显示提供了自定义选项:
tooltip-delay- 该变量指定 Emacs 在显示第一个工具提示前的等待时长,取值单位为秒。
tooltip-short-delay- 该变量指定 Emacs 在已显示第一个工具提示后,为不同项目显示后续工具提示前的等待时长,取值单位为秒。
tooltip-hide-delay- 若鼠标保持不动,工具提示从显示到自动隐藏的间隔时长,取值单位为秒。
tooltip-x-offsettooltip-y-offset- 工具提示的左上角相对于鼠标指针位置的水平、垂直偏移量,取值单位为像素。注意,若
tooltip-frame-parameters被自定义为分别包含left和top参数,这两个偏移量变量将被忽略。选择偏移量数值时,应确保工具提示不会遮挡鼠标指针的热点区域,否则可能干扰鼠标的点击操作。 tooltip-frame-parameters- 用于显示工具提示的框架参数。详见《Emacs Lisp 参考手册》中的「框架参数」章节,以及该手册中的「工具提示」章节。
若需更多工具提示的显示自定义选项,可执行 M-x customize-group RET tooltip RET 进行配置。
当 Emacs 基于 GTK+ 工具包、Nextstep 窗口系统或 Haiku 窗口系统编译构建时,会通过对应工具包显示工具提示,并沿用工具包自身工具提示的默认外观 8。若要禁用此功能,可将变量 use-system-tooltips 设为 nil 。禁用后,或当 Emacs 编译时未加入相应的窗口系统支持时,工具提示文本的大部分显示属性将由 tooltip 面版,以及 X 资源进行指定(参见X 选项与资源)。
GUD 工具提示是一类特殊的工具提示,在使用 GUD 调试程序时,会显示变量的取值。详见调试器操作。
23.20. 鼠标避让功能
在图形化终端中,鼠标指针可能会遮挡 Emacs 框架内的文本内容。Emacs 提供了两种方法来解决这一问题。
第一种,当鼠标指针位于 Emacs 框架内时,你每输入一个自插入字符,Emacs 就会自动隐藏鼠标指针;移动鼠标指针则会使其重新显示。若要禁用该功能,将变量 make-pointer-invisible 设为 nil 即可,详见「显示的自定义」章节。
第二种,你可以启用鼠标避让模式(一款辅助模式),让鼠标指针远离光标位置。启用该模式需自定义变量 mouse-avoidance-mode ,可为其设置不同取值,以多种方式移动鼠标指针:
banish- (归位)按下任意按键时,将鼠标指针移至框架的角落。可通过自定义变量
mouse-avoidance-banish-position,指定指针归位的目标位置。 exil- (暂移)仅当光标过于靠近指针时才将其归位,待光标移开后,允许指针回到原位置。
jum- (跃移)若光标过分靠近指针,将指针向随机方向移动一段随机距离。
animat- (动移)功能与跃移一致,只是会通过分步移动的效果营造出指针的动态移动观感。
cat-and-mous- (猫鼠式)与动移模式功能完全相同。
proteu- (变形移)在动移模式的基础上,还会同时改变鼠标指针的形状。
你也可通过执行命令 M-x mouse-avoidance-mode 启用该模式。每当鼠标避让模式移动鼠标指针时,对应的 Emacs 框架也会被置于顶层显示。
23.21. 文本终端
在文本终端中,Emacs 同一时间仅能显示一个 Emacs 框架,但你仍可创建多个 Emacs 框架并在其中切换。此类终端上的框架切换,与不同窗口配置间的切换方式十分相似。
使用快捷键 C-x 5 2 创建新框架并切换至该框架;使用 C-x 5 o 循环切换所有已存在的框架;使用 C-x 5 0 删除当前框架。
每个框架都有专属编号用于区分。若你的终端同一时间仅能显示一个框架,当前选中框架的编号 n 会以 'Fn' 的形式显示在模式行的开头位置。
'Fn' 实际上是框架的初始名称。你可根据需要为框架设置更具意义的名称,也可通过名称选中对应框架。使用命令 M-x set-frame-name RET name RET ,可为当前选中的框架指定新名称;使用命令 M-x select-frame-by-name RET name RET ,可根据名称选中对应框架。当某一框架被选中时,你为其设置的名称会显示在模式行中。
23.22. 文本终端中的鼠标使用
部分文本终端支持在终端窗口内进行鼠标点击操作。
在兼容 xterm 的终端模拟器中,可使用 M-x xterm-mouse-mode 命令让 Emacs 接管鼠标的基础使用功能 —— 该模式下基本仅支持无修饰键的单次点击操作。新版本的 xterm 还支持鼠标追踪功能。若需使用 xterm 针对此类点击的原生鼠标功能,可在按下鼠标按键的同时按住 SHIFT 键。Xterm 鼠标模式为全局次要模式(参见「次要模式」相关内容),重复执行该命令即可关闭此模式。
在 GNU/Linux 的控制台中,可使用 M-x gpm-mouse-mode 命令启用鼠标支持功能。使用该功能前,你的系统中必须已安装并运行 gpm 服务端程序。请注意,启用此模式后,无法通过鼠标在 Emacs 与其他使用 GPM 的程序之间传输文本,这是由 GPM 和 Linux 内核的功能限制导致的。
有关 MS-DOS 系统下的鼠标支持相关信息,参见「MS-DOS 下的鼠标使用」章节。
24. 国际字符集支持
Emacs 支持多种国际字符集,包括拉丁语系的欧洲变体、越南变体,同时支持阿拉伯文字、婆罗米系文字(适用于孟加拉语、印地语、泰语等语言)、西里尔文字、埃塞俄比亚文字、格鲁吉亚文字、希腊文字、汉字(适用于中、日两国语言)、韩文、希伯来文字以及国际音标字符。Emacs 还支持这些字符的各类编码方式,这类编码也被文字处理软件、邮件客户端等其他国际化软件所采用。
Emacs 通过对所有相关操作提供支持,实现带国际字符文本的编辑功能,具体包括:
- 可打开含非 ASCII 字符的文件、保存非 ASCII 文本,也可在 Emacs 与其调用的程序(如编译器、拼写检查器、邮件客户端)之间传递非 ASCII 文本。设置语言环境(参见「语言环境」)可自动为特定语言或文化配置编码体系及其他相关选项;你也可单独为每个命令指定 Emacs 对文本的编码与解码方式(参见「为文件文本指定编码体系」)。
- 可显示各类文字编码的非 ASCII 字符。在图形化显示界面中,该功能通过调用适配字体实现(参见「定义字体集」);在文本显示界面中,通过向终端发送特殊编码实现(参见「终端输入输出的编码体系」)。若部分字符显示异常,可参考「无法显示的字符」章节,其中介绍了可能出现的问题及对应的解决方法。
- 对于文字自然排版方向为从右至左的文字体系,Emacs 会对其字符重新排序后再显示(参见「双向编辑」),这类文字包括阿拉伯文、希伯来文、叙利亚文、塔安那文等少数文字。
可插入或搜索非 ASCII 字符。你可选择适配自身使用语言的 Emacs 输入方法(参见「选择输入方法」),或使用选择语言环境时默认配置的输入方法;若你的键盘可输入非 ASCII 字符,可选择对应的键盘编码体系(参见「终端输入输出的编码体系」),Emacs 将直接识别这些字符。在图形化显示界面中,现代系统通常会提供原生输入方法,同时也可通过
C-x 8前缀输入 Latin-1 字符(参见「单字节编辑模式」)。在 X 窗口系统中,需将系统区域设置为对应值,以确保 Emacs 正确解析键盘输入(参见「区域设置」与「X 键盘输入的编码体系」)。
本章后续内容将对上述问题展开详细说明。
24.1. 国际字符集简介
国际字符集与文字体系的使用者已制定了诸多标准化程度不一的编码系统,用于文件存储。这类编码系统通常为 multibyte多字节编码 ,即使用两个或更多字节的序列来表示单个非 ASCII 字符。
Emacs 内部采用自研的多字节字符编码,该编码是 Unicode 标准的超集 。这种内部编码支持将几乎所有已知文字体系的字符混合在单个缓冲区或字符串中。Emacs 在读写文件、与子进程交换数据时,会在自身多字节字符编码与其他各类编码系统之间进行转换。
执行命令 C-h h (view-hello-file) 可打开 etc/HELLO 文件,该文件通过展示多种语言的 “hello” 表达,直观呈现了不同的文字体系。若你的终端无法显示部分字符,这些字符会以 '?' 或空心方块形式呈现(参见《无法显示的字符》)。
即便在使用这些字符集的国家,键盘通常也不会为所有字符配备独立按键。你可使用 C-x 8 RET (insert-char) 插入键盘不支持的字符(参见《插入文本》)。部分常用字符配有快捷输入方式:例如,输入 C-x 8 [ 可插入左单引号 ‘ ;若启用智能引号模式,通常直接按 ` 键即可输入。(参见《引号》)。Emacs 还支持多种输入法,通常一种文字体系 / 语言对应一种输入法,可简化对应字符的输入操作(参见《输入法》)。
前缀键 C-x RET 用于调用与多字节字符、编码系统和输入法相关的命令。
命令 C-x = (what-cursor-position) 可显示光标所在位置字符的相关信息。除了《光标位置信息》中介绍的字符位置外,该命令还会展示字符的编码方式。例如,对于字符 'c' ,该命令会在回显区显示以下内容:
Char: c (99, #o143, #x63) point=28062 of 36168 (78%) column=53
'Char:' 后的四个值用于描述光标后方的字符,先显示字符本身,再依次给出其十进制、八进制和十六进制的字符编码。对于非 ASCII 多字节字符,若当前缓冲区的编码系统能对该字符进行 单字节安全编码 ,则会在后续显示 'file' 及该字符在该编码系统中的十六进制表示(参见《编码系统》);若字符的编码长度超过一个字节,Emacs 会显示 'file …' 。
在极少数情况下,Emacs 会遇到 raw bytes原始字节 :即值在 128(八进制 0200)至 255(八进制 0377)范围内的单字节,Emacs 无法将其解析为某类已知非 ASCII 字符编码的一部分。这类原始字节会被归为特殊的 八位字符集 ,Emacs 会将其以转义八进制码形式显示(该方式可自定义,参见《显示的自定义》)。这种情况下, C-x = 会显示原始字节而非文件。此外,Emacs 会将原始字节的字符编码映射至 #x3FFF80~#x3FFFFF 区间并展示,以此与 #x0080~#x00FF 区间的 Unicode 字符区分开。
若为该命令添加前缀参数( C-u C-x = ),会额外调用 describe-char 命令,展示该字符的详细描述信息,包括:
- 字符集名称,以及该字符在对应字符集中的编码;ASCII 字符归属于 ascii 字符集。
- 字符所属的文字体系、语法类型和分类。
- 在当前输入法中输入该字符的按键组合(若当前输入法支持该字符)。
- 字符的编码方式,包括在缓冲区中的内部编码,以及将缓冲区保存为文件时的外部编码。
- 若在图形界面运行 Emacs,会显示该字符对应的字体名称和字形编码;若在文本终端运行 Emacs,会显示发送至终端的编码。
- 若该字符在显示时与后续字符组合形成一个或多个 grapheme cluster字素簇 ,会展示组合信息:图形界面下为对应字体字形,以及参与组合的所有字符。
- 字符的文本属性(参见《Emacs Lisp 参考手册》中的《文本属性》),包括用于显示该字符的所有非默认面,以及包含该字符的所有覆盖层(参见同一手册中的《覆盖层》)。
以下是详细描述的示例(部分行做折行处理以适配手册排版):
position: 1 of 1 (0%), column: 0 # 位置 character: ê (displayed as ê) (codepoint 234, #o352, #xea) # 字符。显示、编码点、八进制、十六进制 preferred charset: unicode (Unicode (ISO10646)) # 首选字符集 code point in charset: 0xEA # 字符集内编码点 script: latin # 文字体系 syntax: w which means: word # 语法类型。w 表示:单词字符 category: .:Base, L:Left-to-right (strong), c:Chinese, j:Japanese, l:Latin, v:Viet # 字符分类。基础字符,L:从左到右(强),c:中文 to input: type "C-x 8 RET ea" or "C-x 8 RET LATIN SMALL LETTER E WITH CIRCUMFLEX" # 输入方式。C-x 8 RET ea buffer code: #xC3 #xAA # 缓冲区编码 file code: #xC3 #xAA (encoded by coding system utf-8-unix) # 文件编码 display: by this font (glyph code) # 显示方式。使用以下字体(字形编码) xft:-PfEd-DejaVu Sans Mono-normal-normal- normal-*-15-*-*-*-m-0-iso10646-1 (#xAC) Character code properties: customize what to show # 字符编码属性:可自定义展示内容 name: LATIN SMALL LETTER E WITH CIRCUMFLEX # 名称 old-name: LATIN SMALL LETTER E CIRCUMFLEX # 旧名称 general-category: Ll (Letter, Lowercase) # 通用分类 decomposition: (101 770) ('e' '^') # 字符分解
24.2. 语言环境
只要启用了多字节字符功能,Emacs 缓冲区就支持所有受兼容的字符集,无需为显示某类字符专门选择对应的语言。但选择 language environment语言环境 对配置各类默认项至关重要,简单来说,语言环境选择的是 首选文字体系 ,而非具体的语言。
语言环境会控制 Emacs 读取文本时识别的编码体系(参见「识别编码体系」),该规则适用于文件、接收的邮件及所有读入 Emacs 的文本,也可指定新建文件时使用的默认编码体系。每种语言环境还会对应一个默认的输入方法。
选择语言环境
可通过自定义变量 current-language-environment ,或使用命令 M-x set-language-environment 选择语言环境。执行该命令时当前处于哪个缓冲区不产生影响,因为其效果会全局作用于整个 Emacs 会话。受支持的语言环境列表可查看变量 language-info-alist ;若需了解某一语言环境 lang-env 的详细信息,可使用命令 C-h L lang-env RET (describe-language-environment) 。
Emacs 支持的语言环境包括:
ASCII, Arabic, Belarusian, Bengali, Brazilian Portuguese, Bulgarian, Burmese, Cham, Chinese-BIG5, Chinese-CNS, Chinese-EUC-TW, Chinese-GB, Chinese-GB18030, Chinese-GBK, Croatian, Cyrillic-ALT, Cyrillic-ISO, Cyrillic-KOI8, Czech, Devanagari, Dutch, English, Esperanto, Ethiopic, French, Georgian, German, Greek, Gujarati, Hebrew, IPA, Italian, Japanese, Kannada, Khmer, Korean, Lao, Latin-1, Latin-2, Latin-3, Latin-4, Latin-5, Latin-6, Latin-7, Latin-8, Latin-9, Latvian, Lithuanian, Malayalam, Oriya, Persian, Polish, Punjabi, Romanian, Russian, Sinhala, Slovak, Slovenian, Spanish, Swedish, TaiViet, Tajik, Tamil, Telugu, Thai, Tibetan, Turkish, UTF-8, Ukrainian, Vietnamese, Welsh, and Windows-1255.
ASCII、阿拉伯语、白俄罗斯语、孟加拉语、巴西葡萄牙语、保加利亚语、缅甸语、占语、中文 - 大五码、中文 - 万国码、中文 - 欧盟扩展码繁体、中文 - 国标码、中文 - 国标 18030、中文 - 国标扩展码、克罗地亚语、西里尔文 - 替代码、西里尔文 - 国际标准码、西里尔文 - KOI8 码、捷克语、天城文、荷兰语、英语、世界语、埃塞俄比亚语、法语、格鲁吉亚语、德语、希腊语、古吉拉特语、希伯来语、国际音标、意大利语、日语、卡纳达语、高棉语、韩语、老挝语、拉丁语 - 1、拉丁语 - 2、拉丁语 - 3、拉丁语 - 4、拉丁语 - 5、拉丁语 - 6、拉丁语 - 7、拉丁语 - 8、拉丁语 - 9、拉脱维亚语、立陶宛语、马拉雅拉姆语、奥里亚语、波斯语、波兰语、旁遮普语、罗马尼亚语、俄语、僧伽罗语、斯洛伐克语、斯洛文尼亚语、西班牙语、瑞典语、泰越语、塔吉克语、泰米尔语、泰卢固语、泰语、藏语、土耳其语、通用多八位编码字符集、乌克兰语、越南语、威尔士语、视窗 1255 编码。
在图形化显示界面中,要显示所选语言环境对应的文字体系,需配备适配的字体,字体的配置细节参见「字体集」章节。
与系统区域设置的关联
部分操作系统可通过设置环境变量 LC_ALL 、 LC_CTYPE 或 LANG 指定使用的字符集区域设置(若多个变量均被设置,以首个非空变量的配置为准)。Emacs 启动时,会在系统区域设置别名表中查找当前字符集区域设置的名称,将其标准名称与变量 locale-charset-language-names 和 locale-language-names 的配置项匹配(前者优先级高于后者);若匹配成功,会自动选择对应的语言环境,同时还会根据该区域设置,按需调整显示表、终端编码体系、区域设置编码体系、首选编码体系, 以及键盘发送的非 ASCII 字符的解析方式 (这一点尤为重要)。
若在 Emacs 运行过程中修改了 LC_ALL 、 LC_CTYPE 或 LANG 环境变量(可通过 M-x setenv 操作),可执行 set-locale-environment 命令,根据新的区域设置重新调整语言环境。
set-locale-environment 函数通常会使用语言环境指定的首选编码体系解析系统消息;但如果当前区域设置与变量 locale-preferred-coding-systems 中的配置项匹配,Emacs 会改用对应的编码体系。例如,若区域设置 ja_JP.PCK 与 locale-preferred-coding-systems 中的 japanese-shift-jis 匹配,即便默认编码为 UTF-8,Emacs 也会使用该编码。
你可通过显式执行 set-language-environment 命令,或在初始化文件中自定义 current-language-environment ,覆盖 Emacs 启动时自动选择的语言环境。
查看语言环境信息
要查看某一语言环境 lang-env 的生效配置,使用命令 C-h L = lang-env= RET (describe-language-environment) 即可。该命令会说明该语言环境适用的语言,列出对应的字符集、编码体系和输入方法,还会展示示例文本以演示其支持的文字体系;若输入空值作为 lang-env ,则会描述当前已选中的语言环境。
自定义语言环境
可通过常规钩子 set-language-environment-hook 自定义任意语言环境, set-language-environment 命令在完成新语言环境的配置后,会执行该钩子。钩子函数可通过检查变量 current-language-environment 判断当前的具体语言环境,针对特定语言环境的非默认配置(如键盘输入和终端输出的编码体系、默认输入方法等),都应在该钩子中设置。
set-language-environment 命令在开始配置新语言环境前,会先执行钩子 exit-language-environment-hook ,该钩子可用于撤销通过 set-language-environment-hook 完成的自定义配置。例如,若你通过 set-language-environment-hook 为某一特定语言环境设置了特殊的按键绑定,就需要在 exit-language-environment-hook 中恢复该按键的默认绑定。
24.3. 输入方法
input method 输入方法 是专为交互式输入设计的一类字符转换方式。本节介绍 Emacs 自带的输入方法;若需了解底层操作系统提供的原生输入方法,参见「单字节编辑模式」章节。
在 Emacs 中,通常一种语言对应一种专属输入方法;部分使用相同字符的语言可共用一个输入方法,也有少数语言支持多种输入方法。
输入方法的核心实现方式
- 简单映射:将 ASCII 字母映射为另一套字母体系,替代 ASCII 字符的输入,希腊语、俄语输入方法均采用此方式。
- 字符合成:将多个字符的序列转换为单个字符,是更常用的高级方式。许多欧洲语言的输入方法通过该方式,将「字母 + 重音符号」(或反之)的序列转换为单个带重音的非 ASCII 字母(例如部分方法可将
o ^合成为单个带抑扬符的 o)。这类输入方法无专属命令,仅完成可打印字符序列的合成。 - 映射 + 合成:音节文字体系的输入方法通常采用此组合方式,泰语、韩语输入方法均是如此。先将按键映射为特定发音或声调符号,再将构成完整音节的符号序列映射为单个音节字符。
中日文专属输入方法
中文输入方法
中文输入方法需先输入汉字的拼音(如 chinese-py 拼音输入法),或字符的拆分部件(如 chinese-4corner 四角码、 chinese-sw 等)。一条输入序列通常对应多个候选汉字,需通过 C-f 、 C-b 、 C-n 、 C-p (或方向键)及数字键选择目标字符,这类按键在候选选择阶段会被赋予特殊含义。
候选汉字在逻辑上分为多行展示,每行最多显示 10 个候选。Emacs 通常在回显区一次仅显示一行,行首会标注 (i/j),代表当前为第 i 行,共 j 行:
- 按
C-n/C-p可上下切换候选行; - 按
C-f/C-b可在当前行中前后切换候选字符,选中的候选会以特殊颜色高亮,按C-SPC可选定该候选并插入到缓冲区; - 候选字符旁会标注数字序号,直接按对应数字键可快速选定当前行的该候选。
在中文输入方法中按 TAB 键,会弹出一个独立缓冲区展示所有候选字符,点击 mouse-2 鼠标中键 可选定对应候选;此时 C-f、C-b、C-n、C-p 及数字键仍可正常使用,只是高亮效果会在候选缓冲区中显示,而非回显区。
若需按拼音声调输入,可使用 chinese-sisheng 四声输入法,该方法基于字符合成实现,例如输入 pi1 可得到带一声调的「pī」。
日文输入方法
日文输入方法需先通过拼音输入完整的单词,待单词字符进入缓冲区后,Emacs 会调用大型词典将其转换为一个或多个日文汉字 / 假名。一个拼音拼写通常对应多个日文单词,按 C-n / C-p 可循环切换候选词完成选择。
终止字符合成的方法
有时需要终止输入方法的字符合成处理,避免已输入的字符与后续字符组合(例如在 latin-1-postfix 输入方法中, o ^ 会自动合成带重音的 'o' ,若需单独输入 'o' 和 '^' ,可通过以下方式实现):
- 重复输入符号:将重音符号输入两次,是输入独立字母和符号的专属技巧,例如输入
o ^ ^可得到两个独立字符o^; - 插入无关字符再删除:在字母后输入一个无法与其合成的字符,再立即删除,例如输入
o o DEL ^可得到独立的o和^; - 通用快捷键:在两个字符之间按
C-\ C-\,可强制终止合成,该方式通用性最强,只是操作稍繁琐;该操作是连续两次执行C-\(toggle-input-method)。详见《选择输入方法》章节。 - 增量搜索中使用:
C-\ C-\在增量搜索中尤为实用,可终止输入方法的等待合成状态,直接基于已输入的字符开始搜索。
输入方法的辅助功能
- 查询字符输入方式:按
C-u C-x =,可查看当前输入方法下,光标所在位置字符的具体输入方式(参见「光标位置信息」章节)。 - 输入过程可视化配置:两个变量可控制输入方法的提示方式:
input-method-highlight-flag:非空时,缓冲区中会高亮显示未完成的合成字符序列(多数输入方法支持,少数会禁用此功能);input-method-verbose-flag:非空时,回显区会显示下一个可输入的候选字符列表(迷你缓冲区中除外)。
- 自定义输入方法:可将自定义函数添加到钩子变量
quail-activate-hook,修改输入方法的工作方式(参见「钩子」章节)。例如通过quail-translation-keymap函数获取输入方法的键映射表,再使用define-key重新定义部分按键的绑定(参见「在初始化文件中重新绑定按键」章节)。 - 只读缓冲区的输入方法抑制:当缓冲区文本因某种原因设为只读时,输入方法会自动禁用。此举可确保在
read-only-mode只读模式、image-mode 图片模式等将缓冲区(或部分区域)设为只读的模式中,单字符按键绑定能正常生效,即便输入方法处于激活状态。
其他字符输入方式
- 按 Unicode 输入任意字符:若需输入键盘上无对应按键的字符,可使用
C-x 8 RET(insert-char) ,通过字符的 Unicode 名称或编码点插入单个字符(参见「插入文本」章节)。 - Emoji 专属输入命令:Emacs 为 Emoji 字符提供了专属输入快捷键,所有相关命令均在
C-x 8 e键映射表中:C-x 8 e e(emoji-insert) :可浏览不同分类的 Emoji,选择后插入;C-x 8 e l(emoji-list) :弹出新缓冲区列出所有 Emoji,点击或按回车可将选中的 Emoji 插入当前缓冲区;C-x 8 e s(emoji-search) :根据名称搜索 Emoji 并插入。😘
- Emoji 信息查询:
describe-char命令可显示光标所在位置字符 / 字形的详细信息(包括 Emoji);若仅需快速查询字符名称,可使用C-x 8 e d(emoji-describe) 命令,该命令主要用于区分外观相似的 Emoji 变体,也可查询非 Emoji 字符的名称。
24.4. 选择输入方法
C-\- 启用或关闭已选中的输入方法 (
toggle-input-method) 。 C-x RET C-\ 输入法名 RET- 为当前缓冲区选择新的输入方法 (
set-input-method) 。 C-x \ 输入法名 RET- 临时启用选定的瞬时输入方法;插入单个字符后,该输入法会自动关闭 (
activate-transient-input-method) 。 C-h I 输入法名 RETC-h C-\ 输入法名 RET- 描述指定的输入方法 (
describe-input-method) 。默认情况下,该命令会描述当前启用的输入方法(若存在),描述内容会详细说明该输入法的完整使用方法。 M-x list-input-methods- 列出所有受支持的输入方法。
为当前缓冲区选择输入方法可使用 C-x RET C-\ (set-input-method) ,该命令会从迷你缓冲区读取输入法名称,名称通常以其适用的语言环境开头。变量 current-input-method 会记录当前选中的输入方法。
输入方法会通过各类 ASCII 字符序列来表示非 ASCII 字符,有时需要临时关闭输入方法,按下 C-\ (toggle-input-method) 即可;再次按下该快捷键,可重新启用输入方法。
若 首次 按下 C-\ 时尚未选择任何输入方法,Emacs 会提示你指定一个,该操作与使用 C-x RET C-\ 指定输入法的效果一致。
带数字参数执行该命令时(如 C-u C-\ ), toggle-input-method 总会提示你选择输入方法,并将最近一次选中的输入法作为默认选项推荐。
选择语言环境时,会为各缓冲区指定一个默认输入方法。若存在默认输入方法,按下 C-\ 即可在当前缓冲区启用它。变量 default-input-method 用于指定默认输入方法(值为 nil 表示无默认输入法)。
部分语言环境支持多种输入方法,若你想使用与 set-language-environment 命令默认选择不同的输入法,可通过 set-language-environment-hook 钩子让 Emacs 为特定语言环境选择自定义的默认输入方法(参见set-language-environment-hook相关说明)。示例如下:
(defun my-chinese-setup () "Set up my private Chinese environment." (if (equal current-language-environment "Chinese-GB") (setq default-input-method "chinese-tonepy"))) (add-hook 'set-language-environment-hook 'my-chinese-setup)
上述代码的作用是:当你选择「Chinese-GB(中文 - 国标码)」语言环境时,将默认输入方法设为带声调的拼音输入法 chinese-tonepy 。
你也可让 Emacs 自动激活指定的输入方法,示例如下:
(add-hook 'text-mode-hook (lambda () (set-input-method "german-prefix")))
该代码会在文本模式(Text mode)下,自动激活德语前缀输入法german-prefix。
部分字母文字的输入方法,本质是通过重新映射键盘,模拟该文字体系常用的各类键盘布局。键盘的正确映射方式取决于你的实际键盘布局,可使用命令 M-x quail-set-keyboard-layout 指定当前的键盘布局。
使用 M-x quail-show-key 命令,可查看在选定的键盘布局下,输入光标后该字符所需按下的按键(或按键序列)。 C-u C-x = 命令除了显示该字符的其他相关信息外,也会展示这一输入方式。
M-x list-input-methods 会列出所有受支持的输入方法,该列表会展示每种输入法的相关信息,包括其在 mode line模式行 中对应的显示标识。
有时需要临时启用输入方法仅插入单个字符,这种场景下使用瞬时输入方法会更为便捷。按下 C-x \ (activate-transient-input-method) 可临时启用输入方法,按该输入法规则插入单个字符后,输入法会自动关闭。若尚未选定瞬时输入方法, C-x \ 会提示你指定一个;后续再次执行该命令,会直接启用已选定的瞬时输入方法。若要更换瞬时输入方法,可按下 C-u C-x \ ,你选定的瞬时输入方法可与通过 C-u C-\ 选择的常规输入方法不同。
24.5. 编码系统
不同语言的使用者已制定出多种标准化程度各异的编码系统,用于表示各自的语言字符。Emacs 内部并不直接使用这些编码系统,而是在 读取数据 时将各类编码系统转换为自身的内部编码,在 写入数据 时再将内部编码转换为其他编码系统。该转换功能可应用于文件的读写、终端的数据收发,以及与子进程间的数据交换场景。
Emacs 为每种编码系统分配了专属名称。多数编码系统仅适用于单一语言,其名称以对应语言名开头;部分编码系统可支持多种语言,这类编码系统的名称通常以 'iso' 开头。此外还有一些特殊编码系统,例如 no-conversion (无转换)、 raw-text (原始文本)和 emacs-internal (Emacs 内部编码)。
有一类特殊的编码系统被统称为 代码页 (codepages),专为兼容微软视窗(MS-Windows)和微软磁盘操作系统(MS-DOS)软件的文本编码而设计。这类编码系统的名称格式为 cpnnnn ,其中 nnnn 为 3 位或 4 位的代码页编号。你可像使用其他编码系统一样使用这类编码,例如,要打开以代码页 850 编码的文件,可键入快捷键: C-x RET c cp850 RET C-x C-f 文件名 RET 。
编码系统除了能对非 ASCII 字符的各类表示形式进行转换外,还可执行 行尾转换 。Emacs 支持处理文件中行分隔符的三种主流规范:换行符(Newline,类 Unix 系统)、回车符后接换行符(Carriage Return + Linefeed,DOS 系统),以及仅使用回车符(Carriage Return,传统 Mac 系统)。
相关操作命令
C-h C 编码系统名 RET- 描述指定编码系统的详细信息 (
describe-coding-system) 。 C-h C RET- 描述当前正在使用的所有编码系统 (
describe-coding-system) 。 M-x list-coding-systems- 显示 Emacs 支持的所有编码系统列表。
命令 C-h C (describe-coding-system) 可展示特定编码系统的相关信息,包括该编码系统指定的行尾转换规则。执行该命令时,你可传入编码系统名作为参数;若留空参数,该命令会描述当前缓冲区及系统默认中,为各类用途选定的编码系统,同时还会展示编码系统的识别优先级列表(参见《编码系统的识别》章节)。
键入 M-x list-coding-systems 可查看 Emacs 支持的全部编码系统列表,该列表会展示每种编码系统的相关信息,包括其在模式行中对应的标识字符(参见《模式行》章节)。
此列表中的所有编码系统( 无转换编码no-conversion除外 ,该编码表示不执行任何形式的转换),均会明确可打印字符的转换方式与是否转换,但行尾转换的具体规则会根据每个文件的内容自动判定。例如,若检测到文件使用回车符 + 换行符作为行分隔符,Emacs 会自动采用 DOS 系统的行尾转换规则。
列表中的每种编码系统都包含三种 变体 ,用于明确指定行尾转换的具体方式:
…-unix- 不执行任何行尾转换,假定文件使用换行符作为行分隔符(该规范为类 Unix 系统、GNU 系统及 macOS 系统的默认规范)。
…-dos- 假定文件使用回车符 + 换行符作为行分隔符,并执行对应的行尾转换(该规范为微软系统的默认规范 9)。
…-mac- 假定文件仅使用回车符作为行分隔符,并执行对应的行尾转换(该规范为经典 Mac OS 系统的默认规范,目前仅在部分遗留软件中使用)。
为简化显示, list-coding-systems 命令的输出结果中会省略上述变体编码系统,因其命名规则完全可推导。例如,编码系统 iso-latin-1 包含 iso-latin-1-unix 、 iso-latin-1-dos 和 iso-latin-1-mac 三种变体。
编码系统unix、dos和mac分别是 undecided-unix 、 undecided-dos 和 undecided-mac 的别名。这类编码系统 仅指定行尾转换规则 ,字符编码的转换方式则由文本内容自行推导。
原始文本编码raw-text 适用于以 ASCII 文本为主、但可能包含值大于 127 的字节(且这些字节并非用于编码非 ASCII 字符)的文件。使用该编码时,Emacs 会原样复制这些字节值,并将当前缓冲区的变量 enable-multibyte-characters 设为 nil ,以保证这些字节被正确解析。 raw-text 会根据检测到的数据,以常规方式处理行尾转换,同时也包含上述三种标准变体,用于指定行尾转换的具体类型。
与之相对,无转换编码 no-conversion 表示不执行任何字符编码转换 —— 既不转换非 ASCII 字节值,也不执行行尾转换。该编码适用于读写二进制文件、tar 归档文件及其他需要逐字原样解析的文件,同时它也会将变量 enable-multibyte-characters 设为 nil 。
以 无任何转换 的方式编辑文件的最简方法,是使用 M-x find-file-literally 命令。该命令会采用no-conversion编码,同时还会禁用 Emacs 中其他可能在你查看文件前修改其内容的功能(参见《打开文件》章节)。
Emacs 内部编码 emacs-internal (与其等效的 utf-8-emacs 同理),表示文件中的非 ASCII 字符以 Emacs 的内部编码格式存储。该编码系统会根据检测到的数据处理行尾转换,同时也包含上述三种标准变体,用于指定行尾转换的具体类型。
24.6. 编码系统识别
Emacs 在读取任意文本内容时,都会尝试识别其应使用的编码系统,该过程适用于读取文件、子进程输出、X 窗口系统选择的文本等各类场景。 只要你设定好偏好配置 ,Emacs 大多时候能自动选中正确的编码系统。
部分编码系统可通过数据中出现的字节序列来识别或区分,但也有一些编码系统,即便在理论上也无法相互区分。例如,拉丁1(Latin-1)和拉丁2(Latin-2)编码就无法区分 —— 二者使用相同的字节值,仅代表的字符含义不同。
Emacs 通过 编码系统优先级列表 来处理上述情况。当你未指定文件的使用编码时,Emacs 读取文件会依次将数据与优先级列表中的编码系统进行匹配,从列表首位开始逐一向下检测,直至找到与数据匹配的编码系统,随后便会假定文件内容以该编码系统存储并完成转换。
编码系统的优先级列表由 选定的语言环境 决定(参见《语言环境》章节)。例如,若你使用法语,大概率希望 Emacs 优先使用拉丁 1 而非拉丁 2;若使用捷克语,则大概率希望优先使用拉丁 2。这也是指定语言环境的原因之一。
你也可通过 M-x prefer-coding-system 命令,对编码系统优先级列表进行精细调整。该命令会从迷你缓冲区读取编码系统名称,并将其添加至优先级列表的头部,使其优先级高于所有其他编码。多次执行该命令时,每次都会在列表头部新增一个编码系统。
若你使用的编码系统指定了行尾转换类型(如 iso-8859-1-dos ),其含义为:Emacs 会优先尝试识别 iso-8859-1 编码,且在识别出该编码时,使用 DOS 格式的行尾转换规则。
有时 文件名 也可指示文件应使用的编码系统,变量 file-coding-system-alist 便定义了这种对应关系。Emacs 提供了专用函数 modify-coding-system-alist ,用于向该列表中添加配置项。例如,要将所有 '.txt' 文件的读写编码设为 chinese-iso-8bit ,可执行以下 Lisp 表达式:
(modify-coding-system-alist 'file "\\.txt\\'" 'chinese-iso-8bit)
该函数的第一个参数固定为 file ,第二个参数为正则表达式,用于匹配该配置适用的文件,第三个参数则为这些文件要使用的编码系统。
Emacs 会根据 文件内容 识别应使用的行尾转换类型:若检测到文件仅使用回车符,或仅使用回车符加换行符的组合作为行分隔符,便会相应选择对应的行尾转换规则。你可将变量 inhibit-eol-conversion 设为非nil值,禁用行尾转换的自动识别功能。禁用后,DOS 格式的文件在缓冲区中会显示可见的 '^M' 字符;相较于模式行左侧边缘较为隐晦的 '(DOS)' 行尾类型标识(参见《行尾助记符》章节),部分用户更偏好这种显示方式。
默认情况下,编码系统的自动检测会 对转义序列敏感 。若 Emacs 检测到以转义字符开头的字符序列,且该序列符合 ISO-2022 编码的规范,便会使用某一种 ISO-2022 编码对文件进行解码。
但在某些场景下,你可能希望 原样读取 文件中的转义序列,此时可将变量 inhibit-iso-escape-detection 设为非nil值。开启该设置后,编码检测会忽略所有转义序列,且绝不会使用 ISO-2022 编码,最终所有转义序列都会在缓冲区中以可见形式显示。
inhibit-iso-escape-detection 的默认值为nil。建议你 不要永久修改 该变量,仅在执行特定操作时临时调整即可。原因在于,Emacs 发行版中的部分 Emacs Lisp 源文件,其非 ASCII 字符采用 iso-2022-7bit 编码存储;若禁用转义序列检测,打开这些文件时将无法正确解码。
变量 auto-coding-alist 和 auto-coding-regexp-alist 是指定编码系统的最高优先级方式:前者针对特定文件名模式,后者针对包含特定内容模式的文件。这两个变量的配置 甚至会覆盖文件自身 的
-*-coding:-*-
标签(参见《指定文件的编码系统》章节)。例如,Emacs 会将 auto-coding-alist 应用于 tar 归档文件,避免因归档内某个文件包含的 '-*-coding:-*-' 标签,导致 Emacs 误判并将该编码应用到整个归档文件上。
另一种指定编码系统的方式是使用变量 auto-coding-functions 。例如,Emacs 内置的其中一个自动编码函数可检测 XML 文件的编码。与上述两个变量不同,该变量 不会覆盖 任何 -*-coding:-*- 标签。
24.7. 指定文件的编码系统
若 Emacs 对文件编码识别有误,你可使用快捷键 C-x RET r (revert-buffer-with-coding-system) ,选用正确的编码系统重新读取该文件。该命令会提示你输入要使用的编码系统。若要查看 Emacs 实际用于解码该文件的编码系统,可查看 mode line模式行 左侧的编码系统助记字符(参见《模式行》章节),或键入 C-h C (describe-coding-system) 查询。
你可直接在文件内部为特定文件指定编码系统,方式有两种:
- 一是在文件开头使用 '
-*-…-*-' 格式的标识, - 二是在文件末尾添加本地变量列表(参见《文件中的本地变量》章节)。
具体操作是为名为 coding 的「variable」定义取值,实际上 Emacs 并不存在真正的 coding 变量,该方式并非设置变量,而是为当前文件指定对应的编码系统。例如,标识 -*-mode: C; coding: latin-1; -*- 既指定了使用 Latin-1 编码系统,也将文件的主模式设为 C 模式。当你在文件中显式指定编码后,该设置会覆盖 file-coding-system-alist 变量中的对应配置。
24.8. 为输出选择编码系统
Emacs 为某个缓冲区选定编码系统后,会将该编码系统存储在变量 buffer-file-coding-system 中。该编码系统会成为从当前缓冲区向文件写入内容相关操作的默认编码,如 save-buffer (保存缓冲区)和 write-region (写入区域)命令。你可通过 set-buffer-file-coding-system 命令,为该缓冲区后续的文件输出操作指定另一编码系统(参见《为文件文本指定编码系统》章节)。
你可在任意 Emacs 缓冲区中插入 Emacs 支持的所有字符,但多数编码系统仅能处理其中的一部分字符。因此,你插入的字符有可能无法用保存该缓冲区时使用的编码系统进行编码。例如,你打开了一个以 iso-8859-2 编码的波兰语文本文件,并在其中添加了一些俄语词汇,保存该缓冲区时,Emacs 将无法使用 buffer-file-coding-system 的当前值进行编码,因为你添加的字符无法被该编码系统解析。
出现上述情况时,Emacs 会尝试使用 优先级最高的编码系统 (由 M-x prefer-coding-system 或 M-x set-language-environment 命令设置)。若该编码系统能对缓冲区中的所有字符完成安全编码,Emacs 会采用此编码,并将其值存入 buffer-file-coding-system ;若不能,Emacs 会展示所有适用于编码该缓冲区内容的编码系统列表,让你从中选择其一。
若你在邮件中插入了无法被当前编码适配的字符,Emacs 的处理方式会略有不同。它会额外检查该优先级最高的编码系统是否推荐用于 MIME 邮件;若不推荐,Emacs 会将这一情况告知你,并提示你选择另一编码系统。这样做是为了避免你无意间发送了采用收件人邮件软件难以解码的编码方式的邮件。(若你在提示时输入对应编码系统名称,仍可使用该不适配的编码。)
当你发送邮件时(参见《发送邮件》章节),Emacs 会通过四种不同方式确定用于编码邮件文本的编码系统,优先级依次为:首先,若 buffer-file-coding-system 的缓冲区自有值非nil,则使用该值;其次,若 sendmail-coding-system 的值非nil,则使用该值;第三,使用 default-sendmail-coding-system 的值。若上述三个值均为 nil ,Emacs 会使用新文件的默认编码系统(即 buffer-file-coding-system 的默认值)对发出的邮件进行编码,而该默认编码系统由你选择的语言环境决定。
24.9. 为文件文本指定编码系统
当 Emacs 未能为文件内容自动选择正确的编码系统时,你可以使用以下命令手动指定:
C-x RET f 编码系统 RET- 使用指定的编码系统保存或重新读取当前缓冲区中的文件 (
set-buffer-file-coding-system) 。 C-x RET c coding RET- 为紧随其后执行的命令指定编码系统 (
universal-coding-system-argument) 。 C-x RET r coding RET- 使用指定的编码系统重新读取当前文件 (
revert-buffer-with-coding-system) 。 M-x recode-region RET 正确编码 RET 错误编码 RET- 转换缓冲区中指定区域的编码 —— 该区域此前以错误编码完成了解码,重新用正确编码对其解码。
命令 C-x RET f (set-buffer-file-coding-system) 用于为当前缓冲区设置 文件编码系统 (即保存或重新读取文件时使用的编码系统),你需要在迷你缓冲区中输入指定的编码系统名称。你也可以通过在模式行的编码系统标识处点击鼠标右键( mouse-3 )来调用该命令(参见《模式行》章节)。
若你指定的编码系统无法处理缓冲区中的所有字符,当你尝试保存缓冲区时,Emacs 会就这些无法处理的字符发出警告,并让你重新选择其他编码系统(参见《为输出选择编码系统》章节)。
你也可通过该命令为当前缓冲区的编码指定 行尾转换规则 (参见行尾转换)。例如,执行 C-x RET f dos RET ,Emacs 会将当前缓冲区的文本以 DOS 格式保存,行尾使用回车符加换行符的组合。
为文件指定编码系统的另一种方式,是在 打开文件时 进行设置。先执行命令 C-x RET c (universal-coding-system-argument) ,该命令会在迷你缓冲区中读取你输入的编码系统名称;退出迷你缓冲区后,这个指定的编码系统就会应用于紧随其后执行的那条命令。
例如,若紧随其后的命令是 C-x C-f (打开文件),Emacs 会使用该编码系统读取文件(并记录该编码系统,供后续保存文件时使用);若紧随其后的命令是 C-x C-w (另存为),则会使用该编码系统写入文件。以这种方式指定保存用的编码系统,而非通过 C-x RET f 命令设置时,即便缓冲区包含该编码系统无法处理的字符,Emacs 也不会发出警告。
受指定编码系统影响的其他文件操作命令包括 C-x i=(插入文件)、 =C-x C-v (重新访问文件),以及 C-x C-f 的跨窗口变体命令。 C-x RET c 命令也会作用于启动子进程的各类命令,包括 M-x shell (参见《从 Emacs 中运行 Shell 命令》章节)。若紧随其后的命令并不使用编码系统,那么 C-x RET c 命令最终将不会产生任何效果。
以 无任何转换 的方式打开文件的简便方法,是使用 M-x find-file-literally 命令(参见《打开文件》章节)。
变量 buffer-file-coding-system 的默认值,指定了创建新文件时使用的编码系统。该值适用于新建文件的场景,也适用于先创建缓冲区再将其保存为文件的场景。选择语言环境时,Emacs 通常会将该变量设为适合该语言环境的默认编码系统。
若你使用错误的编码系统打开了文件,可通过 C-x RET r (revert-buffer-with-coding-system) 命令修正。该命令会让你指定一个编码系统,并用其重新读取当前文件。
若某段文本已通过错误的编码系统插入到缓冲区中,你可使用 M-x recode-region 命令重新解码。该命令会先提示你输入正确的编码系统,再提示你输入此前实际使用的错误编码系统,随后完成编码转换 —— 它会先使用错误编码将该区域的内容编码,再使用正确编码对其重新解码。
24.10. 进程间通信的编码系统
本节介绍如何为与其他进程的通信操作指定编码系统。
C-x RET x coding RET- 使用指定编码系统在 Emacs 与其他图形化应用之间传输选中的文本 (
set-selection-coding-system) 。 C-x RET X coding RET- 使用指定编码系统传输下一次选中的文本,实现与其他图形化应用间的单向或双向传输 (
set-next-selection-coding-system) 。 C-x RET p input-coding RET out-coding RET- 为当前缓冲区中与子进程的输入、输出操作分别指定输入编码和输出编码 (
set-buffer-process-coding-system) 。
命令 C-x RET x (set-selection-coding-system) 用于指定编码系统,该编码将应用于 Emacs 向其他窗口应用发送选中文本 ,以及 从其他应用接收选中的文本 这两种场景。该命令的设置会作用于后续所有的文本选中传输操作,直至再次执行该命令覆盖原有设置。命令 C-x RET X (set-next-selection-coding-system) 则仅为 Emacs 中接下来的一次选中文本操作 ,或 Emacs 读取的下一次外部选中文本操作 指定编码系统。
变量 x-select-request-type 用于指定从 X 窗口系统中接收其他应用选中文本时,请求获取的数据类型。若该变量值为 nil (默认值),Emacs 会按顺序尝试 UTF8_STRING 和 COMPOUND_TEXT 两种类型,并通过多种启发式规则从两个返回结果中选择更合适的一种;若这两种类型均尝试失败,Emacs 会回退使用 STRING 类型。若 x-select-request-type 的值为 COMPOUND_TEXT 、 UTF8_STRING 、 STRING 或 TEXT 中的任一符号,Emacs 将仅使用该指定的请求类型。若该变量值为上述部分符号组成的列表,Emacs 会按列表顺序依次尝试其中的请求类型,直至某一类型尝试成功,或遍历完整个列表为止。
命令 C-x RET p (set-buffer-process-coding-system) 为与子进程的输入、输出操作指定编码系统,该命令的设置仅作用于当 前缓冲区 。通常每个子进程都有其对应的专属缓冲区,因此可在对应子进程的缓冲区中执行该命令,为与该特定子进程的双向数据转换指定编码系统。
你也可在执行启动子进程的命令前,先执行 C-x RET c (universal-coding-system-argument) 命令,为与该子进程的通信操作指定编码系统,具体用法参见《为文件文本指定编码系统》章节。
与子进程间输入、输出数据转换使用的默认编码系统,由 当前的语言环境 决定。
变量 locale-coding-system 用于指定编码系统,该编码将应用于系统字符串的编码和解码操作,例如系统错误信息、 format-time-string 函数的格式串及时间戳的编解码。在 X 窗口系统中,该编码系统也可能被用于解码非 ASCII 的键盘输入;在批处理模式下,该编码还会用于对发送至标准输出流和标准错误流的文本进行编码。你应选择与 底层系统的文本表示方式 相兼容的编码系统,而系统的文本表示方式通常由环境变量 LC_ALL 、 LC_CTYPE 和 LANG 中的一个决定(按上述排列顺序,第一个值非空的环境变量将作为文本表示方式的判定依据)。
24.11. 文件名的编码系统
C-x RET F coding RET- 使用指定的编码系统对文件名进行编码和解码 (
set-file-name-coding-system) 。
命令 C-x RET F (set-file-name-coding-system) 用于指定专门对文件名进行编码的编码系统,该设置对文件内容的读写操作无任何影响。
实际上,该命令的作用仅为修改变量 file-name-coding-system 的值。若将该变量设为某一编码系统名称(可为 Lisp 符号或字符串格式),Emacs 会在所有文件操作中,使用该编码系统对文件名进行编码。这一设置让文件名中可以包含非 ASCII 字符 —— 至少支持该指定编码系统能编码的所有非 ASCII 字符。
若 file-name-coding-system 的值为 nil ,Emacs 会使用由选定语言环境决定的默认编码系统,该默认编码存储在变量 default-file-name-coding-system 中(默认值通常为 UTF-8 )。
当 Emacs 运行在基于 NT 内核的微软视窗系统版本中(包括 Windows 2000、XP 及所有后续版本), file-name-coding-system 的值基本会被忽略,因为 Emacs 默认会调用相关应用程序接口(API),直接传递 Unicode 格式的文件名。与之相反,在 Windows 9X 系统中,文件名会通过 file-name-coding-system 进行编码,该变量应被设为与当前系统区域设置对应的代码页(参见代码页相关内容)。变量 w32-unicode-filenames 用于控制 Emacs 调用接收文件名的系统函数时,是否使用 Unicode 应用程序接口:Emacs 的启动代码会在 Windows 9X 系统中将该变量设为 nil ,在更高版本的微软视窗系统中则设为 t 。
警告 :若在 Emacs 会话过程中修改 file-name-coding-system 的值(或更改语言环境),而你此前已打开过部分文件,这些文件的名称是通过旧编码系统编码的,且在新编码系统下无法编码(或编码方式不同),就可能引发问题。若你尝试以原文件名保存这类缓冲区的内容,可能会使用错误的文件名,或直接触发保存错误。若出现此类问题,可使用 C-x C-w 命令为该缓冲区指定新的文件名。
若对文件名进行编码时发生错误,可使用 M-x recode-file-name 命令修改文件名的编码系统。执行该命令时,Emacs 会依次提示你输入目标现有文件名、该文件名原有的编码系统,以及你希望转换到的目标编码系统。
24.12. X 键盘输入的编码系统
X 窗口系统下的输入法会指定专属的编码系统,这类编码系统是解码键盘输入所必需的。默认情况下,Emacs 在与输入法服务器建立连接后,会自动为每种输入法确定对应的解码编码系统,并使用该专属编码系统对键盘输入进行解码。但这种自动判定操作有时可能失败,出现该情况时,Emacs 会改用区域设置编码系统进行解码(参见《进程间通信的编码系统》章节)。
若输入法未正确声明其用于编码文本的编码系统,就需要手动指定 Emacs 对该输入法的输入文本进行解码时所用的编码系统。将变量 x-input-coding-system 的值设为某一编码系统符号后,Emacs 会无条件使用该编码系统,对所有来自输入法的键盘输入进行解码。
24.13. 终端 I/O 的编码系统
C-x RET t coding RET- 使用指定编码系统处理终端输出 (
set-terminal-coding-system) 。 C-x RET k coding RET- 使用指定编码系统处理键盘输入 (
set-keyboard-coding-system) 。
命令 C-x RET t (set-terminal-coding-system) 用于指定 终端输出 的编码系统。若为终端输出指定了字符编码,所有输出到终端的字符都会转换为该编码系统格式。
该功能适用于专为支持特定语言或字符集设计的纯字符终端 —— 例如,支持某一种 ISO 拉丁字符集的欧洲终端。在使用多字节文本时,你需要指定终端编码系统,让 Emacs 知晓该终端实际能处理哪些字符。
默认情况下,终端输出不会进行任何编码转换,除非 Emacs 能从你的终端类型或区域设置中推导出合适的编码系统(参见《语言环境》章节)。
命令 C-x RET k (set-keyboard-coding-system) 或变量 keyboard-coding-system ,用于指定 键盘输入 的编码系统。对于部分会发送非 ASCII 图形字符的终端,键盘输入的字符编码转换功能十分实用 —— 例如,部分为 ISO Latin-1 字符集或其子集设计的终端。
默认情况下,键盘输入会根据系统的区域设置进行编码转换。若你的终端实际并不支持区域设置所对应的编码(例如,按下 M-i 时终端插入了非 ASCII 字符),你需要将 keyboard-coding-system 设为 nil 来关闭编码转换。你可在初始化文件中添加以下代码实现该设置:
(set-keyboard-coding-system nil)
在微软视窗系统中,设置 keyboard-coding-system 无任何效果;仅在老旧的 Windows 9X 系统中例外,此时该编码必须与微软视窗控制台的当前代码页匹配,而控制台代码页可通过调用 w32-set-console-codepage 函数修改。
为键盘输入设置编码系统转换,与使用输入法之间存在一定相似性:二者都会将一系列键盘输入转换为单个字符。但二者设计初衷不同, 输入法 是为了方便用户交互式操作,待转换的输入序列通常为 ASCII 可打印字符;而 编码系统 的转换对象,通常为非图形字符序列。
24.14. 字体集
一种字体通常仅为某一种字母体系或文字脚本定义字形。因此,要显示 Emacs 所支持的全范围文字脚本,就需要组合使用多种字体。在 Emacs 中,这样的字体组合被称为 字体集 。字体集由一系列字体规格定义而成,其中每一种字体规格被分配处理一个字符编码范围;对于其指定字体未覆盖的字符,该字体集会回退调用另一个字体集来处理。
每个字体集都和单种字体一样拥有专属名称。但不同的是,字体由系统存储,可用的字体名称也由系统定义,而 字体集是在 Emacs 内部自行定义 的。一旦完成某一字体集的定义,你便可在 Emacs 中通过指定其名称来使用它,所有可使用单种字体的场景均适用。当然,Emacs 字体集仅能使用你的系统所支持的字体。若部分字符在屏幕上显示为空白方框或十六进制编码,说明用于显示这些字符的字体集中,没有对应适配的字体。出现这种情况,或是字符虽能显示但显示效果不符合预期时,你可能需要安装额外的字体,或是修改该字体集,使其调用你系统中已安装的特定字体(详见下文)。你的操作系统可能会提供可选安装的字体,你也可以安装 GNU 国际字体包(GNU Intlfonts),该字体包包含了 Emacs 所支持的绝大多数文字脚本对应的字体 10。
Emacs 会自动创建三种字体集:standard fontset标准字体集、 startup启动字体集和 default默认字体集。 默认字体集 最有可能包含适配各类非 ASCII 字符的字体,它是另外两种字体集的默认回退字体集;当你设置的是默认字体而非字体集时,该字体集也会作为默认回退选项。但默认字体集并未指定字体族名称,因此若直接使用,显示效果可能会存在一定的随机性。你可在启动 Emacs 时使用 '-fn' 选项指定特定的字体集,例如:
emacs -fn fontset-standard
你也可通过 X 资源中的 'Font' 项指定字体集(参见《X 选项与资源》章节)。
若未指定要使用的字体集,Emacs 会使用一款 ASCII 字体,对于该字体未覆盖的字符,则会以 'fontset-default' (默认字体集)作为回退。 标准字体集 尽管名称如此,却仅在被显式调用时才会生效。
要查看特定字体集的相关信息,可使用 M-x describe-fontset 命令。该命令会提示你输入字体集名称,默认值为当前框架正在使用的字体集;执行后会展示该字体集中的所有字符子范围,以及分配给各范围的对应字体。若要查看在未指定字体集的情况下(常规启动的默认情况),Emacs 在当前会话中使用的字体信息,可在命令提示时输入 fontset-default 并回车,或直接回车,即可查看当前框架所用字体集的详情。
一个字体集并非必须为每一个字符编码都指定对应字体。若某字体集未为某个字符指定字体,或是其指定的字体在你的系统中不存在,该字符就无法被正确显示,取而代之的会是该字符的十六进制编码、细空格或空白方框(详见《无字形字符的显示处理》章节)。此外,即便字体集为某一字符范围指定了字体,你也可能对其视觉显示效果不满意。出现上述这些情况时,你可以对字体集进行修改,具体方法参见《修改字体集》章节。
24.15. 定义字体集
在 X 窗口系统中运行时,Emacs 会根据 standard-fontset-spec 的取值自动创建一个标准字体集,该字体集的名称为:
-*-fixed-medium-r-normal-*-16-*-*-*-*-*-fontset-standard
可简称为 'fontset-standard' 。
在 GNUstep 和 macOS 系统中,标准字体集由 ns-standard-fontset-spec 的取值生成;在微软视窗系统(MS Windows)中,标准字体集则由 w32-standard-fontset-spec 的取值生成。
标准字体集的粗体、斜体、粗斜体变体也会被自动创建,其名称会将原名称中的 'medium' 替换为 'bold' ,或将 'r' 替换为 'i' ,也可同时替换两者。
若你通过 'Font' 资源、 '-fn' 命令行参数指定了默认的 ASCII 字体,或 Emacs 启动时自动识别到了默认字体,程序会基于该字体自动生成一个字体集,即 启动字体集 ,名称为 fontset-startup 。Emacs 生成该字体集的规则为:将字体名称中的 charset_registry (字符集注册名)字段替换为 'fontset' , charset_encoding (字符集编码名)字段替换为 'startup' ,再将生成的字符串作为字体集的指定标识。
例如,若你通过以下形式指定字体启动 Emacs:
emacs -fn "*courier-medium-r-normal--14-140-*-iso8859-1"
Emacs 会生成下述字体集,并将其应用于 X 窗口系统的初始框架:
-*-courier-medium-r-normal-*-14-140-*-*-*-*-fontset-startup
对于该字体所支持的所有字符,启动字体集会直接使用该指定字体(或注册表、编码不同的变体);对于其他字符,则会回退至 'fontset-default' (默认字体集)进行匹配。
通过 X 资源 'Emacs.Font' ,你可像指定实际字体名一样指定字体集名称。但注意 不要 在 'Emacs*Font' 这类通配符资源中指定字体集名称 —— 该通配符会匹配菜单等其他各类资源,而菜单组件无法识别并处理字体集。相关细节可参考《X 窗口系统的选项与资源》章节。
你可通过命名为 'Fontset-n' 的 X 资源指定额外的字体集(其中n为从 0 开始的整数),该资源的取值需遵循以下格式:
fontpattern, [charset:font]… 字体模式, [字符集:字体]…
其中, fontpattern字体模式 需符合标准 X 窗口系统的字体名格式(可参考前文启动字体集的示例),但最后两个字段除外,这两个字段需设为 'fontset-alias' 的形式。
每个字体集均有两个名称:长名称和短名称。长名称即上述的 字体模式 ,短名称为长名称的最后两个字段,即 'fontset-alias'(例如启动时自动创建的字体集,其短名称为 'fontset-startup' )。你可通过任意一个名称引用该字体集。
'charset:font' 格式用于指定(当前字体集中)某一特定字符集所使用的字体。其中, charset 为字符集名称, font 为该字符集对应的字体。在定义单个字体集时,可根据需要多次使用该格式。
对于其余未单独指定的字符集,Emacs 会依据字体模式( fontpattern )自动匹配字体:将模式中的 'fontset-alias' 替换为对应字符集的描述值。例如针对 ASCII 字符的字体, 'fontset-alias' 会被替换为 'ISO8859-1' 。
此外,当字体模式中出现多个连续的通配符时,Emacs 会将其合并为单个通配符,这一设计是为了避免使用 auto-scaled fonts自动缩放的字体。通过放大原有字体得到的缩放字体并不适用于文本编辑,而缩小字体也无实际意义 —— 直接使用小字体的原始尺寸效果会更好,Emacs 也会按此方式处理。
因此,若 fontpatter 字体模式为下述形式:
-*-fixed-medium-r-normal-*-24-*-*-*-*-*-fontset-24
则 ASCII 字符对应的字体指定规则为:
-*-fixed-medium-r-normal-*-24-*-ISO8859-1
而中文 GB2312 字符对应的字体指定规则为:
-*-fixed-medium-r-normal-*-24-*-gb2312*-*
你的系统中可能没有匹配上述字体指定规则的中文字体。大多数 X 窗口系统发行版所包含的中文字体,其字体族字段均为「宋体(song ti)」或「仿宋(fangsong ti)」。这种情况下,可按如下方式指定「Fontset-n」:
Emacs.Fontset-0: -*-fixed-medium-r-normal-*-24-*-*-*-*-*-fontset-24,\
chinese-gb2312:-*-*-medium-r-normal-*-24-*-gb2312*-*
配置后,除中文 GB2312 字符外,所有字符对应的字体指定规则中,字体族字段均为「fixed」;而中文 GB2312 字符的字体指定规则中,字体族字段为通配符 '*' ,可自动匹配系统中的中文字体。
Emacs 中负责解析字体集资源配置值并创建字体集的函数为 create-fontset-from-fontset-spec ,你也可以显式调用该函数手动创建字体集。
关于字体命名的更多相关信息,可参阅《字体》章节。
24.16. 修改字体集
字体集并非总需要从头创建。若仅需小幅修改,直接编辑现有字体集通常更简便,最常用的是 'fontset-default' (默认字体集)。修改 'fontset-default' 还会影响所有将其作为回退字体集的其他字体集,因此这是解决 Emacs 为特定文字体系选择字体时各类问题的有效方法。
可通过 set-fontset-font 函数修改字体集,该函数需指定 要修改字体的字符、字符集、文字体系或字符范围 ,以及 待使用的字体规格 。以下为具体示例:
;; 为汉字优先使用大五码(Big5)字体 (set-fontset-font "fontset-default" 'han (font-spec :registry "big5") nil 'prepend) ;; 为表情符号文字体系使用“Noto Color Emoji”字体(默认配置) (set-fontset-font "fontset-default" 'emoji '("Noto Color Emoji" . "iso10646-1") nil 'prepend) ;; 使用彩色字体显示心形字符 (set-fontset-font "fontset-default" #x2764 "Noto Color Emoji") ;; 为Unicode私有使用区使用自定义私有字体MyPrivateFont (set-fontset-font "fontset-default" '(#xe000 . #xf8ff) "MyPrivateFont") ;; 为拉丁-3字符集使用Liberation Mono字体 (set-fontset-font "fontset-default" 'iso-8859-3 "Liberation Mono") ;; 在启动字体集(fontset-startup)中,将DejaVu Sans Mono设为回退字体, ;; 优先于默认字体集(fontset-default)调用 (set-fontset-font "fontset-startup" nil "DejaVu Sans Mono" nil 'append)
修改 symbol script符号文字体系 的字体集时,变量 use-default-font-for-symbols 的取值会决定该 字体集 是否实际生效。
关于 set-fontset-font 函数的更多详细用法,可参考《GNU Emacs Lisp 参考手册》中的字体集章节。
若你不清楚某个字符的编码点或其所属的文字体系,可让 Emacs 直接查询:将光标移至该字符处,按下 C-u C-x = (what-cursor-position) ,相关信息及更多细节会在 Emacs 弹出的 *Help* 帮助缓冲区中显示,具体可参考光标位置信息章节。
例如,日文字符归属于kana(假名)文字体系,但日文文本中常会混合汉字,因此可通过为han(汉字)文字体系配置字体,让 Emacs 使用Kochi Gothic(高知哥特体)显示日文:
(set-fontset-font "fontset-default" 'han "Kochi Gothic")
(为方便使用,Emacs 中的 'han' 文字体系并非仅支持中文字符,而是覆盖了中日韩( CJK )所有统一字符。)
Emacs 支持的所有 script 文字体系 列表,可查看变量 script-representative-chars 的取值。
上述这类字体集配置 仅对默认字体不支持的字符生效 :例如若 'Kochi Gothic' 字体包含拉丁字符,Emacs 也不会用它显示拉丁文字体系 —— 因为 Emacs 默认字体通常已支持基础拉丁字符集。
你系统中安装的部分字体可能存在损坏,或显示对应字符时效果不佳,此时可让 Emacs 在搜索合适的显示字体时,完全忽略这些问题字体。具体方法为:将问题字体名称添加至列表型变量 face-ignored-fonts 的取值中,可在 '~/.emacs' 配置文件中添加如下示例代码:
(add-to-list 'face-ignored-fonts "Some Bad Font")
24.17. 无法显示的字符
你的终端可能无法显示部分非 ASCII 字符。大多数文本终端仅支持一种字符集(可通过变量 default-terminal-coding-system 告知 Emacs 当前终端使用的字符集,详见《终端输入输出的编码体系》章节); 无法用该编码体系编码的字符,默认会显示为问号 '?' 。
图形化显示界面能支持更广泛的字符显示,但如果你的系统未安装对应字符的字体,这些字符会以 空心方块 的形式呈现。
若你需要使用 Latin-1 字符,而终端不支持 Latin-1 字符的显示,可将其替换为助记 ASCII 序列来展示,例如用 ""o" 表示带分音符的字母 ö 。加载 iso-ascii 库即可实现该功能。
若你的终端支持 Latin-1 字符显示,还可通过 Latin-1 等效字符与ASCII 助记序列结合的方式,显示其他欧洲字符集的字符。自定义变量 latin1-display 并启用该功能即可,其中所用的 ASCII 助记序列,大多与前缀输入法的助记序列一致。
24.18. 单字节编辑模式
ISO 8859 系列拉丁字符集将八进制 0240 至 0377(十进制 160 至 255)的字符编码范围,用于表示欧洲各类语言(及部分非欧洲语言)所需的带重音字母和标点符号。需要注意的是,即便在单字节缓冲区中(即禁用多字节字符的情况下),Emacs 仍会将此编码范围内的字节视为 原始字节 ,而非字符;但 Emacs 仍可将这些字符编码临时视作某一单字节字符集的内容来处理。若要指定使用的字符集,可调用 M-x set-language-environment 命令,并选择合适的语言环境(如 'Latin-n' )。相关细节可参考《GNU Emacs Lisp 参考手册》中的「禁用多字节字符」章节。
只要当前使用的终端或字体支持,Emacs 也能将十进制 160 至 255 范围内的字节显示为可识别的字符,该功能会自动生效。在图形化显示界面中,Emacs 还可通过 字体集 显示单字节字符,其实际原理是根据当前语言环境,将单字节字符转换为等效的多字节字符后再展示。若要启用该功能,只需将变量 unibyte-display-via-language-environment 设置为非nil值即可。请注意,该设置 仅影响此类字节的显示方式 ,并不会改变 Emacs 将其视作原始字节而非字符的核心处理逻辑。
若你的终端不支持 Latin-1 字符集的显示,Emacs 可将这些字符转换为 ASCII 序列展示,至少能让你清晰识别字符本身。加载 iso-ascii 库即可实现该功能。理论上可为其他 ISO 8859 拉丁字符集实现类似的库,但目前暂未开发。
默认情况下,非 ISO 8859 字符(十进制 128 至 159 范围内的编码)会以 八进制转义序列 的形式显示。若需为非标准的 ISO 8859 字符集扩展版本修改此显示方式,可使用 disp-table 库中的 standard-display-8bit 函数。
输入单字节非 ASCII 字符有两种方法:
- 使用当前选定语言环境对应的输入法(详见「输入法」章节)。在单字节缓冲区中使用输入法时,通过输入法输入的非 ASCII 字符会被自动转换为单字节格式。
若你的键盘可生成表示非 ASCII 字符的、十进制 128 及以上的字符编码,可直接敲击对应按键输入该编码。
在图形化显示界面中,使用上述按键无需进行特殊设置,可直接生效;而在文本终端中,需调用
M-x set-keyboard-coding-system命令,或自定义变量keyboard-coding-system,以此指定键盘所使用的编码体系(详见「终端输入输出的编码体系」章节)。启用该功能后,你可能需要通过ESC键来输入元字符(Meta);但在控制台终端或xterm等终端模拟器中,你可将元字符映射为ESC键,同时仍能直接通过键盘、组合键(Compose)或右Alt键(AltGr)输入 8 位字符,相关细节可参考「用户输入的类型」章节。许多现代系统为各类无对应键盘按键的语言,提供了 原生输入法 支持。若 Emacs 在编译时启用了原生输入法支持,你即可激活此类输入法并输入其支持的字符。原生输入法的激活与使用方式因系统和输入法类型而异,本文不再赘述,可参考对应的系统文档。本节仅介绍 Emacs 中用于控制原生输入法使用的相关功能。
在基于 GTK 工具包编译的 Emacs 中,变量
x-gtk-use-native-input用于控制 Emacs 是否接收 GTK 输入法生成的字符。该变量默认值为nil,此时 Emacs 使用 X 输入法(XIM);若设为非nil值,则使用 GTK 输入法。X 资源中的useXIM用于控制是否启用 XIM,inputStyle则用于控制原生输入法在 X 界面中生成的预览文本的显示方式,相关细节可参考「Emacs 的 X 资源列表」。在微软视窗(MS-Windows)系统中,Emacs 支持由输入法管理器(IMM)提供的原生输入法,若有需要也可将其关闭,相关细节可参考「微软视窗系统中的键盘使用方法」。
你可将组合键
C-x 8用作 合成字符前缀 ,来输入非 ASCII 的 Latin-1字符及其他可打印字符。C-x 8可在迷你缓冲区、普通缓冲区中用于字符插入,也可在搜索过程中及所有允许输入按键序列的场景中使用。C-x 8的功能通过加载iso-transl库实现。该库加载后,若键盘配有Alt修饰键,该键将与C-x 8实现相同的功能:按住Alt键并敲击重音字符,即可为后续输入的字母添加对应重音。此外,iso-transl库加载后,若键盘带有 Latin-1 虚重音字符键,这些按键也会被定义为合成键,可与后续输入的字符组合生成带重音的字符。按下
C-x 8 C-h可列出所有可用的C-x 8字符合成映射关系。你可通过
M-x iso-transl-set-language命令,为特定语言扩展C-x 8支持的字符合成映射集,目前该功能支持的语言包括:法语(French)、德语(German)、葡萄牙语(Portuguese)、西班牙语(Spanish)和世界语(Esperanto)。相关细节可参考变量iso-transl-language-alist。
24.19. 字符集
在 Emacs 中, charset 是 字符集(character set)的缩写。Emacs 除了支持自身定义的若干字符集(如 emacs、unicode-bmp、eight-bit)外,还兼容绝大多数主流字符集(如 ascii、iso-8859-1、cp1250、big5、unicode)。所有受支持的字符,均归属于一个或多个字符集。
Emacs 通常会自动对字符集做最优处理,无需用户手动干预;但了解字符集的一些底层细节,有时能为使用带来帮助。
一个典型应用场景是字体选择(详见「字体」章节):每种语言环境(详见「语言环境」章节)都会为各类字符集定义一个 优先级列表 。Emacs 搜索字体时,会优先尝试找到能显示最高优先级字符集的字体。例如在日语语言环境中, 'japanese-jisx0208' 字符集拥有最高优先级,因此 Emacs 会尝试使用 注册表属性 为 'JISX0208.1983-0' 的字体。
有两个命令可用于查询字符集相关信息:
- 执行
M-x list-charset-chars,按提示输入字符集名称,即可显示该字符集中的所有字符; - 执行
M-x describe-character-set,按提示输入字符集名称,即可展示该字符集的详细信息,包括其在 Emacs 内部的表示方式。
执行 M-x list-character-sets 可显示 Emacs 支持的所有字符集列表,该列表会标注各字符集的名称及身份识别补充信息。如需了解更多背景,可参考由日本信息处理学会 / 日本信息技术标准委员会(IPSJ/ITSCJ)维护的《用于转义序列的 ISO 编码字符集国际注册表》(ISO-IR)。该列表中的字符集分为两类:常规字符集排在前列,后续为补充字符集。补充字符集指用于定义其他字符集(作为父集或子集),或为旧版 Emacs 提供向下兼容性的字符集。
若要查询缓冲区中某个字符所属的字符集,只需将光标移至该字符前,按下 C-u C-x = 即可(详见「国际字符集简介」章节)。
24.20. 双向编辑
Emacs 支持编辑阿拉伯语、波斯语、希伯来语等文字体系的文本,这类文字的横向自然显示顺序为 从右到左 ,但嵌入其中的数字和拉丁语文本仍按从左到右的方式显示。拉丁语文档中也常嵌入小段阿拉伯语或希伯来语文本(例如程序源文件中的注释和字符串),因此这类文字的文本实际为 双向文本 :同时包含从左到右和从右到左的字符段。
本节介绍 Emacs 为编辑双向文本提供的功能与配置项。
Emacs 以逻辑顺序(也叫阅读顺序)存储从右到左的文本和双向文本:即阅读时第一个字符在缓冲区或字符串中的位置,排在下一个字符之前。双向文本会在显示阶段重新排序为视觉顺序,这就导致字符在缓冲区中的位置,与其在屏幕上的显示位置不再保持单调递增的关系。Emacs 实现了《Unicode 标准附录 #9》中规定的Unicode 双向算法(UBA),用于双向文本的显示重排;仅在文本方向与段落基础方向相反时的折行显示上存在差异(例如从右到左的段落中出现长段英语文本)。
缓冲区局部变量 bidi-display-reordering 用于控制缓冲区中的文本是否在显示时重排。若其值为非nil,Emacs 会对显示的含从右到左方向属性的字符进行重排,该变量默认值为 t 。
双向文本的每个段落均可设置独立的 基础方向 ,可设为从右到左或从左到右。从左到右的段落,文本从窗口左边界开始显示,到达右边界时自动截断或折行;与之相反,从右到左的段落,文本从窗口右边界开始显示,到达左边界时截断或折行。默认情况下, 空行(即全部由空白字符组成的行) 为段落分隔符;若需修改,可自定义两个缓冲区局部变量 bidi-paragraph-start-re 和 bidi-paragraph-separate-re (详见「局部变量」章节),其值为正则表达式(字符串)—— 例如将两个变量均设为 "^" ,即可将单个换行符作为新段落的起始标识。
Emacs 会根据 段落开头的文本内容 ,动态判断每个段落的基础方向。但有时需要为缓冲区中的所有段落强制指定固定的基础方向:若变量 bidi-paragraph-direction 设为非nil,则会关闭基础方向的动态判断,转而强制缓冲区中所有段落使用该变量的缓冲区局部值指定的方向,其有效值为 right-to-left (从右到左)和 left-to-right (从左到右),其他值均视为 nil 。
此外,也可通过在段落开头 插入特殊格式字符 来控制段落的基础方向: 从右到左标记(RLM) 会强制后续段落为从右到左方向, 从左到右标记(LRM) 则强制为从左到右方向(可通过 C-x 8 RET 插入这类字符)。在图形界面会话中,LRM 和 RLM 字符会显示为极窄的空白字符;在文本终端中则显示为普通空白字符。
由于字符在显示时会被重排,那些按 逻辑顺序 或 缓冲区位置段 执行的 Emacs 命令,可能会产生特殊效果。例如 C-f (前进字符)和 C-b (后退字符)按逻辑顺序移动光标,因此当光标遍历经过重排的双向文本时,有时会出现跳跃;同理,若高亮区域覆盖的连续缓冲区字符段跨过重排文本,视觉上该区域可能显得不连续。这属于正常现象,与其他支持双向文本的程序表现一致。
绑定在方向键上的光标移动命令(如 LEFT 、 C-RIGHT ),会 适配当前段落的基础方向 。在从左到右的段落中,带或不带修饰键的右箭头命令,会沿缓冲区文本向前移动;而在从右到左的段落中,该操作会变为向后移动。这一设计契合从右到左段落的特点:屏幕上向左移动时,字符在缓冲区中的位置通常呈递增趋势。
当光标移出某一段落时,若前一段落或后一段落的基础方向与原段落不同,方向键的功能可能会发生变化,此时需要根据新的基础方向调整按下的方向键。
默认情况下, LEFT 、 RIGHT 方向键按 逻辑顺序 移动光标;若将变量 visual-order-cursor-movement 设为非nil,则这些命令会移动到当前屏幕位置 视觉上 左侧或右侧的字符,并在需要时跳转到下一行或上一行屏幕文本。请注意,受周边双向文本上下文影响,该操作可能会使光标在缓冲区中移动较长的距离。
双向文本有时会使用 特殊格式字符 来影响文本的显示重排,前文提到的 LRM 和 RLM 就是其中两种,这类字符还有其他类型。默认情况下,它们在图形界面帧中显示为窄空白符号,在文本模式帧中显示为普通空格。若希望能直观看到这些特殊控制字符,避免其对显示的影响超出预期,可开启 glyphless-display-mode (无字形显示模式,详见「文本的显示方式」章节)。开启该次要模式后,这些格式字符会显示为 小方框内的缩写形式 ,在屏幕上突出显示,便于理解其作用。
25. 主模式与次模式
Emacs 内置多种编辑模式,可通过实用的方式改变其基础操作行为,这些模式分为 major modes 主模式和 minor modes 次模式两类。
主模式为处理特定类型文件(如 C 语言源文件,参见「编辑程序」章节)或特定类型的非文件缓冲区(如 Shell 缓冲区,参见「在 Emacs 中执行 Shell 命令」章节)提供专属功能支持。主模式之间互斥,任意时刻每个缓冲区都有且仅有一个主模式。
次模式是可自由开启或关闭的可选功能,并非必须绑定于特定类型的文件或缓冲区。例如 Auto Fill mode 自动换行模式便是一种副模式,开启后输入空格时,会在单词之间自动换行(参见「自动换行模式」章节)。各副模式之间相互独立,且与当前选中的主模式也彼此独立。
25.1. 主模式
每个缓冲区都拥有一个主模式,该模式决定了此缓冲区为当前缓冲区时 Emacs 的编辑行为。 mode line模式行 通常会在 圆括号内显示当前主模式 的名称(参见「模式行」章节)。
功能最基础的主模式为 Fundamental mode 基本模式 。该模式无专属的模式重定义或变量设置,因此 Emacs 的每个命令都会以最通用的方式执行,所有用户选项变量也均处于默认状态。
编辑 Emacs 可识别的特定类型文本(如 Lisp 代码、英文文本)时,通常会使用更专用的主模式,例如 Lisp mode、Text mode。绝大多数主模式可分为三大类:
- 第一类是适用于普通文本的模式,包含纯文本和带标记的文本,涵盖 Text mode、HTML mode、SGML mode、TeX mode和 Outline mode;
- 第二类是针对特定编程语言的模式,包括 Lisp mode(含多个变体)、C mode、Fortran mode等;
- 第三类是与文件无直接关联的主模式,这类模式用于 Emacs 为特定用途创建的缓冲区,例如 Dired 功能创建的缓冲区所使用的 Dired mode (参见「目录编辑器 Dired」章节)、快捷键
C-x m在缓冲区所使用的Message mode (参见「发送邮件」章节)、用于与子 Shell 进程交互的缓冲区所使用的 Shell mode (参见「交互式子 Shell」章节)。
通常,当你首次打开文件或创建缓冲区时,Emacs 会自动设置主模式(参见「选择文件模式」章节)。你也可以通过 M-x 命令手动选择新的主模式:将模式名后加上 -mode ,即可得到对应模式的选择命令(例如,执行 M-x lisp-mode 即可进入 Lisp 模式)。由于每个缓冲区有且仅有一个主模式,因此不存在「关闭」主模式的操作,若需切换,只需更换为其他主模式即可。
缓冲区局部变量 major-mode 的取值为一个符号,该符号与对应主模式的命令名完全一致(例如 lisp-mode )。此变量由 Emacs 自动设置, 请勿手动修改 。
major-mode 的默认值决定了两类场景下使用的主模式:一是未指定主模式的文件,二是通过 C-x b 创建的新缓冲区。该变量的默认值通常为符号 fundamental-mode ,即对应基本模式。你可通过自定义界面修改此默认值(参见「简易自定义界面」章节),也可在初始化文件中添加如下语句实现修改(参见「Emacs 初始化文件」章节):
(setq-default major-mode 'text-mode)
若 major-mode 的默认值为 nil ,则新缓冲区会沿用前一个当前缓冲区的主模式。
专用主模式通常会重新定义部分按键的功能,使其更适配该模式的使用场景。例如,编程语言相关模式会将 TAB 键绑定为 按对应语言的语法规则缩进当前行的功能 (参见「缩进」章节)。常被重新定义的按键包括 TAB 、 DEL 和 C-j 。许多主模式还会自定义专属命令,这类命令通常绑定到以 C-c 为前缀的按键序列(参见「按键」章节)。主模式也可修改用户选项和变量,例如,编程语言模式通常会为变量 comment-start 设置缓冲区局部值,该变量用于定义源代码注释的分隔符规则(参见「处理注释」章节)。
若要查看当前主模式的说明文档(含其按键绑定列表),可按下 C-h m (describe-mode) ,相关内容亦可参见「其他帮助命令」章节。
除 Fundamental mode基本模式外,所有主模式都定义了 mode hook 模式钩子 —— 这是一个可自定义的 Lisp 函数列表,每当该主模式在某个缓冲区中启用时,列表内的函数都会依次执行。关于钩子的更多信息,参见「钩子」章节。各模式钩子均以对应主模式命名,例如 Fortran mode的钩子为 fortran-mode-hook 。此外,所有基于文本的主模式在执行自身模式钩子前,都会先执行 text-mode-hook ;多数编程语言模式11(包括 Emacs 自带的所有编程语言模式),则会先执行 prog-mode-hook ,再执行自身模式钩子。钩子函数可通过读取变量 major-mode 的值,判断当前实际进入的是哪一个主模式。
模式钩子常被用于启用次模式(参见「次模式」章节)。例如,你可在初始化文件中添加以下语句,实现「在所有基于文本的主模式中启用 Flyspell mode拼写检查次模式」(参见「检查并修正拼写」章节),以及「在 Emacs Lisp 模式中启用 EIDoc 文档实时提示次模式」(参见「编程语言文档查阅」章节):
(add-hook 'text-mode-hook 'flyspell-mode) (add-hook 'emacs-lisp-mode-hook 'eldoc-mode)
25.2. 次模式
minor mode 次模式是一类可选的编辑模式,可按照明确定义的方式改变 Emacs 的操作行为。与主模式不同,任意时刻均可启用 任意数量 的次模式。部分次模式为 buffer-local 缓冲区局部 模式,可在特定缓冲区中开启(启用)、在其他缓冲区中关闭(禁用);另一部分则为 global 全局 模式,一旦启用,会作用于 Emacs 会话中所有操作及全部缓冲区。多数次模式默认处于禁用状态,仅有少数次模式默认启用。
多数缓冲区局部次模式启用后,会在 mode line模式行的主模式标识后方显示自身状态。例如,模式行中的 'Fill' 表示 Auto Fill mode自动换行模式已启用,相关说明参见「模式行」章节。
与主模式相同,每个次模式都对应一个 mode command 模式命令 ,命令名为「模式名-mode」。例如,自动换行模式对应的命令为 auto-fill-mode 。但主模式命令仅用于启用对应模式,次模式命令则可实现启用与禁用的双向操作,规则如下:
- 无前缀参数直接调用次模式命令时(可通过
M-x执行,或为命令绑定按键后敲击对应按键,参见「自定义按键绑定」章节),会 切换 该次模式状态:若原本禁用则启用,若原本启用则禁用。 - 带前缀参数调用次模式命令时,若参数为 0 或负数,会 无条件禁用 该次模式;若参数为正数,则 无条件启用 。
- 在 Lisp 代码中调用次模式命令时,若省略参数或参数为nil,会 无条件启用 该次模式(此设计便于在主模式的模式钩子中启用次模式,参见「主模式」章节);若参数为非nil值,则按上述交互式前缀参数的规则处理。
多数次模式还配有与模式命令同名的 mode variable 模式变量 :变量值为非nil表示次模式已启用,为 nil 则表示禁用。 注意 :请勿在 Lisp 代码中通过直接修改模式变量的值来启用 / 禁用次模式,应调用对应的模式命令;而通过自定义界面(参见「简易自定义界面」章节)设置模式变量时,会自动触发对应模式命令,确保次模式被正确启用 / 禁用。
以下为常用的 buffer-local 缓冲区局部次模式列表:
- 缩写模式(Abbrev mode):根据预定义的缩写规则自动展开文本,参见「缩写」章节。
- 自动换行模式(Auto Fill mode):输入时自动插入换行符,防止行内容过长,参见「文本换行」章节。
- 自动保存模式(Auto Save mode):定期保存缓冲区内容,减少程序崩溃时的工作丢失,参见「自动保存:应对意外故障」章节。
- 智能引号模式(Electric Quote mode):自动转换引号样式,例如将输入的
`like this'自动转换为‘like this’;可自定义其生效的文本类型,也可在单个缓冲区中完全禁用,参见「引号」章节。 - 富文本模式(Enriched mode):支持编辑和保存带格式的文本,参见「富文本」章节。
- 拼写检查模式(Flyspell mode):自动高亮拼写错误的单词,参见「检查并修正拼写」章节。
- 语法高亮模式(Font-Lock mode):自动高亮程序中的各类文本单元,该模式默认全局启用,也可在单个缓冲区中禁用,参见「文本外观」章节。
- 行号显示模式(Display Line Numbers mode):是
display-line-numbers功能的便捷封装,根据display-line-numbers-type的值设置行号显示规则,参见「显示自定义」章节。 - 大纲次模式(Outline minor mode):提供与大纲主模式(Outline mode)相似的功能,参见「大纲模式」章节。
- 覆盖模式(Overwrite mode):输入普通打印字符时会替换原有文本,而非将原有文本后移。例如,光标位于 'FOOBAR' 中字母 'B' 前方时,开启该模式后输入 'G' 会得到 'FOOGAR',而非默认的 'FOOGBAR' 。该模式下,命令
C-q会插入后续任意字符(包括数字),可实现「插入字符而非替换原有字符」的操作。该模式的命令overwrite-mode绑定于Insert键。 - 二进制覆盖模式(Binary Overwrite mode):覆盖模式的变体,适用于编辑二进制文件;将换行符、制表符视作普通字符,可被其他字符替换,也可替换其他字符。该模式下,
C-q后的数字仍按常规表示八进制字符编码。 - 视觉行模式(Visual Line mode):按单词边界进行自动换行,使长行在单词之间折行显示,参见「视觉行模式」章节。
以下为实用的全局次模式列表:
- 列号显示模式(Column Number mode):在模式行中显示当前光标所在列号,参见「模式行」章节。
- 选中删除模式(Delete Selection mode):若选区处于激活状态,输入文本时会先删除选区内容,参见「选区操作」章节。
- 补全提示模式(Icomplete mode):在迷你缓冲区中进行补全操作时,实时显示可用的补全选项,参见「迷你缓冲区快速选择」章节。
- 行号模式(Line Number mode):在模式行中显示当前光标所在行号,默认启用,参见「模式行」章节。
- 菜单栏模式(Menu Bar mode):为每个框架添加菜单栏,默认启用,参见「菜单栏」章节。
- 滚动条模式(Scroll Bar mode):为每个窗口添加滚动条,默认启用,仅在图形化终端中显示滚动条,参见「滚动条」章节。
- 工具栏模式(Tool Bar mode):为每个框架添加工具栏,默认启用,仅在图形化终端中显示工具栏,参见「工具栏」章节。
- 窗口工具栏模式(Window Tool Bar mode):为每个窗口添加工具栏,参见「窗口工具栏」章节。
- 标签栏模式(Tab Bar mode):为每个框架添加标签栏,参见「标签栏」章节。
- 标签行模式(Tab Line mode):为每个窗口添加标签行,参见「窗口标签行」章节。
- 临时标记模式(Transient Mark mode):高亮显示选区,且当标记处于激活状态时,让 Emacs 的多数命令作用于选区,默认启用,参见「标记与选区」章节
25.3. 选择文件模式
访问文件时,Emacs 会自动为其选择一种 主要模式 。默认情况下,该选择基于文件名 —— 例如,后缀为 '.c' 的文件会默认使用 C mode 编辑;部分场景下,也会根据文件内的特殊文本选择主要模式,这类特殊文本也可用于启用 buffer-local 缓冲区本地的次要模式 。
Emacs 选择文件模式的具体流程如下:
第一步:检查文件本地模式变量
Emacs 会先检查文件中是否包含 file-local mode文件本地模式变量 (参见《文件中的本地变量》章节)。若存在指定了主要模式的文件本地变量,Emacs 会直接使用该模式,忽略其他所有判断条件。通过文件本地变量指定主要模式的方法有多种, 最简方式 是在文件的 第一个非空行 中,将模式名包裹在 '-*-' 之间,该行也可包含其他文本。例如:
; -*-Lisp-*-
上述代码会告知 Emacs 使用 Lisp 模式。注意此处的分号是为了让 Lisp 将该行识别为注释,也可等价写为:
; -*- mode: Lisp;-*-
也可通过 eval 规范 ,利用文件本地变量指定缓冲区本地的次要模式。例如,以下第一个非空行会将缓冲区设为 Lisp mode,并启用自动换行模式:
; -*- mode: Lisp; eval: (auto-fill-mode 1); -*-
注意:通常不建议以这种方式启用次要模式,因为多数次要模式代表用户的个性化偏好。若你希望为特定文件类型启用某一次要模式,更推荐通过 主要模式钩子 实现(参见《主要模式》章节)。
第二步:检查目录本地的自动模式关联表
Emacs 会检查文件后缀是否匹配任意目录本地的 auto-mode-alist 中的条目,该表通过 .dir-locals.el 工具实现(参见《按目录划分的本地变量》章节)。
第三步:检查文件首行的 #! 标记
若仍未确定主要模式,Emacs 会检查文件内容是否以 #! 开头。该标记表示此文件是可执行的 Shell 命令,系统会通过文件首行指定的解释器运行该文件(文件其余内容作为解释器的输入)。因此,Emacs 会尝试通过解释器名称选择模式。例如,首行为 '#!/usr/bin/perl' 的文件会以 Perl mode 打开。解释器程序名与主要模式的对应关系,由变量 interpreter-mode-alist 定义。
若文件首行以 #! 开头,通常无法在首行使用 '-*-' 标记(否则系统运行解释器时会出现解析错误)。
- 因此,这类文件中,Emacs 会同时在第一行和第二行查找 '-*-' 标记。
- 手册页(man page)也存在类似情况:其首行通常以魔术字符串
'\"开头,用于指定 troff 预处理器列表,这类文件同样会在第二行查找 '-*-' 标记。
第四步:通过 magic-mode-alist 匹配缓冲区首文本
Emacs 会基于 magic-mode-alist 变量,通过检查 缓冲区开头的文本内容 来判定主要模式。该变量的默认值为 nil (空列表),因此 Emacs 会跳过此步骤;你可在初始化文件中对其进行自定义配置(参见《Emacs 初始化文件》章节)。该变量的值需为若干指定格式的元素组成的列表。元素格式有两种:
正则表达式格式:
(regexp . mode-function)其中 regexp 为正则表达式(参见《正则表达式的语法》), mode-function 为主要模式命令。若文件开头的文本匹配该正则表达式,Emacs 会使用 mode-function 指定的主要模式。
函数匹配格式:
(match-function . mode-function)其中 match-function 为 Lisp 函数,Emacs 会在缓冲区开头调用该函数;若函数返回非 nil 的值,则使用 mode-function 设置主要模式。
第五步:根据文件名匹配自动模式关联表
若仍未找到合适的主要模式,Emacs 会最终通过 文件名 判断,文件名与主要模式的对应关系由变量 auto-mode-alist 控制。该变量的值为列表,每个元素有两种格式:
- 基础格式:
(regexp . mode-function) - 扩展格式:
(regexp mode-function flag)
例如,该列表中默认包含条目 ("\\.c\\'" . c-mode) ,用于为后缀为 .c 的文件选择 C mode 。(注意:Lisp 语法中,需用 '\\' 表示字符串中的 '\',而正则表达式中需用 '\' 取消 '.' 的特殊含义。)
若元素为扩展格式 (regexp mode-function flag) 且 flag 为非 nil,则 Emacs 调用 mode-function (若其非 nil)后,会 舍弃匹配正则表达式的文件后缀 ,并重新遍历列表查找新的匹配项。这种「递归剥离后缀」的机制,适用于多后缀文件—— 此类文件的「外层后缀」会掩盖实际指定模式的「内层后缀」,例如备份文件、后缀为 .gpg 的 GPG 加密文件均使用该特性。
在 GNU/Linux 等区分文件名大小写的系统中,Emacs 会先对 auto-mode-alist 进行区分大小写的查找;若查找失败,会再次进行不区分大小写的查找。若要关闭第二次查找,可将变量 auto-mode-case-fold 设为 nil。
在微软 Windows 等不区分文件名大小写的系统中,Emacs 仅对 auto-mode-alist 进行一次不区分大小写的查找。
第六步:通过 magic-fallback-mode-alist 表匹配
若以上所有步骤均未找到主要模式,Emacs 会将缓冲区开头的文本与变量 magic-fallback-mode-alist 进行匹配。
该变量的工作方式与 magic-mode-alist 一致,唯一区别是仅在 auto-mode-alist 匹配失败后才会被调用。其默认值包含用于识别图片文件、HTML/XML/SGML 文件、PostScript 文件和Unix 风格配置文件的规则。
主要模式的重映射
当 Emacs 找到某一主要模式后,会做最后一次检查:该模式是否被 major-mode-remap-alist 重映射。若存在重映射,则使用重映射后的模式。该机制适用于 同一种文件类型可对应多种主要模式 的场景,方便用户指定偏好的模式。注意:该重映射会影响上述所有方法找到的主要模式 —— 例如,文件首行指定的模式,未必是最终在访问该文件的缓冲区中启用的模式(该重映射也会影响 revert-buffer 命令,参见《恢复缓冲区》章节)。
若某一文件类型有多种可选模式,可通过自定义 major-mode-remap-alist 告知 Emacs 你的偏好。例如,在 ~/.emacs 初始化文件中添加以下代码(参见《Emacs 初始化文件》章节):
(add-to-list 'major-mode-remap-alist '(c-mode . c-ts-mode))
可强制 Emacs 在 auto-mode-alist 或文件本地变量指定 c-mode 时,调用 c-ts-mode 。反之,添加:
(add-to-list 'major-mode-remap-alist '(c-mode))
可强制 Emacs 永远不将 c-mode 重映射为其他模式。
major-mode-remap-alist 的默认值为 nil ,即无重映射。但加载部分 Lisp 包或功能时,可能会自动引入模式重映射(Emacs 会默认认为,加载这些包代表用户偏好使用替代模式)。因此,为了让 Emacs 行为可预测,建议始终自定义 major-mode-remap-alist 明确你的偏好—— 该变量会覆盖 Emacs 内部的所有重映射规则。
恢复默认主要模式: normal-mode 命令
若你手动修改了缓冲区的主要模式,可键入 M-x normal-mode ,让 Emacs 恢复为其自动选择的主要模式。 find-file 命令选择主要模式时,调用的正是该函数。
- 若缓冲区正在访问某一文件,该命令还会处理文件的 '-*-' 行和文件本地变量列表(若有);参考 文件本地变量
- 若缓冲区未访问任何文件,该命令仅处理 '-*-' 行和文件本地变量列表中的主要模式规范(若有)。
normal-mode 会考虑模式重映射 —— 若在 Emacs 选择缓冲区的主要模式后,你自定义了 Major-mode-remap-alist ,该命令可能会启用与 Emacs 初始选择不同的模式。
文件名变更时的模式自动切换
C-x C-w 和 set-visited-file-name 命令若修改了文件名,且新文件名对应某一主要模式,Emacs 会自动切换至该模式(参见《保存文件》章节);若缓冲区原本未访问任何文件, C-x C-s 命令也会触发此行为。例外情况:若缓冲区内容已通过特殊文本指定了主要模式,或当前为部分特殊主要模式(不允许模式变更),则不会触发自动切换。若要关闭该特性,可将变量 change-major-mode-with-file-name 设为 nil 。
26. 缩进
Indentation 缩进 指在文本行开头插入或调整 whitespace characters 空白字符 (空格和 / 或制表符)。本章介绍 Text mode及相关模式与编程语言模式通用的缩进命令和配置项。关于编程模式下的缩进相关额外说明,参见《程序缩进》章节。
实现缩进最简便的方式是按下 TAB 键。在大多数主要模式中,按下该键会执行 indent-for-tab-command 命令(在 C 语言及相关模式中, TAB 键执行 c-indent-line-or-region 命令,其行为与之类似,参见《C 语言缩进命令》章节)。
TAB- 以适配当前模式的方式插入空白字符,或为当前行执行缩进 (
indent-for-tab-command) 。若区域处于激活状态,则为区域内所有行执行缩进。
TAB 键的具体行为由当前主要模式决定:在文本模式及相关主要模式中, TAB 键通常会插入若干空格和制表符的组合,将光标移动至下一个制表位(参见《制表位》章节)。在此模式下,上一行首个非空白字符的位置会被视作一个额外的制表位,因此你可通过 TAB 键让光标与上一行对齐。若区域处于激活状态(参见《区域操作》章节), TAB 键会执行特殊行为:为区域内每一行缩进,使各行首个非空白字符与上一行对齐。
在编程模式中, TAB 键会根据前序代码的结构,为当前代码行执行符合语法逻辑的缩进。若区域处于激活状态,则为区域内所有行执行该缩进操作。若光标初始位置处于当前行的缩进空白段内,执行该命令后光标会被重新定位至该行首个非空白字符处。
若你仅需在缓冲区中插入一个制表符,可键入 C-q TAB (参见《插入文本》章节)。
26.1. 缩进命令
除 TAB 制表键 (indent-for-tab-command) 外,Emacs 还提供多种命令,以其他方式实现缩进操作。
C-M-o- 在光标位置拆分当前行 (
split-line) 。光标后的行内文本将成为新行,且缩进至光标所在的列位置。该命令会先将光标向前移动,跳过所有空格和制表符;拆分完成后,光标将停在插入的换行符之前。 M-m- 将光标移至当前行第一个非空白字符处 (
back-to-indentation) 。若当前行无任何非空白字符,则将光标移至行尾。 M-i- 在光标位置插入空白字符,直至下一个制表位 (
tab-to-tab-stop) ,详见制表位章节。 M-x indent-relative- 在光标位置插入空白字符,直至光标与上一行(实际为最后一个非空行)的第一个非空白字符对齐。若光标原本已在该位置右侧,则执行
tab-to-tab-stop命令;若带数字参数调用此命令,则不执行任何操作。 M-^合并上一行与当前行 (
delete-indentation) 。该命令会将当前行开头的所有缩进字符及行分隔符替换为单个空格,实现两行的整洁合并。特殊情况(适用于 Lisp 代码):若待合并的字符为连续的左右括号,或合并位置紧跟另一个换行符,则省略上述的单个空格。
若当前设置了填充前缀,当被删除的换行符后紧跟填充前缀时,
M-^会同时删除该填充前缀,详见填充前缀章节。带前缀参数调用此命令时,将合并当前行与下一行;若区域处于激活状态且未带前缀参数,则合并该区域内的所有行。
C-M-\缩进区域内的所有行,效果等同于在每行行首按下
TAB制表键 (indent-region) 。若带数字参数调用,将把区域内所有行统一缩进至该数字指定的列数。
C-x TAB缩进所有起始于该区域的行,受影响的行将作为一个整体进行刚性移动 (
indent-rigidly) 。无参数调用此命令时,将激活一个临时模式,可交互式调整受影响行的缩进:临时模式激活期间,按左方向键/右方向键可分别向左 / 向右缩进 1 个空格;也可按
S-LEFT/S-RIGTH,向左 / 向右缩进至下一个制表位(详见制表位章节)。按下其他任意按键,将退出该临时模式,且该按键会按常规功能执行。带前缀参数
n调用此命令时,将把目标行向前缩进n个空格(不激活临时模式);n为负值时则向后缩进,因此可使用一个绝对值较大的负值,移除区域内所有行的缩进,示例操作:C-u -999 C-x TAB
26.2. 制表位
Emacs 会定义特定的列号作为 tab stop 制表位 。在文本模式及相关模式下插入空白符时,制表键( TAB )会将光标停在这些位置(参见缩进相关说明), M-i 这类缩进命令也会遵循该规则(参见缩进命令相关说明)。制表位的位置由变量 tab-stop-list 控制,其默认值为 nil ,代表每 8 列设置一个制表位。该变量也可设为一个 从零开始计数 的列号列表(按升序排列),列表中的列号即为制表位的位置。Emacs 会取列表中最后一个元素与倒数第二个元素的差值,以此为间隔无限延伸该制表位列表。
无需直接自定义 tab-stop-list 变量,Emacs 提供了更便捷的制表位查看与设置方式:执行命令 M-x edit-tab-stops 。该命令会打开一个专用缓冲区,用于展示当前制表位的设置情况,缓冲区内容格式如下:
: : : : : : 0 1 2 3 4 0123456789012345678901234567890123456789012345678 To install changes, type C-c C-c
第一行的冒号(:)对应每个制表位的位置,下方两行的数字仅用于标示冒号所在的列数。若 tab-stop-list 为默认值 nil,该缓冲区初始状态下将不显示任何冒号。
你可在该缓冲区中编辑制表位:在需要设置制表位的列位置输入冒号即可,此缓冲区默认启用 Overwrite mode改写模式(参见次要模式)。需注意,Emacs 会以你手动设置的最后两个显式制表位的差值为步长,无限延伸后续的制表位。编辑完成后,按下 C-c C-c 即可使新的制表位设置生效。默认情况下,新的制表位设置会应用于所有缓冲区;但如果你已将 tab-stop-list 设为调用 M-x edit-tab-stops 时所在缓冲区的局部变量(参见局部变量),则新设置仅对该缓冲区生效。若要将制表位设置保存至后续的 Emacs 会话,可通过自定义界面(Customize)保存 tab-stop-list 的值(参见简易自定义界面)。
注意:本节所讲的制表位,与缓冲区中制表符(tab 字符)的显示方式无任何关联。制表符在显示时,始终会以空白字符的形式填充至 下一个显示制表位 ,具体规则参见文本的显示方式相关内容。
26.3. 制表符与空格的选择
默认情况下,缩进命令会插入(或删除) 最短的制表符与空格组合 ,使光标对齐至目标列。制表符在显示时会以一段空白的形式呈现,直至下一个显示制表位。默认配置下,每 tab-width 列会设置一个显示制表位(该变量默认值为 8),详见《文本的显示方式》。
若需要,可将所有缩进统一设置为仅使用空格实现:将 buffer-local 缓冲区局部 变量 indent-tabs-mode 设为 nil 即可。关于缓冲区局部变量的设置方法,详见《局部变量》章节。需注意,无论 indent-tabs-mode 的取值如何,按下 C-q TAB 始终会插入一个制表符。
将 indent-tabs-mode 设为 nil 的一个重要原因是:不同编辑器对制表符的显示规则不一致,即便是 Emacs 用户,也可能为 tab-width 配置不同的自定义值。仅使用空格进行缩进,可确保文件在任意编辑器中显示的缩进格式完全一致。如果仅关注文件在 Emacs 中的显示效果,也可通过 file-local variable文件局部变量 设置 tab-width 来解决该问题(详见《文件中的局部变量》)。
Emacs 也提供了制表符与空格的互转命令,且转换过程中 始终保留所有非空白文本的列位置不变 :
M-x tabify:扫描指定区域内的空格序列,若转换后不会改变缩进效果,会将连续至少两个空格的序列转换为制表符;M-x untabify:将指定区域内的所有制表符,转换为对应数量的空格。
26.4. 缩进的便捷功能
变量 tab-always-indent 可调整制表键 (indent-for-tab-command) 的行为。其默认值为 t ,此时制表键将遵循「缩进」章节中描述的默认缩进行为。若将该值改为符号 complete ,制表键会先尝试缩进当前行;若当前行已完成缩进,则会尝试补全光标处的文本(参见《符号名的补全》)。若该值设为 nil ,则仅当光标位于左边界或当前行的缩进区域内时,制表键才会缩进当前行;其余情况下,制表键将直接插入一个制表符。
当 tab-always-indent 设为 complete 时,制表键优先执行补全还是缩进,可通过变量 tab-first-completion 进一步自定义。例如,若将该变量设为 eol ,则仅当光标位于行尾时,制表键才会执行文本补全。更多细节可参见《Emacs Lisp 参考手册》中的「模式专属缩进」章节。
Electric Indent mode 是一个全局次要模式,开启后按下回车键( RET )会自动缩进新行,该模式默认启用。按下 M-x electric-indent-mode 可切换该全局模式的开关;若仅需在单个缓冲区中切换,可使用命令 M-x electric-indent-local-mode 。
26.5. 代码对齐
Alignment 对齐 是指调整区域内多行文本的空白符,使所有行中特定内容均从同一列开始的操作。该操作通常用于提升文本或代码的可读性,典型示例为对类 C 编程语言中的一系列赋值语句进行对齐: 原始代码:
int a = 1; short foo = 2; double blah = 4;
对齐后通常为:
int a = 1; short foo = 2; double blah = 4;
可使用命令 M-x align 对当前区域内的行进行对齐,该命令适配多种标记语言和编程语言的通用对齐模式,并将这些模式编码为一套 alignment rules 对齐规则 ,定义了不同上下文下各类文本的对齐方式。
用户选项 align-rules-list 指定了 M-x align 需参考的对齐规则,其值为一个描述对齐规则的元素列表。每个元素均为一个点对 (title . attributes) :其中 title 为符号类型的对齐规则名称, attributes 为规则属性列表,用于定义规则的生效条件,以及对行的拆分和对齐方式。每个规则属性也是一个点对 (attribute . value) ,唯一的必选 attribute 为 regexp , value 为一个正则表达式,通过子表达式匹配每行中需要 M-x align 增减空白符的位置(参见《正则表达式中的反斜杠》)。关于对齐规则属性的完整说明,可查看 align-rules-list 的文档字符串 (C-h v align-rules-list RET) 。该选项默认配置了一长串适配 Emacs 所支持的多种语言的对齐规则,且默认规则通过 modes 规则属性指定了 M-x align 的生效主模式。主模式也可通过将缓冲区局部变量 align-mode-rules-list 设为非空的对齐规则列表,来覆盖 align-rules-list 的配置;当 align-mode-rules-list 为非空值时, M-x align 将优先参考该变量,而非 align-rules-list 。
除对齐规则外, M-x align 还会使用另一类 exclusion rules 排除规则 ,用于指定区域内无需对齐、需保持原样的内容。用户选项 align-exclude-rules-list 定义了这些排除规则,其值同样为描述排除规则的点对列表,与 align-rules-list 格式一致。该选项默认包含了对 Lisp、C 等语言中 字符串常量 和 注释 的对齐排除规则。除 align-exclude-rules-list 中的默认排除规则外,主模式还可通过将 align-mode-exclude-rules-list 设为非空的规则列表,定义自定义排除规则;该变量对 align-exclude-rules-list 的覆盖方式,与 align-mode-rules-list 对 align-rules-list 的覆盖方式一致。
M-x align 会将目标区域拆分为多个 sections 分段 (通常为连续的非空行),并通过增减空白符,按照所有匹配的对齐规则对每个分段分别对齐。该命令会保证单个分段内的所有行对齐方式一致,但同一区域内的不同分段可能采用不同的对齐方式。用户选项 align-region-separate 指定了 M-x align 拆分区域为分段的方式,其值可为符号 entire 、 group ,或一个正则表达式:
- 若值为
entire,Emacs 会将整个区域作为一个分段进行对齐; - 若值为
group,Emacs 会将区域内每组连续的非空行分别作为独立分段; - 若值为正则表达式,
M-x align会扫描区域中匹配该表达式的内容,将其作为分段分隔符。
该选项默认设为一个正则表达式,可匹配 空行 ,以及仅包含空白符和单个大括号('{' 或 '}')的行。若正则表达式无法满足精准拆分的特殊场景,也可将 align-region-separate 设为一个函数,由该函数定义区域到对齐分段的拆分规则,更多细节可查看其文档字符串。特定的对齐规则还可通过指定 separate 规则属性,覆盖 align-region-separate 的取值,定义专属的分段分隔符。
若带前缀参数调用 M-x align (快捷键 C-u M-x align ),该命令会启用更多对齐规则 —— 这些规则通常实用,但有时可能会产生过度对齐的效果。例如,在 Lisp 缓冲区中有如下代码:
(set-face-attribute 'mode-line-inactive nil
:box nil
:background nil
:underline "black")
执行 C-u M-x align 后将得到:
(set-face-attribute 'mode-line-inactive nil
:box nil
:background nil
:underline "black")
多数情况下,应先直接调用 M-x align (不带前缀参数);若结果不符合预期,可通过 C-/ 撤销操作,再尝试带前缀参数的 C-u M-x align 。
可使用命令 M-x align-highlight-rule ,在当前区域中可视化特定对齐规则或排除规则的作用效果。该命令会提示你输入规则标题,并高亮显示区域内受该规则影响的内容:对对齐规则,会高亮 M-x align 将增减的空白符;对排除规则,会高亮 M-x align 将跳过对齐的内容。若要清除该命令的高亮效果,执行 M-x align-unhighlight-rule 即可。
命令 M-x align-current 与 M-x align 功能类似,区别在于:无论当前是否选中区域,该命令仅对 包含光标 的对齐分段生效,其会根据 align-region-separate 定义的分段分隔符,确定当前分段的边界。 M-x align-entire 是 M-x align 的另一个变体,该命令会忽略 align-region-separate 的配置,将整个区域作为单个对齐分段进行统一对齐。若将 align-region-separate 设为 entire , M-x align 的默认行为将与 M-x align-entire 一致。
以下示例可说明将整个区域作为单个分段对齐的效果,原始代码:
one = 1; foobarbaz = 2; spam = 3; emacs = 4;
若选中所有行并执行 M-x align ,结果为:
one = 1; foobarbaz = 2; spam = 3; emacs = 4;
而执行 M-x align-entire 时,所有行被作为单个分段对齐,所有等号 '=' 将出现在同一列:
one = 1; foobarbaz = 2; spam = 3; emacs = 4;
命令 M-x align-regexp 允许你通过 自定义临时对齐规则 对当前区域进行对齐,而非使用 align-rules-list 中的预定义规则。该命令会提示你输入一个正则表达式,并将该表达式作为临时对齐规则的 regexp 属性,用于区域对齐。默认情况下,该命令会调整匹配你所指定正则表达式 第一个子表达式 的空白符;若带前缀参数调用 M-x align-regexp ,其还会提示你指定待使用的子表达式、设置对齐的空白符填充量,以及是否对每行中所有匹配该正则表达式的内容重复应用该规则。关于正则表达式及其子表达式的更多信息,参见《正则表达式中的反斜杠》。
若用户选项 align-indent-before-aligning 为非空值,Emacs 会在执行 M-x align 对齐区域前,先对该区域进行缩进处理(参见《缩进》章节),该选项默认值为 nil 。
用户选项 align-to-tab-stop 用于指定对齐后的内容是否从制表位开始(参见《制表位》章节):
- 若值为
nil,M-x align仅使用满足对齐需求的最少空白符,忽略制表位; - 若值为非空符号,
M-x align会检查该符号的取值,若其值为非空,则按制表位进行对齐。
该选项默认设为 indent-tabs-mode ,因此在使用制表符进行缩进的缓冲区中,对齐操作会遵循制表位的配置(参见《制表符与空格》章节)。
用户选项 align-default-spacing 指定了 M-x align 及其相关命令在对齐时,为每行不同内容之间填充的默认空白符数量:
- 当
align-to-tab-stop为nil时,该值为填充的空格数; - 当
align-to-tab-stop为非空值时,该值为填充的制表位数量。
各对齐规则可通过 spacing 规则属性,覆盖 align-default-spacing 设置的默认值。
27. 自然语言相关命令
本章介绍 Emacs 中用于处理 text 文本 的命令,此处的文本指自然语言的字符序列(而非计算机编程语言的字符序列)。这类命令的执行逻辑会兼顾自然语言的句法与格式规范,包括与单词、句子、段落及大写字母相关的各类规则。Emacs 也提供 filling填充 相关命令,即重新调整段落的行排版,使各行长度大致均等。这些命令虽主要为文本编辑设计,在程序代码编辑中也同样常用。
Emacs 提供了多种适用于自然语言文本编辑的主模式:若编辑普通纯文本文件,可使用 Text mode文本模式 ,该模式会针对自然语言的句法规范,对 Emacs 做轻量定制; * Outline mode大纲模式* 则为编辑大纲结构的文本提供了专属操作命令,详见《大纲模式》章节。
Org mode 是大纲模式的拓展,可将 Emacs 变为功能完善的事务管理工具:你可通过该模式管理待办清单、记录笔记,并将笔记导出为多种格式。相关用法详见 Emacs 自带的《Org 信息手册》。
针对包含嵌入式命令的特殊文本,Emacs 也提供了对应的专用主模式,包括 TeX 与 LaTeX 模式(详见《TeX 模式》)、HTML 与 SGML 模式(详见《SGML 和 HTML 模式》)、XML 模式(详见 Emacs 自带的《nXML 模式信息手册》),以及 Groff 与 Nroff 模式(详见《Nroff 模式》)。
若需编辑由文本字符构成的 ASCII 绘图,可使用 Picture mode图片模式 —— 这是为编辑此类绘图专门设计的主模式,详见《编辑字符绘图》章节。
27.1. 单词操作
Emacs 定义了多个用于单词移动和单词操作的命令:
M-f- 向前移动一个单词 (
forward-word) 。(PS: 向前指光标右边) M-b- 向后移动一个单词 (
backward-word) 。 M-d- 删除至单词末尾 (
kill-word) 。 M-DEL- 删除至单词开头 (
backward-kill-word) 。 M-@- 将标记置于下一个单词的末尾 (
mark-word) 。 M-t- 交换两个单词的位置,或拖动一个单词至其他单词后方 (
transpose-words) 。
可以看到,上述快捷键与基于字符操作的 C-f 、 C-b 、 C-d=、=DEL 、 C-t 形成对应系列。 M-@ 与 C-@ 同源,而 C-@ 是 C-SPC 的别名。
M-f (forward-word) 和 M-b (backward-word) 命令分别实现单词级的向前、向后移动,这类基于 Meta 键的快捷键,与基于单个字符移动的 C-f 、 C-b 快捷键用法相仿,数字参数同样可作为重复执行的次数。带负参数的 M-f 会向后移动,带负参数的 M-b 则会向前移动。向前移动时,光标会停在单词最后一个字符 后方 ;向后移动时,光标会停在单词第一个字符 前方 。
M-d (kill-word) 命令会删除光标位置之后的单词内容。准确来说,该命令会删除从光标处到 M-f 命令目标位置之间的所有内容。因此,若光标处于单词中间, M-d 仅删除光标后的部分单词内容;若光标与下一个单词之间存在标点符号,标点会随单词一同被删除。(若你只想删除下一个单词、保留其前的标点,只需先按 M-f 移至单词末尾,再按 M-DEL 从后向前删除单词即可。) M-d 命令的参数用法与 M-f 完全一致。
M-DEL (backward-kill-word) 命令会删除光标位置之前的单词内容,删除范围为从光标处到 M-b 命令目标位置之间的所有内容。例如,若光标位于 “FOO, BAR” 中空格的后方,执行该命令会删除 “FOO, ” 这部分内容。若你只想删除 “FOO”、保留逗号和空格,可使用 M-b 搭配 M-d 完成,而非直接按 M-DEL 。
M-t (transpose-words) 命令会将光标前方(或包含光标)的单词,与后方的单词互换位置,单词之间的分隔符不会随单词移动。例如,“FOO, BAR” 执行该命令后会变为 “BAR, FOO”,而非 “BAR FOO,”。关于文本交换的更多用法,参见「文本交换」章节。
若要通过作用于选区的操作处理单词,可使用 M-@ (mark-word) 命令,该命令会将标记置于 M-f 命令的目标位置。关于此命令的更多信息,参见「标记文本对象的命令」章节。
单词命令对单词边界的识别规则,由 语法表 控制。例如,可将任意字符定义为单词分隔符。相关内容参见《Emacs Lisp 参考手册》中的「语法表」章节。
此外, M-= (count-words-region) 和 M-x count-words 命令可统计并显示选区或缓冲区中的单词数量,相关用法参见「光标位置信息」章节。
27.2. 句子操作
Emacs 中用于操作句子和段落的命令,与单词操作命令类似,大多绑定在 Meta 键上。
M-a- 回退至句子开头 (
backward-sentence) 。 M-e- 前移至句子结尾 (
forward-sentence) 。(PS: 前移指向光标右边) M-k- 向前删除至句子结尾 (
kill-sentence) 。(PS: 向前指向光标右边) C-x DEL- 向后删除至句子开头 (
backward-kill-sentence) 。
M-a (backward-sentence) 和 M-e (forward-sentence) 命令,分别跳转至当前句子的开头和结尾。其快捷键的设计参考了跳转行首、行尾的 C-a 和 C-e ,与后者不同的是,重复按下 M-a 或 M-e ,可连续在多个句子间向前 / 向后跳转。
向句子后方回退时,光标会停在句子第一个字符 前方 ;向句子前方移动时,光标会停在结束句子的标点 后方 。两种操作均不会跨过句子边界的空白字符。
如同行操作的 C-a 、 C-e 配有对应的删除命令 C-k , M-a 和 M-e 也有专属的删除命令: M-k (kill-sentence) 会删除从光标处到句子结尾的所有内容。带正数字参数 n 时,该命令会删除后续 n 个句子;带负数字参数 -n 时,则会向后删除至前 n 个句子的开头。
C-x DEL (backward-kill-sentence) 命令的作用为,向后删除从光标处到句子开头的所有内容。
句子相关命令的识别逻辑,遵循 美式打字的排版惯例 —— 句子结尾 需标注 两个空格 。即当句末出现英文句点 '.' 、'?' 或 '!' ,且其后紧跟行尾或两个空格时,判定为句子结束;符号与行尾 / 两个空格之间,允许出现任意数量的右括号 ')' 、右方括号 ']' 、单引号 ‘'’ 或双引号 ‘"’ 。此外,段落的开头和结尾,也同时视为句子的起止位置。遵循这一惯例十分实用,能让 Emacs 的句子命令准确区分 句末结束符 和 缩写词后的句点 。
若你习惯在句子之间只使用一个空格,可将变量 sentence-end-double-space 设为 nil ,让句子命令适配单个空格的判定规则。但该设置存在弊端:Emacs 将无法区分句末结束符和缩写词后的句点。为了实现便捷、准确的文本编辑,我们建议你遵循 句末双空格 的排版惯例。变量 sentence-end-double-space 的设置同样会影响文本填充功能(详见「显式填充命令」章节)。
变量 sentence-end 用于控制 Emacs 对 句尾 的识别规则。若该变量非nil,其值需为一个正则表达式,用于匹配句子的最后几个字符,以及句子后方的空白字符(详见「正则表达式语法」章节);若设为默认值 nil ,Emacs 会根据多种规则判定句尾,例如变量 sentence-end-double-space 的取值。
部分语言(如泰语)并不使用句点标识句子结束,此类场景下,可将变量 sentence-end-without-period 设为 t 。
尽管上述句子移动命令为适配自然语言设计,但 Emacs 的其他模式也可重新绑定这些命令,实现类似的功能(详见「按句子跳转」章节)。
27.3. 段落操作
Emacs 中用于段落操作的命令同样绑定在 Meta 键上。
M-{- 回退至前一个段落开头 (
backward-paragraph) 。 M-}- 前移至下一个段落结尾 (
forward-paragraph) 。 M-h- 将光标和标记定位至当前或下一个段落的首尾 (
mark-paragraph) 。
M-{ (backward-paragraph) 会跳转至当前段落或前一个段落的开头,具体取决于执行命令时光标的位置(段落的定义见下文)。 M-} (forward-paragraph) 则同理,跳转至当前段落或下一个段落的结尾。若某段落前方存在空行, M-{ 会直接跳转至该空行处。
若要对某一段落执行操作,可按下 M-h (mark-paragraph) ,将该段落设为选区。例如,按下 M-h 后再按 C-w ,即可删除光标所在或光标后方的整个段落。 M-h 会将光标置于原光标所在段落的开头,标记置于该段落的结尾;若光标处于段落之间(连续空行中或段落边界处), M-h 则会将下一个段落设为选区;若目标段落的首行前方存在空行,选区会包含其中一行空行。若选区已处于激活状态,执行该命令时仅会移动标记而保持光标位置不变,且后续每按一次 M-h ,标记会向后再推进一个段落。
段落的定义由 主模式 决定。在 Fundamental mode、Text 模式及相关衍生模式中,段落与相邻段落之间由一行或多行空行分隔 —— 空行指无任何字符的行,或仅包含空格、制表符和 / 或换页符的行。在编程语言模式中,段落的定义方式与之大致相同,因此即便程序代码中并无实际意义上的段落,你仍可使用各类段落操作命令。
请注意,在文本模式中,缩进行本身并不构成段落分隔。若你希望将缩进行作为段落的分隔标识,可改用 Paragraph-Indent Text mode 段落缩进文本模式 ,详见「文本模式」章节。
若你设置了 填充前缀 ,则所有未以该填充前缀开头的行,均会作为段落的分隔符,详见「文本填充」章节。
段落边界的精确判定规则,由变量 paragraph-separate 和 paragraph-start 控制。 paragraph-start 的取值为一个正则表达式,用于匹配作为段落开头或分隔段落的行(详见「正则表达式语法」章节); paragraph-separate 的取值同样为正则表达式,用于匹配仅作为段落分隔、不属于任何段落的行(例如空行)。作为新段落开头且隶属于该段落的行,仅需匹配 paragraph-start ,无需匹配 paragraph-separate 。例如在基本模式中, paragraph-start 的取值为"\f\\|[ \t]*$" , paragraph-separate 的取值为 "[ \t\f]*$"。
需注意, paragraph-start 和 paragraph-separate 的匹配对象为 左页边距处的文本 ,而非必定是行首的文本,因此这两个正则表达式不应使用 '^' 作为锚定符,以确保段落相关操作命令在由页边距设置形成的缩进文本区域内也能正常生效
27.4. 页面操作
部分文本文件中,内容会以 formfeed character 换页符 (ASCII code 12,也记作 'control-L' )分隔为不同页面,该字符在 Emacs 中会以转义序列 '^L' 显示(详见「文本的显示方式」章节)。按照惯例,此类文本文件打印到硬拷贝时,每个换页符都会强制触发分页。Emacs 中大部分命令会将换页符视作普通字符处理,因此可通过 C-q C-l 插入、按退格键 DEL 删除该字符。除此之外,Emacs 还提供了专门用于页面间跳转和页面操作的命令。
M-x what-page- 显示光标所在的页面编号,以及光标在该页面内的行号。
C-x [- 将光标移至上一个页面边界 (
backward-page) 。 C-x ]- 将光标移至下一个页面边界 (
forward-page) 。 C-x C-p- 将光标和标记定位至当前(或指定)页面的首尾 (
mark-page) 。 C-x l- 统计当前页面的行数 (
count-lines-page) 。
M-x what-page 命令会从文件开头开始统计页面编号,同时统计光标在当前页面内的行号,并将两个数值一同显示在回显区。
C-x [ (backward-page) 会将光标移至上一个页面分隔符的 后方 ;若光标原本就处于某一页面分隔符后方,该命令会跳过此分隔符,继续定位至前一个分隔符处。数字参数可作为该命令的重复执行次数。 C-x ] (forward-page) 则会将光标向前移至下一个页面分隔符的 后方 。
C-x C-p (mark-page) 会将光标置于当前页面的起始位置(即页面首部分隔符的后方),并将标记置于当前页面的结束位置(即页面尾部分隔符的后方)。
C-x C-p 搭配 C-w 是快速删除某一页面并将其移动至其他位置的便捷方式:先通过 C-x [ 和 C-x ] 跳转到其他页面分隔符处,再粘贴被删除的页面,所有页面会重新恢复正确的分隔格式。 C-x C-p 命令的选区仅包含页面尾部的分隔符,正是为了保证该操作能按预期生效。
为 C-x C-p 添加数字参数,可指定跳转至相对于当前页面的某一页面:0 代表当前页面,1 代表下一页,-1 代表上一页。
C-x l (count-lines-page) 可辅助拆分页面,该命令会在回显区显示当前页面的总行数,并按当前行将其拆分为「当前行之前的行数」和「当前行之后的行数」,显示格式如下:
Page has 96 (72+25) lines 页面共有 96 行(72+25)
需注意,显示的两个数字之和与总行数相差 1—— 若光标并非位于行首,此为正常现象。
变量 page-delimiter 用于控制页面的起始位置,其值为一个正则表达式,用于匹配作为页面分隔符的行首内容(详见「正则表达式语法」章节)。该变量的默认值为 "^\f" ,即匹配行首的换页符。
27.5. 引号处理
引号的常用输入方式有两种,一是打字机惯例,使用直单引号 ‘'like this'’ 或直双引号 ‘"like this"’ 标注;二是弯引号惯例,使用左右单弯引号 ‘like this’ 或左右双弯引号 “like this” 标注12。在文本文件中,打字机引号的优势是输入简单、兼容性强;弯引号则能避免歧义,视觉呈现也更美观。
Electric Quote mode 可简化弯引号的输入操作。开启该模式后,输入指定字符会自动转换为对应弯引号:输入 ` 转为左单弯引号 ‘ ,输入 ' 转为右单弯引号 ’ ,输入 `` 转为左双弯引号 “ ,输入 '' 转为右双弯引号 ” 。你可通过自定义变量 electric-quote-chars 修改上述默认转换规则,该变量为包含四个字符的列表,依次对应左单弯引号、右单弯引号、左双弯引号、右双弯引号,其默认值为 '(?‘ ?’ ?“ ?”) 。
Electric Quote mode 的生效范围可通过相关变量自定义:若变量 electric-quote-paragraph 设为非nil,模式在文本段落中生效;若 electric-quote-comment 设为非nil,模式在编程语言的注释中生效;若 electric-quote-string 设为非nil,模式在编程语言的字符串中生效。该模式的默认配置为: electric-quote-string 值为 nil ,其余两个变量值为 t 。
你也可将选项 electric-quote-replace-double 设为非nil,开启后输入直双引号 ",程序会根据上下文自动转换为对应双弯引号:在缓冲区开头、换行符后、空白字符后、左括号后或引号字符后输入 ",会转为左双弯引号 “;其他场景下输入则转为右双弯引号”。
Electric Quote mode 默认处于关闭状态。若需在单个缓冲区中切换该模式,可执行 M-x electric-quote-local-mode ;若需全局切换,执行 M-x electric-quote-mode 即可。若想临时禁用该模式输入单个字符,可按下 C-q ` 或 C-q ' 替代直接输入 ` 或 ' 。即便 Electric Quote mode 处于关闭或未生效状态,仍可通过快捷键插入弯引号:按 C-x 8 [ 插入左单弯引号 ‘,按 C-x 8 ] 插入右单弯引号 ’,按 C-x 8 { 插入左双弯引号 “,按 C-x 8 } 插入右双弯引号 ” (详见「文本插入」章节)。需注意,变量 electric-quote-chars 的设置不会影响上述快捷键,这些快捷键并非绑定在 electric-quote-mode 中,而是全局映射表( global-map )中的全局绑定。
27.6. 文本填充
Filling text 文本填充 指将文本拆分为符合指定行宽的若干行。Emacs 提供两种文本填充方式:开启自动填充模式后,通过自插入字符输入文本时,会自动完成填充;同时也提供了可手动调用的显式填充命令,供编辑文本时使用。
27.6.1. 自动填充模式
Auto Fill mode自动填充模式 是一种缓冲区局部次要模式(参见「次要模式」章节),开启后当行宽超出限值,且你输入空格(SPC)或回车键(RET)时,行会被自动折行。
M-x auto-fill-mode- 启用或关闭自动填充模式。
SPCRET- 在自动填充模式下,于合适位置折行。
执行命令 M-x auto-fill-mode 可切换当前缓冲区的自动填充模式。与其他所有次要模式一致,带正数字参数执行时会启用该模式,带负数字参数则关闭。若需在特定主模式中自动启用自动填充模式,可将 auto-fill-mode 添加至 模式钩子 中(参见「主模式」章节)。当自动填充模式启用时,模式行中会显示模式标识 'Fill' (参见「模式行」章节)。
当行宽超过设定的理想宽度时,自动填充模式会在合适位置自动折行,且该操作 仅在你输入空格或回车键时触发 。若你希望插入空格或换行但禁止折行,可分别输入 C-q SPC 或 C-q C-j ;此外,按下 C-o 插入换行时也不会触发折行。
自动填充模式的折行位置由行内字符类型决定:对于 ASCII、拉丁语系及其他大多数文字体系的字符,Emacs 会在空格处折行,以保证单词的完整性;而对于中日韩(CJK)文字体系,可在任意两个字符之间折行(若加载 kinsoku 库,Emacs 会遵循特殊规则,避免在特定的 CJK 字符组合之间折行)。
自动填充模式折行时,会尝试遵循 adaptive fill prefix自适应填充前缀 规则:若能从当前段落的第一行或第二行推导出填充前缀,该前缀会被插入新行(参见「自适应填充」章节);否则新行会被自动缩进,效果等同于在该行按下制表符( TAB )(参见「缩进」章节)。在编程语言模式中,若在注释中间折行,Emacs 会按需插入新的注释分隔符,完成注释的拆分。
自动填充模式 不会重新填充整个段落 ,它仅执行折行操作,不合并行。因此,在段落中间进行编辑后,可能会导致段落排版格式混乱,此时可调用 显式填充命令 重新整理(参见「显式填充命令」章节)。
另有一个相似功能为 Visual Line mode视觉行模式 ,可在显示时自动折行长行(参见「视觉行模式」章节)。
27.6.2. 显式填充命令
M-q- 填充当前段落 (
fill-paragraph) C-x f- 设置填充列 (
set-fill-column) M-x fill-region- 填充区域内的每个段落 (
fill-region) M-x fill-region-as-paragraph- 将整个区域视为单个段落进行填充
M-x center-line- 居中对齐行
M-q (fill-paragraph) 命令用于填充当前段落,会重新调整段落内的换行位置,删除段落中多余的空格和制表符,使所有行的宽度均不超过设定的最大限值。与自动填充模式一致,该命令及其他填充命令通常以空格为换行依据;而对于中日韩(CJK)字符,这类命令几乎可在任意两个字符间换行,且会遵循禁则排版规则(详见「自动填充模式」)。
默认情况下, M-q 作用于光标所在的段落;若光标处于段落之间,则作用于光标后方的段落;若区域处于激活状态,则会转而作用于区域内的文本。也可直接调用 M-x fill-region 命令,专门对区域内的文本进行填充。
M-q 和 fill-region 命令遵循 Emacs 通用的段落边界判定规则(详见「段落」)。若需要更精细的控制,可使用 M-x fill-region-as-paragraph 命令,该命令会将光标与标记之间的所有内容作为单个段落重新填充,同时删除区域内的所有空行,使原本分离的文本块合并为一个整体。
为 M-q 命令添加数字参数时,会在填充文本的同时进行对齐处理,即插入额外的空格,使右边缘与填充列精准对齐;若无需额外空格,直接执行无参数的 M-q 即可( fill-region 命令同理)。
文本填充的最大行宽由缓冲区局部变量 fill-column 定义,其默认值为 70(详见「局部变量」)。在当前缓冲区中设置 fill-column 最便捷的方式是使用 C-x f (set-fill-column) 命令:带数字参数执行时,将该数字设为新的填充列值;仅以 C-u 为参数执行时,将填充列设为光标当前的水平位置。需注意, fill-column 本质以列单位计量,其在图形界面中的实际显示位置取决于所使用的字体;尤其是使用变宽字体时,不同行的 fill-column 对应的水平位置会有所不同。
M-x center-line 命令可将当前行在填充列范围内居中对齐;带数字参数 n 执行时,会将连续 n 行分别居中,并将光标移至这些行之后。该快捷键由文本模式绑定,仅在文本模式及相关衍生模式中可用(详见「文本模式」)。
Emacs 默认将「句点后接两个空格」或「句点后接换行」视为句子结束;若句点后仅接一个空格,则判定为缩写,而非句子结束。因此,填充命令不会在「单空格接句点」的位置换行。若将变量 sentence-end-double-space 设为 nil ,填充命令会在「句点后接一个空格」的位置换行,且所有句点后均仅保留一个空格(该设置的其他影响及潜在问题详见「句子」)。
若变量 colon-double-space 设为非 nil 值,填充命令会在冒号后保留两个空格。
若需自定义禁止换行的额外场景,可对异常钩子变量 fill-nobreak-predicate 进行定制(详见「钩子」)。该钩子中的每个函数均为无参执行,执行时光标位于 Emacs 拟换行的位置;若某一函数返回非 nil 值,Emacs 则不会在该位置换行。可用于该钩子的函数包括: fill-single-word-nobreak-p (句子首个单词后、最后一个单词前禁止换行)、 fill-single-char-nobreak-p (空白字符后的单字母单词后禁止换行)、 fill-french-nobreak-p (左括号后、右括号 / 冒号 / 问号前禁止换行)、 fill-polish-nobreak-p (单字母单词后禁止换行,无论其前是否为空白字符)。
可通过「显示填充列指示符模式」,让 Emacs 在填充列位置显示标识线(详见 display-fill-column-indicator 相关说明)。
27.6.3. 填充前缀
fill prefix 填充前缀 功能可在对段落进行填充时,让每一行都以特定的字符串开头(例如一段空格,实现段落缩进效果)。你可以显式指定填充前缀;若未指定,Emacs 会尝试自动推导填充前缀(详见「自适应填充」)。
C-x .- 设置填充前缀 (
set-fill-prefix) M-q- 使用当前填充前缀填充段落 (
fill-paragraph) M-x fill-individual-paragraphs- 填充指定区域,将缩进量的每次变化均视为新段落的开始
M-x fill-nonuniform-paragraphs- 填充指定区域,仅将段落分隔行视为新段落的开始
为当前缓冲区设置填充前缀的操作方法:将光标移至以目标前缀开头的行,把点定位到该前缀的末尾,按下 C-x . (即 C-x 后接句点)即可。若要关闭填充前缀,只需将点定位到行首,执行 C-x . 来指定空前缀即可。
当填充前缀生效时,填充命令会在填充前先移除段落中每一行的填充前缀,填充完成后再为每一行重新插入该前缀。(段落首行的开头部分会保持原样,因为该位置的格式通常是有意设置的特殊样式。)自动填充模式在换行时,也会自动插入填充前缀(详见「自动填充模式」)。当你在行首使用 C-o 命令换行时,该命令也会在新建的行上插入填充前缀(详见「空行」)。反之, M-^ 命令在删除换行符后,若该位置存在填充前缀,会一并将其删除(详见「缩进」)。
举例来说,若填充列设为 40,且将填充前缀设为‘;; ’,那么对以下文本执行 M-q 命令:
;; This is an ;; example of a paragraph ;; inside a Lisp-style comment.
执行后会得到如下结果:
;; This is an example of a paragraph ;; inside a Lisp-style comment.
在 M-q 命令和各类段落相关命令中, 不以填充前缀开头的行都会被视为新段落的起始行 ,这一规则对悬挂式缩进的段落(除首行外,其余所有行均缩进)处理效果极佳。 空行 ,或是 移除填充前缀后出现缩进的行 ,也会被视作段落分隔行或新段落的起始行;如果你需要编写每行都带注释分隔符的多段注释,这一规则会恰好满足你的排版需求。
你可以使用 M-x fill-individual-paragraphs 命令,为每个段落自动设置填充前缀。该命令会将指定区域划分为多个段落,把 缩进量的每一次变化 都当作新段落的开始,再对每个分段分别进行填充。如此一来,同一个段落内的所有行都会拥有相同的缩进量,而该缩进量会成为这个段落的填充前缀。
M-x fill-nonuniform-paragraphs 是一个类似的填充命令,其段落划分方式有所不同:该命令 仅将段落分隔行 (由 paragraph-separate 变量定义)视作新段落的开始。这意味着同一个段落内的各行可能拥有不同的缩进量,此时命令会将 该段落所有行中最小的缩进量 作为填充前缀。对于「段落首行缩进量与其余行不同」的排版样式,该命令能实现理想的填充效果。
填充前缀的相关设置存储在变量 fill-prefix 中,其值为一个字符串;若未设置填充前缀,该变量值为 nil 。这是一个缓冲区局部变量,修改该变量仅会对当前缓冲区生效,同时你也可以修改其默认值(详见「局部变量」)。
除填充前缀外,还可通过 缩进文本属性 控制段落的缩进量,相关说明详见「富文本中的缩进」。
27.6.4. 自适应填充
在某些情况下,填充命令可自动为段落推导合适的填充前缀:行首的空白字符,或是特定的标点符号序列,会被应用到该段落的所有行中。
若某段落包含两行及以上内容,填充前缀将取自该段落的第二行;但 仅当该前缀同时出现在首行时 ,此规则才生效。
若段落仅有一行,填充命令也可能从这一行提取前缀,但其判定逻辑更为复杂 —— 针对这种情况,有三种符合常规的排版处理方式,命令需从中选择:
- 将首行的前缀应用到该段落的所有行;
- 后续行仅用空白字符缩进,使其对齐首行前缀后的文本内容, 不直接复制 首行的前缀本身;
- 第二行及后续行不做任何特殊的前缀 / 缩进处理。
上述三种排版样式均为常用形式,因此填充命令会根据提取到的前缀特征,结合当前的主模式,来判断用户的排版需求,具体判定规则如下。
若从首行提取的前缀匹配正则表达式 adaptive-fill-first-line-regexp ,或该前缀看起来是 注释起始符序列 (具体判定规则依赖当前主模式),且该前缀不会被识别为后续行的段落起始标识,则此前缀将被用于该段落的填充。
若不满足上述条件,提取到的前缀会被转换为同等缩进宽度的空白字符;若这些空白字符不会被识别为后续行的段落起始标识,则将其作为该段落后续所有行的填充前缀。
在文本模式,以及其他 仅将空行和分页符视作段落分隔符 的模式中,自适应填充所选的前缀永远不会被识别为段落起始标识,因此可直接用于段落填充。
变量 adaptive-fill-regexp 用于定义 可作为填充前缀的行首字符规则 :行首所有匹配该正则表达式的字符序列,都会被当作填充前缀提取。若将变量 adaptive-fill-mode 设为 nil ,则填充命令将 永远不会自动推导 填充前缀。
若需自定义更复杂的自动填充前缀推导规则,可将变量 adaptive-fill-function 设为一个自定义函数。该函数执行时,光标会定位在目标行的左边界之后,函数需根据该行内容返回对应的填充前缀;若函数返回 nil ,则会转而通过 adaptive-fill-regexp 来提取前缀。
27.7. 大小写转换命令
Emacs 提供了专门命令,可将单个单词或任意范围的文本转换为大写或小写形式。
M-l- 将后续单词转换为小写 (
downcase-word) 。 M-- M-l- 将前一个单词转换为小写。注:
M--即 Meta-减号。 M-u- 将后续单词转换为大写 (
upcase-word) 。 M-- M-u- 将前一个单词转换为全大写形式。
M-c- 将后续单词首字母大写 (
capitalize-word) 。 M-- M-c- 将前一个单词转换为首字母大写、其余字母小写的形式。
C-x C-l- 将选中区域的文本转换为小写 (
downcase-region) 。 C-x C-u- 将选中区域的文本转换为大写 (
upcase-region) 。
M-l (downcase-word) 会将光标后方的单词转换为小写,并将光标移至该单词末尾。因此,重复按下 M-l 可依次转换后续的多个单词。 M-u (upcase-word) 则将单词转换为全大写,而 M-c (capitalize-word) 仅将单词首字母转为大写,其余字母均转为小写。若为这些命令指定参数,可一次性转换多个单词。这些命令在将大量全大写文本转换为大小写混合格式时尤为实用:你可在文本中移动光标,根据需要对每个单词分别使用 M-l 、 M-u 或 M-c ,也可偶尔使用 M-f 跳过无需转换的单词。
若为单词大小写转换命令指定负参数(如 C-u - 5 M-c ),该命令会对光标 前方 指定数量的单词进行转换,且 不会移动光标 。当你刚输入完一个大小写错误的单词时,此用法会非常便捷:直接使用 M-- M-u 这类转换命令即可修正,无需中断输入。
若在单词 中间位置 执行单词大小写转换命令,该命令仅对光标 后方 的单词部分生效(这一行为与 M-d ( kill-word ,删除单词) 类似);若指定负参数,则仅对光标 前方 的单词部分进行大小写转换。
另外两个大小写转换命令为 C-x C-u (upcase-region) 和 C-x C-l (downcase-region) ,二者会将光标与标记之间的 整个区域 内的所有文本转换为指定大小写,执行后光标与标记的位置均保持不变。
区域大小写转换命令 upcase-region 和 downcase-region 默认处于 禁用状态 ,这意味着你尝试使用这类命令时,Emacs 会要求你确认操作。当你确认后,可选择启用该命令,后续使用时将不再弹出确认提示。详见「命令的禁用」相关内容。
27.8. 文本模式
文本模式是用于编辑自然语言文本文件的主模式,扩展名以 .txt 结尾的文件通常会以文本模式打开(参见「文件模式的选择」)。若要显式切换至文本模式,可执行命令 M-x text-mode 。
在文本模式下, 仅空行和分页符可作为段落的分隔符 。因此段落可设置缩进,且自适应填充功能会在填充段落时,自动判定应使用的缩进格式(参见「自适应填充」)。
文本模式下,按下 TAB 键 (indent-for-tab-command) 通常会插入空白字符,直至下一个制表位,而非为当前行进行缩进处理,具体细节参见「缩进」相关说明。
文本模式会关闭所有与注释相关的功能,除非你显式调用这些功能;同时该模式会修改语法表,将撇号认定为单词的组成部分(例如don't会被视作一个完整单词)。但在首字母大写的处理逻辑中,若单词以撇号开头,该撇号会被当作前缀处理(例如执行 M-c 时,'hello'会按预期转换为'Hello')。
若你习惯为段落首行设置缩进,应使用 段落缩进文本模式 (执行 M-x paragraph-indent-text-mode )而非普通文本模式。在该模式下,段落之间无需添加空行,因为首行的缩进已足够作为新段落的标识;但此模式 不支持 所有行均带缩进的段落排版形式。若因场景限制无法修改主模式(例如撰写邮件时),可执行 M-x paragraph-indent-minor-mode ,启用功能等效的副模式。
按下 M-TAB 键 (completion-at-point) 可对光标前方的部分单词进行补全,该功能默认以拼写词典作为候选词库(参见「拼写检查与修正」)。若你的窗口管理器已将 M-TAB 绑定为窗口切换快捷键,可改用 ESC TAB 或 C-M-i 执行单词补全;若要禁用该补全功能,可将变量 text-mode-ispell-word-completion 自定义为 nil 。
进入文本模式时,会自动运行模式钩子 text-mode-hook (参见「主模式」)。
后续章节将介绍若干由文本模式派生的主模式,这些派生模式继承了上文所述的文本模式绝大部分功能;值得注意的是,文本模式的派生模式在运行自身的模式钩子前,会先执行 text-mode-hook 。
27.9. 大纲模式
outline mode 大纲模式 是从文本模式派生出的主模式,专为编辑大纲内容设计。该模式提供了大纲结构中条目间的导航命令,还可将缓冲区中的部分内容临时隐藏,让大纲的层级结构更易查看。输入 M-x outline-mode 即可切换至大纲模式。进入大纲模式时,会先运行钩子函数 text-mode-hook ,再运行钩子函数 outline-mode-hook (参见「钩子」)。
当你使用大纲模式的命令隐藏某一行时(参见「大纲可见性命令」),该行会从屏幕中消失,且在其上一行可见内容的末尾会显示一个省略号(连续三个句点),用以标识存在隐藏文本。连续多行隐藏内容仅会显示一个省略号。
各类对行进行操作的编辑命令(如 C-n 和 C-p ),会将隐藏行的文本视作其前一行可见内容的一部分。删除某行可见内容末尾的省略号,实际会删除该省略号对应的所有后续隐藏文本。
27.9.1. 大纲次模式
大纲次要模式是一种 buffer-local minor mode 缓冲区局部的次要模式 ,其提供的命令与主模式「大纲模式」完全一致,且可与其他主模式配合使用。你可以键入 M-x outline-minor-mode 来切换当前缓冲区的大纲次要模式,也可通过 file-local variable 文件局部变量 设置,在指定文件中启用该模式(参见《文件中的局部变量》)。
主模式「大纲模式」在 C-c 前缀下提供专属按键绑定;而大纲次要模式则将 C-c @ 作为前缀实现类似的按键绑定,这一设计是为了减少与主模式专属命令的冲突(前缀可通过变量 outline-minor-mode-prefix 进行自定义)。
若变量 outline-minor-mode-use-buttons 设为非空值,大纲次要模式除了显示省略号外,还会在标题行开头添加 按钮 ,用以标识该章节处于隐藏状态。点击该按钮可切换章节的显示 / 隐藏状态。若该变量值设为 insert ,按钮会直接插入到缓冲区文本中,此时按下回车键( RET )也可实现与鼠标点击相同的章节显示切换效果;但 不建议 在可编辑缓冲区中将该值设为 insert ,因为该操作会修改缓冲区内容。若变量值设为 in-margins ,大纲次要模式会在 窗口边距 中标识隐藏的章节,且这些按钮可自定义为图标样式(参见《图标》)。
若用户选项 outline-minor-mode-cycle 设为非空值,大纲标题行将启用 TAB 和 S-TAB 键的 循环切换可见性 功能(参见outline-cycle 命令):按下 TAB 可对当前章节循环执行「隐藏」「显示子标题」「显示全部内容」操作;按下 S-TAB 则会对整个缓冲区执行相同的循环操作。
27.9.2. 大纲格式
大纲模式假定缓冲区中的行分为两类: / heading lines 标题行/ 与 body lines 正文行 。标题行代表大纲中的一个主题,以一个或多个星号('*')开头;星号的数量决定了该标题在大纲结构中的层级。因此,以单个 '*' 开头的标题行为一级主题,在该标题行与下一个一级标题行之间、所有以两个 '*' 开头的标题行均为其子主题,依此类推。非标题行的所有行均为正文行,且正文行归属于其前方最近的标题行。示例如下:
* 饮食 这是饮食主题的正文内容, 用于阐述与饮食相关的信息。 ** 美味的饮食 这是二级标题的正文内容。 ** 难吃的饮食 该标题 也可包含 正文内容, 且可跨多行。 *** 宿舍饮食 * 居所 这是另一个一级主题及其标题行。
一个标题行与其后所有的正文行,合称为一个 entry 大纲项 ;一个标题行与其后所有层级更深的标题行、以及这些标题行对应的正文行,合称为一个 subtree 大纲子树 。
你可通过设置变量 outline-regexp ,自定义区分标题行的判定规则(推荐在主模式函数中或通过文件局部变量完成设置)。凡是行首匹配该正则表达式的行,都会被判定为标题行;行内(非左边界处)的匹配结果无效。
匹配文本的长度决定了标题的层级 ,匹配文本越长,标题的嵌套层级越深。例如,若某文本格式化工具使用 '@chapter(章节)' 、 '@section(小节)' 、 '@subsection(子小节)' 命令划分文档结构,你可将 outline-regexp 设为‘ "@chap\\|@\\(sub\\)*section" ’,使这些命令所在行成为大纲的标题行。此处有一个技巧: 'chapter' 与 'section' 两个单词长度相同,但通过将正则表达式设为仅匹配 'chap' ,可让 '@chapter' 对应的匹配文本更短,从而让大纲模式识别出「小节归属于章节」的层级关系 —— 该设置的前提是,无其他命令以 '@chap' 开头。
除设置 outline-regexp 外,你也可将变量 outline-search-function 设为一个自定义函数,用于匹配当前标题并查找下一个标题(详见《Emacs Lisp 参考手册》中的「大纲次要模式」章节)。
你还可通过设置变量 outline-level ,显式指定标题行的层级计算规则。该变量的值应为一个 无参函数 ,调用后会返回当前标题的层级。推荐在主模式命令中或通过文件局部变量完成该变量的设置。
27.9.3. 大纲移动命令
大纲模式提供了专用于在标题行间向前、向后跳转的移动命令。
C-c C-n- 将光标移至下一个可见的标题行 (
outline-next-visible-heading) 。 C-c C-p- 将光标移至上一个可见的标题行 (
outline-previous-visible-heading) 。 C-c C-f- 将光标移至与当前行同级的下一个可见标题行 (
outline-forward-same-level) 。 C-c C-b- 将光标移至与当前行同级的上一个可见标题行 (
outline-backward-same-level) 。 C-c C-u- 将光标上移至更高层级(包含性更强)的可见标题行 (
outline-up-heading) 。
上述所有命令均可接收数字参数作为重复执行的次数。例如,为 C-c C-f 指定数字参数时,光标会向前跳转指定次数的同级可见标题行;为 C-c C-u 指定数字参数时,光标会向上跳出指定层数的嵌套标题层级。
27.9.4. 大纲可见性命令
大纲模式提供了若干命令,可基于大纲结构临时隐藏或显示缓冲区中的部分内容。这些命令不支持撤销—— 撤销机制不会记录其执行效果,因此你可直接撤销至执行这些命令之前的状态(参见「撤销」相关说明)。
此类命令大多作用于当前标题行:若光标位于某标题行,该行即为当前标题行;若光标位于正文行,当前标题行则为其前方最近的那个标题行。
C-c C-c- 隐藏当前标题行对应的正文内容 (
outline-hide-entry) 。 C-c C-e- 显示当前标题行对应的正文内容 (
outline-show-entry) 。 C-c C-d- 隐藏当前标题行下的所有内容(标题行本身除外) (
outline-hide-subtree) 。 C-c C-s- 显示当前标题行下的所有内容,包括正文、子标题及其对应的正文 (
outline-show-subtree) 。 C-c C-l- 隐藏当前标题行及其所有子标题的正文内容 (
outline-hide-leaves) 。 C-c C-k- 显示当前标题行所有层级的子标题 (
outline-show-branches) 。 C-c C-i- 显示当前标题行的直接子标题(下一级标题) (
outline-show-children) 。 C-c C-t- 隐藏缓冲区中的所有正文行 (
outline-hide-body) 。 C-c C-a- 显示缓冲区中的所有行 (
outline-show-all) 。 C-c C-q- 仅显示大纲前 n 级标题行,隐藏其余所有内容 (
outline-hide-sublevels) 。 C-c C-o- 仅显示光标所在的大纲项(标题 / 正文),以及从该位置向上至大纲顶层的所有标题,隐藏其余内容 (
outline-hide-other) 。 C-c /h- 正则表达式 回车隐藏标题内容匹配指定正则表达式的所有标题对应的正文 (
outline-hide-by-heading-regexp) 。 C-c /s- 正则表达式 回车显示标题内容匹配指定正则表达式的所有标题对应的正文 (
outline-show-by-heading-regexp) 。
上述命令中最基础的是 C-c C-c (outline-hide-entry) 和 C-c C-e (outline-show-entry) :前者隐藏紧跟当前标题行的正文内容,后者则将其显示,这两个命令均 不影响子标题及其正文 。
C-c C-d (outline-hide-subtree) 和 C-c C-s (outline-show-subtree) 是功能更强大的命令,二者作用于当前标题行的 subtree 大纲子树 ,包括该标题的正文、所有直接和间接子标题,以及这些子标题对应的全部正文。
C-c C-l (outline-hide-leaves) 会隐藏当前标题行的正文,以及其大纲子树中所有内容的正文, 子标题本身仍保持可见 ; C-c C-k (outline-show-branches) 可将此前被隐藏的子标题(如通过 C-c C-d 隐藏)重新显示; C-c C-i (outline-show-children) 是该命令的轻量版本,仅显示当前标题行的直接子标题(即下一级标题)。
C-c C-o (outline-hide-other) 会隐藏除以下内容外的所有部分:光标所在的大纲项、该大纲项的所有上级标题(从当前位置至大纲顶层的标题),以及大纲的所有顶层标题;同时,该命令还会显示缓冲区中第一个标题行之前的所有正文行。
C-c /h (outline-hide-by-heading-regexp) 会先提示你输入正则表达式,随后隐藏所有标题内容匹配该正则表达式的标题对应的正文; C-c /s (outline-show-by-heading-regexp) 的作用与之相反,会提示输入正则表达式并显示匹配标题的正文。
其余命令则作用于 整个缓冲区 :
C-c C-t (outline-hide-body) 隐藏所有正文行,仅显示大纲的结构(一个特殊例外:该命令不会隐藏文件开头、第一个标题行之前的内容,尽管从技术上来说这些内容也属于正文行); C-c C-a (outline-show-all) 显示缓冲区中的所有行; C-c C-q (outline-hide-sublevels) 默认隐藏除当前标题行层级及以上的顶层标题外的所有内容(若光标未在标题行,默认仅显示 1 级标题);若为该命令指定数字参数 n ,则仅显示大纲的前 n 级标题行,其余内容全部隐藏。注意:该命令会将前 n 级标题行完全显示,同时也会显示第一个标题行之前的所有正文行。
大纲模式还提供了两个便捷的循环切换命令,可分别对单个章节和整个缓冲区的内容可见性进行循环切换:在标题行按下 TAB 键 (outline-cycle) ,会将当前章节在「隐藏全部内容」「仅显示子标题」「显示全部内容」三种状态间循环切换;按下 S-TAB 键 (outline-cycle-buffer) ,会将整个缓冲区在「仅显示顶层标题」「显示所有标题(含子标题)」「显示全部内容」三种状态间循环切换。
当增量搜索找到被大纲模式隐藏的文本时,会自动将缓冲区中的该部分内容显示;若你在该位置退出搜索,文本仍会保持可见。若要切换 激活的增量搜索 是否可匹配隐藏文本,可按下 M-s i ;若要修改后续搜索的默认行为,可自定义选项 search-invisible (该选项也会影响查询替换及相关函数对隐藏文本的处理方式,参见「查询替换」相关说明)。你也可启用 显示模式 (M-x reveal-mode,一种缓冲区局部的次要模式),在导航文本时自动显示对应的隐藏内容。
变量 outline-default-state 用于控制启用大纲模式后,哪些标题行默认保持可见,其取值对应不同的初始可见性规则:
- 设为非空值:部分标题行默认处于大纲折叠状态;
- 设为数字
n:仅显示第n级及以上层级的标题行; - 设为
outline-show-all:显示缓冲区中的所有文本内容; - 设为
outline-show-only-headings:仅显示所有标题行(无论层级),隐藏所有正文; - 设为 lambda 匿名函数 / 函数名:启用大纲模式后,会无参调用该函数,由函数控制标题行的可见性切换。
27.9.5. 多视图查看单个大纲
你可在不同窗口中同时显示同一个大纲的两个视图,操作时需通过 M-x make-indirect-buffer 命令创建 indirect buffer 间接缓冲区 。该命令的第一个参数为已存在的大纲缓冲区名称,第二个参数为新建间接缓冲区的自定义名称(详见「间接缓冲区」相关说明)。
间接缓冲区创建完成后,即可通过 C-x 4 b 或其他 Emacs 常规命令,在窗口中展示该缓冲区。大纲模式中用于隐藏、显示文本内容的各类命令,会 对每个缓冲区独立生效 ;因此每个缓冲区均可拥有专属的大纲视图。若需为同一个大纲创建两个以上的视图,新建更多间接缓冲区即可。
27.9.6. 折叠编辑
Foldout 折叠扩展包为大纲模式和大纲次要模式新增了折叠相关命令。折叠的核心思路是 聚焦大纲的嵌套部分 ,同时隐藏其更高层级的同级内容。
假设某大纲模式缓冲区中,所有一级标题下的文本和子标题均处于隐藏状态。若要查看其中某个一级标题下的隐藏内容,你可使用 C-c C-e (M-x outline-show-entry) 显示其正文,或使用 C-c C-i 显示其子级(二级)标题。
而使用 Foldout 扩展包时,你只需按下 C-c C-z (M-x foldout-zoom-subtree) ,该操作会同时显示目标一级标题的正文和二级子标题,并对缓冲区进行 缩窄处理 ,最终仅保留该一级标题、其正文及下属二级标题的可见性。若需继续查看某二级标题下的内容,将光标移至该二级标题处,再次按下 C-c C-z 即可 —— 此操作会显示该二级标题的正文和三级子标题,并再次缩窄缓冲区。你可根据需要,对后续各级子标题持续执行该聚焦操作,模式行中会显示当前的折叠聚焦层级。
对标题执行聚焦操作时,若仅需显示其子级标题,可为命令指定 数字参数 : C-u C-c C-z ;你也可指定显示的子标题层级数(可对比 M-x outline-show-children 命令),例如 M-2 C-c C-z 会显示目标标题下 两层 子标题。反之,若仅需显示目标标题的正文,可指定 负参数 : M-- C-c C-z ;若要展开目标标题的整个子树(效果等同于 C-c C-s / M-x outline-show-subtree ),则指定 零参数 : M-0 C-c C-z 。
处于折叠聚焦状态时,你仍可正常使用大纲模式的各类显示 / 隐藏命令,且不会影响 Foldout 的折叠状态。同时,由于缓冲区已被缩窄,所有全局编辑操作 仅会作用于当前聚焦标题下的内容 ,这一特性可帮助你将修改范围限定在文档的特定章节或小节中。
若要 退出折叠聚焦 ,按下 C-c C-x (M-x foldout-exit-fold) 即可,该操作会隐藏当前顶层聚焦标题下的所有文本和子标题,并恢复缓冲区此前的视图。为该命令指定数字参数,可退出对应层数的折叠;指定零参数,则会直接退出所有折叠层级。
若希望 取消缓冲区的缩窄处理 ,但保留当前已显示的文本和子标题(不隐藏),可为退出命令指定负参数。例如 M--2 C-c C-x ,会退出两层折叠,且保持当前所有文本和子标题的可见状态。
Foldout 模式还提供了鼠标操作命令,用于执行折叠的进入、退出及文本的显示 / 隐藏,具体如下:
C-M-mouse-1聚焦点击的目标标题- 单击:显示标题正文
- 双击:显示标题的子级标题
- 三击:显示标题的正文和子级标题
- 四击:展开标题的整个子树
C-M-mouse-2显示点击的目标标题下的内容- 单击:显示标题正文
- 双击:显示标题的子级标题
- 三击:显示标题的正文和子级标题
- 四击:展开标题的整个子树
C-M-mouse-3隐藏点击的目标标题下的内容,或退出折叠- 单击:隐藏标题的整个子树
- 双击:退出折叠并隐藏对应内容
- 三击:退出折叠,保留内容可见
- 四击:退出所有折叠并隐藏对应内容
你可通过设置变量 foldout-mouse-modifiers ,自定义鼠标操作的修饰键(替代默认的 Ctrl+Meta 组合键);但需注意,若你已加载 foldout.el 库文件,需 重新加载 该库,修改才能生效。
使用 Foldout 扩展包的方法有两种:一是手动键入 M-x load-library RET foldout RET 加载;二是将以下代码添加至 Emacs 初始化文件中,实现 自动加载 :
(with-eval-after-load "outline" (require 'foldout))
27.10. 组织模式(Org Mode)
Org 模式是大纲模式的一个变体,专为将 Emacs 用作事务管理工具和 / 或文档编辑系统设计。扩展名以 .org 结尾的文件会默认以 Org 模式打开(参见《选择文件模式》);若要显式切换至 Org 模式,可键入 M-x org-mode 命令。
与大纲模式一致,Org 模式中每个大纲项都有以一个或多个星号( '*' )开头的标题行(详见《大纲的格式》)。此外,所有以井号( '#' )开头的行都会被视作 注释行 。
Org 模式提供了便捷的命令,用于查看和操作大纲结构,其中最基础的是 TAB 键 (org-cycle) 。若在标题行触发该命令,会对当前大纲子树的可见状态进行循环切换,依次为:(1) 仅显示该标题行;(2) 仅显示该标题行及其直接子标题行(若存在);(3) 显示整个大纲子树。若在正文行触发该命令,则会执行 TAB 键的全局绑定功能。
在 Org 模式缓冲区的任意位置按下 S-TAB 键 (org-shifttab) ,会对整个大纲结构的可见状态进行循环切换,依次为:(1) 仅显示顶层标题行;(2) 显示所有标题行、隐藏所有正文行;(3) 显示所有内容。
在标题行按下 M-UP (org-metaup) 或 M-DOWN (org-metadown) ,可将 整个大纲项 (包括其正文行和下属子树,若有)在缓冲区中向上或向下移动;同理,按下 M-LEFT (org-metaleft) 和 M-RIGHT (org-metaright) ,可对标题行进行 升级 或 降级 操作。若在正文行触发这些快捷键,则会执行其全局绑定功能。
下述小节将介绍将 Org 模式用作事务管理工具和文档编辑系统的基础用法,更多细节可参见《Org 模式手册》中的《简介》章节。
27.10.1. 作为事务管理器的 Org 模式
在大纲项内的任意位置按下 C-c C-t (org-todo) ,即可将该 Org 大纲项标记为 TODO item 待办项 ,此操作会在标题行添加 'TODO' 关键字。再次按下 C-c C-t ,关键字会切换为 'DONE' ;第三次按下则会彻底移除该关键字,后续按动会按自定义的关键字序列循环切换。你可通过变量 org-todo-keywords ,自定义 C-c C-t 命令所使用的关键字。
除了将大纲项标记为待办,还可在项内按下 C-c C-s (org-schedule) 为其添加 日程日期 。该命令会弹出 Emacs 日历供你选择日期(参见《日历与日记》),并在标题行下方添加 'SCHEDULED' 标签及选定的日期。 C-c C-d (org-deadline) 命令的操作方式与之相同,区别仅在于该命令使用 'DEADLINE' (截止日期)标签。
当你在 Org 文件中规划了若干待办项后,按下 C-c [ (org-agenda-file-to-front) ,即可将该文件添加至 日程文件列表 中。Org 模式支持便捷管理多个日程文件,可用于规划生活中的不同事务板块,日程文件列表的信息会存储在变量 org-agenda-files 中。
要查看所有日程文件中的事项,键入 M-x org-agenda 即可。该命令会提示你选择查看的内容类型,例如本周待办事项列表、含特定关键字的待办项列表等。更多细节可参见《Org 模式手册》中的《日程视图》章节。
27.10.2. 作为创作系统的 Org 模式
你可对 Org 笔记进行美观的格式排版,并将其整理为适用于导出和发布的形式。在 Org 缓冲区的任意位置按下 C-c C-e (org-export-dispatch) ,即可导出当前缓冲区的内容。该命令会提示你选择导出格式,目前支持的格式包括 HTML、LaTeX、Texinfo、开放文档(.odt)、iCalendar、Markdown、手册页以及 PDF。部分格式(如 PDF)需要安装相应的系统工具方可使用。
若要将多个文件一次性导出至本地或网络中的指定目录,你需要通过变量 org-publish-project-alist 定义项目列表,具体细节可参考该变量的说明文档。
Org 模式支持一套简洁的标记语法,可对导出文档的文本进行格式设置,示例如下:
- This text is /emphasized/ - This text is *in bold* - This text is _underlined_ - This text uses =a teletype font= #+begin_quote ``This is a quote.'' #+end_quote #+begin_example This is an example. #+end_example
27.11. TeX 模式
TeX 是由 Donald Knuth (唐纳德・克努特) 编写的一款功能强大的文本格式化程序,与 GNU Emacs 一样,它属于自由软件。TeX 格式拥有多个衍生版本,包括:LaTeX(TeX 的简化输入格式)、DocTeX(编写 LaTeX 源码所使用的专用文件格式,将源码与文档说明融为一体)以及 SliTeX(LaTeX 的一款已废弃专用格式13)。
Emacs 为上述每一种 TeX 衍生版本都提供了对应的 TeX 主模式:纯 TeX mode、LaTeX mode、DocTeX mode 和 SliTeX mode 。Emacs 会通过分析缓冲区的内容,自动选择对应的模式(该操作由 tex-mode 命令完成,打开类 TeX 文件时该命令通常会自动调用,参见《选择文件模式》)。若缓冲区内容不足以判定格式类型,Emacs 会选用变量 tex-default-mode 指定的模式,该变量的默认值为 latex-mode (LaTeX 模式)。若 Emacs 的自动识别结果有误,你可通过 plain-tex-mode 、 latex-mode 、 slitex-mode 或 doctex-mode 命令,手动选择正确的 TeX 衍生模式。
以下小节将介绍 TeX 模式及其各衍生版本的功能特性。另有多款与 TeX 相关的 Emacs 扩展包未在本手册中说明,相关信息如下:
- BibTeX mode:适用于 BibTeX 文件的主模式,该类文件常用于管理 LaTeX 文档的参考文献。更多信息可查阅
bibtex-mode命令的文档字符串。 - RefTeX 扩展包:提供一款可与 LaTeX 模式配合使用的次要模式,用于管理参考文献。更多信息可参见随 Emacs 发布的 RefTeX 信息手册。
- AUCTeX 扩展包:为编辑 TeX 及其相关格式提供更高级的功能,包括在 Emacs 缓冲区中预览 TeX 公式的能力。与 BibTeX 模式和 RefTeX 扩展包不同,AUCTeX 并非 Emacs 的默认内置扩展包,可通过软件包菜单下载(参见《Emacs Lisp 软件包》);安装完成后,可参阅该扩展包自带的 AUCTeX 手册。
- TeX 编辑命令
- LaTeX 编辑命令
- TeX 打印命令
- TeX 模式杂项功能
27.11.1. TeX 编辑命令
"- 根据上下文插入 ‘``’ 、‘"’或‘''’ (
tex-insert-quote) 。 C-j- 插入段落分隔符(两个换行符),并检查前一段落是否存在大括号或美元符配对不匹配的情况 (
tex-terminate-paragraph) 。 M-x tex-validate-region- 检查选中区域内的每一个段落,排查大括号或美元符的配对不匹配问题。
C-c {- 插入 ‘{}’,并将光标定位在两个符号之间 (
tex-insert-braces) 。 C-c }- 向前移动光标,跳过下一个未配对的右大括号 (
up-list) 。
在 TeX 中,通常不使用双引号字符‘"’;引用内容需以两个反引号‘``’ 开头,以两个单引号 ‘''’结尾。因此 TeX 模式会将双引号 " 键绑定到 tex-insert-quote 命令。该命令的行为为:在空白字符或左大括号后,在反斜杠后按下 " 双引号,会直接插入 `` 反引号 ;在其他任意字符后会插入 '' 双单引号 。
有一个特殊例外规则:若光标前的文本是 `` 反引号或 '' 双单引号,此时按下 " 双引号,Emacs 会将光标前的这两个字符替换为单个 " 双引号字符。因此当你需要插入单个 " 双引号时,直接连续按两次 " 双引号即可实现(也可使用快捷键 C-q " 插入该字符)。
在 TeX 模式下,美元符 '$' 被赋予特殊的语法编码,用以识别 TeX 数学模式分隔符的配对规则。当你插入一个用于退出数学模式的 '$' 时,系统会短暂显示对应的、用于进入数学模式的配对 '$' 的位置。这一功能与插入右大括号时,系统显示配对左大括号位置的功能一致。但由于系统无法判断一个 '$' 是用于进入还是退出数学模式,因此当你插入一个用于进入数学模式的 '$' 时,系统仍会显示上一个 '$' 的位置,仿佛二者是配对关系,即便它们实际上并无关联。
TeX 将大括号作为必须严格配对的分隔符。部分用户倾向于始终保持大括号成对存在,而非单独插入单侧。可使用快捷键 C-c { (tex-insert-braces) 插入一对大括号,光标会停在两个大括号之间,方便你输入括号内的内容。输入完成后,可使用快捷键 C-c } (up-list) 将光标移至右大括号后方。若你已选中一段文本,再按下 C-c { ,该命令会直接将选中的文本用一对大括号包裹起来。
Emacs 提供了两个检查大括号配对的命令: C-j (tex-terminate-paragraph) 会检查光标所在位置之前的整段文本,同时插入两个换行符以开始新段落;若检测到配对错误,会在回显区输出提示信息。执行 M-x tex-validate-region 命令可逐段检查选中的区域,所有配对错误会列在 *Occur* 缓冲区中,你可在该缓冲区使用 Occur 模式的常规命令(如 C-c C-c )定位到具体的配对错误位置(详见「其他搜索与循环命令」)。
需注意,在 TeX 模式下,Emacs 的各类命令会对中括号、小括号和大括号一并进行配对计数,而非仅检查大括号。从 TeX 语法检查的严格角度来说,这一处理方式并不完全合规;但由于小括号和中括号在文本中也常作为需要配对的分隔符使用,让各类光标移动命令和自动配对显示功能支持这两类符号,实际使用中会更为便捷。
27.11.2. LaTeX 编辑命令
LaTeX 模式提供了若干不适用于纯 TeX 的额外功能:
C-c C-o- 插入 LaTeX 块的 '
\begin' 和 '\end' 命令,并将光标定位到二者之间的空行 (latex-insert-block) 。 C-c C-e- 闭合最内层尚未闭合的 LaTeX 块 (
latex-close-block) 。
在 LaTeX 输入中, '\begin' 和 '\end' 标记用于对文本块进行分组。插入文本块时,按下 C-c C-o (latex-insert-block) ,该命令会提示输入块类型,随后插入配对的对应 '\begin' 和 '\end' 标记,在二者之间保留一行空行并将光标移至该行。
在为 C-c C-o 命令输入块类型参数时,可使用常规的补全命令(详见「补全」章节)。默认补全列表包含标准的 LaTeX 块类型;若需在补全中添加更多块类型,可自定义列表型变量 latex-block-names 。
在 LaTeX 输入中, '\begin' 和 '\end' 标记必须成对出现。按下 C-c C-e (latex-close-block) 可插入与最后一个未配对 '\begin' 相匹配的 '\end' 标记,同时会为该 '\end' 标记设置与对应 '\begin' 一致的缩进;若光标位于行首,还会在 '\end' 标记后插入一个换行符。次要模式 latex-electric-env-pair-mode 开启后,当你输入 '\begin' 或 '\end' 标记时,会自动为你插入配对的另一标记。
27.11.3. TeX 打印命令
你可以将 TeX 作为 Emacs 的子进程调用,编译缓冲区中的全部内容,也可仅编译部分内容(例如大型文档中的某一章节)。
C-c C-b- 对当前缓冲区的全部内容调用 TeX 编译 (
tex-buffer) 。 C-c C-r- 结合缓冲区的头部内容,对当前区域调用 TeX 编译 (
tex-region) 。 C-c C-f- 对当前文件调用 TeX 编译 (
tex-file) 。 C-c C-v- 预览上一次执行
C-c C-b、C-c C-r或C-c C-f命令后的编译输出结果 (tex-view) 。 C-c C-p- 打印上一次执行
C-c C-b、=C-c C-r= 或C-c C-f命令后的编译输出文件 (tex-print) 。 C-c TAB- 对当前文件调用 BibTeX 进行参考文献处理 (
tex-bibtex-file) 。 C-c C-l- 将显示 TeX 编译输出的窗口重定中心,使最后一行内容可见 (
tex-recenter-output-buffer) 。 C-c C-k- 终止 TeX 子进程 (
tex-kill-job) 。 C-c C-c- 对当前缓冲区的全部内容调用其他编译命令 (
tex-compile) 。
要对当前缓冲区内容进行 TeX 编译,按下 C-c C-b (tex-buffer) 即可,格式化后的输出内容会写入一个临时文件,默认是 .dvi 格式文件。编译完成后,可按下 C-c C-v (tex-view) 启动 xdvi 等外部程序查看该输出文件,也可按下 C-c C-p (tex-print) 打印输出文件的硬拷贝。
默认情况下, C-c C-b 会在 当前目录 中运行 TeX,编译输出文件也会生成在该目录。若要在其他目录运行 TeX,可将变量 tex-directory 修改为目标目录。如果你的环境变量 TEXINPUTS 包含相对路径,或 TeX 文件中使用了带相对文件名的 '\input' 命令,那么 tex-directory 必须设为 . (当前目录),否则会出现编译错误;其他情况下,可安全地指定 '/tmp' 等其他目录。
缓冲区使用的 TeX 变体,决定了 C-c C-b 实际执行的 Shell 命令:纯 TeX 模式下,由变量 tex-run-command 指定,默认值为 tex ;LaTeX 模式下,由变量 latex-run-command 指定,默认值为 latex 。无论使用哪种 TeX 变体, C-c C-v 用于查看 .dvi 输出文件的 Shell 命令,均由变量 tex-dvi-view-command 决定; C-c C-p 用于打印输出文件的 Shell 命令,均由变量 tex-dvi-print-command 决定。可通过变量 tex-print-file-extension 设置 TeX 编译文件的查看和打印所需扩展名,例如将其设为 .pdf ,并相应更新 tex-dvi-view-command 、 tex-dvi-print-command ,以及 latex-run-command 或 tex-run-command 。
通常,Emacs 会自动将输出文件名追加到上述 Shell 命令后。例如若 tex-dvi-view-command 设为 xdvi ,执行 C-c C-v 时会运行 xdvi 输出文件名。但在某些场景下,需要将文件名嵌入命令中(例如需将文件名作为某一命令的参数,且该命令的输出通过管道传递给另一命令),此时可在命令字符串中用 '*' 指定文件名的插入位置,示例如下:
(setq tex-dvi-print-command "dvips -f * | lpr")
TeX 的终端输出(包括所有错误信息)会显示在名为 *tex-shell* 的缓冲区中。若 TeX 编译出现错误,你可切换至该缓冲区并输入交互指令(操作方式与 Shell 模式一致,详见「交互式子 Shell」章节);无需切换缓冲区时,按下 C-c C-l 即可滚动该缓冲区,使最后一行内容可见。
若发现 TeX 进程的输出不再有参考价值,按下 C-c C-k (tex-kill-job) 即可终止该进程;执行 C-c C-b 或 C-c C-r 时,也会自动终止仍在运行的 TeX 进程。
你也可按下 C-c C-r (tex-region) ,对任意指定区域进行 TeX 编译,但该操作存在一定限制:大多数 TeX 输入文件的开头会包含参数设置和宏定义命令,缺少这些命令,文件后续内容将无法正确格式化。为解决该问题, C-c C-r 允许你将文件中的某一部分标记为 核心命令区(头部) ,编译时会将该区域内容作为 TeX 输入的一部分,添加到指定编译区域之前。文件中这一被标记的部分称为 头部(header) 。
在 纯 TeX 模式 下,需在文件中插入两个特殊字符串来标记头部的范围:在头部内容前插入 '%**start of header' ,在头部内容后插入 '%**end of header' 。每个字符串必须完整出现在单独一行中,该行前后可包含其他文本,且包含这两个字符串的行也会被纳入头部。若在缓冲区的前 100 行中未找到 '%**start of header' , C-c C-r 会判定该文件无头部。
在 LaTeX 模式 下,头部内容以 '\documentclass' 或 '\documentstyle' 开头,以 '\begin{document}' 结尾。这些是 LaTeX 对所有文档的强制要求命令,因此无需额外操作即可自动识别头部。
tex-buffer 和 tex-region 命令的所有操作均在 临时目录 中执行,无法访问 TeX 交叉引用所需的任何辅助文件;因此这些命令通常不适合编译需要保证所有交叉引用均正确的最终版本文档。
若需要生成交叉引用所需的辅助文件,请使用 C-c C-f (tex-file) 命令,该命令会在当前缓冲区对应文件的 所在目录 中,对该文件执行 TeX 编译。运行 TeX 前,Emacs 会提示保存所有已修改的缓冲区。通常需要执行两次 tex-file 命令,才能保证交叉引用的内容全部正确。
变量 tex-start-options 的取值,用于指定 TeX 编译时的运行选项。变量 tex-start-commands 的取值,用于指定启动 TeX 的 TeX 命令,默认值会让 TeX 以 非停止模式 运行;若要以交互模式运行 TeX,将该变量设为空字符串 "" 即可。
大型 TeX 文档通常会拆分为多个文件,包含一个主文件和若干子文件。直接对於文件执行 TeX 编译通常无法成功,必须对主文件进行编译。为了在编辑子文件时, tex-file 命令仍能正常使用,可将变量 tex-main-file 设为主文件的名称,此后执行 tex-file 时,会自动对该主文件进行编译。
使用 tex-main-file 最便捷的方式,是在每个子文件的 局部变量列表 中指定其值(详见「文件中的局部变量」章节)。
对于 LaTeX 文件,可使用 BibTeX 处理当前缓冲区对应文件的辅助文件:BibTeX 会在参考文献数据库中检索引用条目,并为文档的参考文献部分生成格式化的引用内容。按下 C-c TAB (tex-bibtex-file) 会执行 Shell 命令 tex-bibtex-command ,为当前文件生成.bbl 格式的参考文献文件。通常的操作流程为:先执行一次 C-c C-f (tex-file) 生成 .aux 辅助文件,再执行 C-c TAB (tex-bibtex-file) 处理参考文献,最后再执行两次 C-c C-f (tex-file) ,即可保证交叉引用和参考文献均正确显示。
若要对当前 TeX 缓冲区调用其他编译程序,按下 C-c C-c (tex-compile) 即可。该命令支持为 pdflatex 、 yap 、 xdvi 、 dvips 等常用编译程序传递参数,你可使用标准的补全按键选择所需的编译程序(详见「补全」章节)。
27.11.4. TeX 模式杂项功能
进入任意一种 TeX 模式变体时,会先执行钩子函数 text-mode-hook 和 tex-mode-hook ,随后根据实际模式,执行对应的 plain-tex-mode-hook 、 doctex-mode-hook 、 latex-mode-hook 或 slitex-mode-hook 。启动 TeX Shell 时,会执行钩子函数 tex-shell-hook 。相关说明详见「钩子函数」章节。
可使用命令 M-x iso-iso2tex 、 M-x iso-tex2iso 、 M-x iso-iso2gtex 和 M-x iso-gtex2iso ,实现 Latin-1 编码文件与 TeX 编码等效文件之间的格式转换。
27.12. SGML 与 HTML 模式
SGML 和 HTML 对应的主模式提供缩进支持,并内置了针对标签的操作命令。
HTML 相关的编辑包含两种模式:一种是基础的 html-mode (HTML 模式),它是 SGML 模式的轻量定制版本;另一种是 mhtml-mode (多模式 HTML),也是 Emacs 对 HTML 文件的默认模式,该模式可正确处理 <script> 标签中嵌入的 JavaScript 代码,以及 <style> 标签中嵌入的 CSS 样式代码。
核心快捷键与对应功能
C-c C-n- 交互式指定特殊字符,并插入该字符对应的 SGML 转义命令(以 '
&' 开头) (sgml-name-char) 。 C-c C-t交互式指定标签及其属性 (
sgml-tag) 。该命令会先提示输入标签名和各属性值,随后插入配对的开始标签与结束标签,并将光标定位在两个标签之间。若带数字前缀参数
n,该命令会将标签包裹在光标后方的n个单词外;当区域处于激活状态时,标签会直接包裹该区域(若暂存标记模式关闭,传入数字参数-1也可实现区域包裹功能)。C-c C-a- 为当前标签交互式插入属性值 (
sgml-attributes) 。 C-c C-f- 跳过一组配对的标签(即从开始标签到对应结束标签的完整内容) (
sgml-skip-tag-forward) 。数字参数可指定跳过的次数。 C-c C-b- 反向跳过一组配对的标签 (
sgml-skip-tag-backward) 。数字参数可指定反向跳过的次数。 C-c C-d- 删除光标位置或光标后方的标签,并同时删除其配对的标签 (
sgml-delete-tag) 。若目标标签是开始标签,会连带删除结束标签;若为结束标签,则连带删除开始标签。 C-c ? 标签名 RET- 显示指定标签的含义说明 (
sgml-tag-help) 。若未输入标签名(直接回车),则说明光标所在位置的标签。 C-c /- 为最内层未闭合的标签插入结束标签 (
sgml-close-tag) 。若光标处于某一标签内部或注释内容中,该命令会直接闭合当前标签 或 注释,而非插入新的结束标签。 C-c 8- 切换一个次要模式,开启后输入 Latin-1 字符时,会自动插入其对应的 SGML 转义命令,而非字符本身 (
sgml-name-8bit-mode) 。 C-c C-v- 执行自定义的 Shell 命令,对当前缓冲区的内容进行 SGML 有效性验证 (
sgml-validate) (在 HTML 模式下,该快捷键对应其他命令)。 C-c TAB- 切换缓冲区中现有标签的可见性,可作为简易的预览功能使用 (
sgml-tags-invisible) 。
XML 文档编辑:nXML 模式
编辑 XML 文档的主模式为 nXML mode(nXML 模式),这是一款功能强大的主模式:可识别多种现有 XML 模式(Schema),支持通过 M-TAB 实现 XML 元素的补全,还能实时验证 XML 语法并高亮显示错误。在已有缓冲区中启用该模式,可执行 M-x nxml-mode (或等效的 M-x xml-mode )。Emacs 会为 .xml 后缀的文件自动启用 nXML 模式;而对于 .xhtml 后缀的 XHTML 文件,Emacs 默认启用 HTML 模式,若需改为 nXML 模式,可自定义变量 auto-mode-alist (详见「文件模式的选择」章节)。nXML 模式的详细说明,可参考 Emacs 自带的 Info 手册。
你也可选择使用功能更简洁的 SGML 模式编辑 XML(因 XML 是 SGML 的严格子集)。在缓冲区中执行 M-x sgml-mode 即可启用 SGML 模式,启用时 Emacs 会自动检测缓冲区内容是否为 XML 格式;若检测为 XML,会将变量 sgml-xml-mode 设为非nil值,此时上述 SGML 模式的标签插入命令,会始终为标签生成显式的结束标签。
27.13. Nroff 模式
Nroff 模式是衍生自文本模式的主模式,专门用于编辑 nroff 文件(如 Unix 手册页)。输入 M-x nroff-mode 即可进入该模式。进入 Nroff 模式时,会先执行钩子函数 text-mode-hook ,再执行 nroff-mode-hook (详见钩子函数章节)。
在 Nroff 模式下,nroff 命令行被视作段落分隔符,分页由 '.bp' 命令实现,注释以反斜杠加双引号( \" )开头。该模式还定义了以下命令:
M-n- 移动到下一行非 nroff 命令的文本行开头 (
nroff-forward-text-line) ,数字参数可指定重复执行的次数。 M-p- 功能与 M-n 类似,方向为向上移动 (
nroff-backward-text-line) 。 M-?- 在回显区显示当前区域内的文本行行数 (即非 nroff 命令的行数,
nroff-count-text-lines) 。
Electric Nroff mode 是可与 Nroff 模式配合使用的缓冲区局部次要模式,输入 M-x nroff-electric-mode 可切换该模式的开启与关闭(详见次要模式章节)。开启后,当你在包含 分组开启类 nroff 命令 的行尾按下 RET 回车键时,系统会在后续行自动插入对应的分组关闭类 nroff 命令。
若你在 Nroff 模式中启用大纲次要模式(详见大纲模式章节),则标题行定义为以 '.H' 开头、后接数字(表示标题层级)的行。
27.14. 富文本
Enriched mode 富文本模式是一种 WYSIWYG (What You See Is What You Get) 所见即所得 的次要模式,用于编辑带格式的文本文件。启用该模式后,你可为缓冲区中的文本设置各类格式属性(如字体、颜色等);保存缓冲区时,这些格式属性会随文本一同保存,采用 MIME 标准的 'text/enriched' 文件格式。
富文本模式通常与文本模式配合使用(详见文本模式章节),它与语法高亮模式(Font Lock mode)互不兼容 —— 后者被多数主模式(包括大部分编程语言模式)用于实现语法高亮(详见语法高亮模式章节)。与富文本模式不同,语法高亮模式会根据缓冲区当前内容自动分配文本属性,且这些属性不会保存到磁盘中。
Emacs 的 data-directory 变量对应目录下的 'enriched.txt' 文件,可作为富文本模式功能的示例参考。
27.14.1. 富文本模式
富文本模式是一种缓冲区局部次要模式(详见次要模式章节)。当你打开以 'text/enriched' 格式保存的文件时,Emacs 会自动启用富文本模式,并将文件中的格式信息应用到缓冲区文本上。若启用富文本模式后保存缓冲区,文件会以 'text/enriched' 格式存储,其中包含所有的格式信息。
若要创建新的格式化文本文件,可先打开该不存在的文件,再执行 M-x enriched-mode 命令。该命令实际为富文本模式的切换开关:带正数值前缀参数时启用富文本模式,非正数值则禁用。禁用富文本模式后,Emacs 不再以 'text/enriched' 格式保存缓冲区;已添加到缓冲区的各类格式属性会保留在缓冲区中,但不会写入磁盘。
富文本模式 并非保存 Emacs 所有的文本属性 ,仅会保存变量 enriched-translations 中指定的属性,包括字体、颜色、缩进和对齐方式相关的属性。
若打开文件时,Emacs 未能识别出其为 'text/enriched' 格式,可执行 M-x format-decode-buffer 命令。该命令会提示你指定文件格式,并按该格式重新读取文件;当指定为 'text/enriched' 格式时,会自动启用富文本模式。
若要以原始形式查看 'text/enriched' 格式文件(即显示带有标记的纯文本,而非格式化后的文本),可使用 M-x find-file-literally 命令(详见打开文件章节)。
关于 Emacs 如何识别并转换 'text/enriched' 这类文件格式的详细说明,可参考《Emacs Lisp 参考手册》中的格式转换章节;有关文本属性的更多信息,可参考该手册中的文本属性章节。
27.14.2. 硬换行与软换行
在富文本模式下,Emacs 会区分两种不同的换行符: / hard newlines硬换行/ 和 soft newlines 软换行 。你也可以在其他缓冲区中,通过执行 M-x use-hard-newlines 命令启用或关闭该功能。
硬换行用于分隔段落,或在所有需要强制换行的位置使用 —— 无论文本如何自动折行,硬换行会始终保留;软换行则专门用于文本的自动折行排版。按下 RET (newline)和 C-o (open-line) 插入的是硬换行。各类折行排版命令(包括自动折行模式,详见《自动折行模式》章节)仅会插入软换行,且仅删除软换行,不会改动硬换行。
因此,在富文本模式下编辑时, 不要 在已折行排版的段落中间使用 RET 回车键或 C-o 进行换行。此类场景应使用自动折行模式,或手动执行折行排版命令(详见《手动折行排版命令》章节)。仅在需要始终保留换行的位置使用 RET 回车键或 C-o ,例如编辑表格和列表时。对于这类包含强制换行的文本行,你还可以将其对齐方式设置为不折行(详见《富文本中的对齐方式》章节)。
27.14.3. 格式信息编辑
修改文本属性最简便的方式是使用 'Text Properties 文本属性' 菜单。你可以通过菜单栏中的 'Edit' 菜单打开该菜单(详见菜单栏章节),也可通过鼠标快捷键 C-mouse-2 (详见菜单的鼠标点击操作章节)。以下列出文本属性菜单中的部分命令(这些命令也可通过 M-x 调用执行):
- Remove Face Properties
- 清除选区中的显示样式属性 (
facemenu-remove-face-props) 。 - Remove Text Properties
- 清除选区中的全部文本属性,包括显示样式属性 (
facemenu-remove-all) 。 - Describe Properties
- 列出光标后方字符的所有文本属性及其他相关信息 (
describe-text-properties) 。 - Display Faces
- 展示已定义的所有显示样式列表 (
list-faces-display) ,详见文本显示样式章节。 - Display Colors
- 展示已定义的所有颜色列表 (
list-colors-display) ,详见显示样式的颜色设置章节。
该菜单中的其他功能项将在后续小节中介绍。
27.14.4. 富文本中的外观样式
下述命令可用于添加或移除显示样式(参见「文本显示样式」章节)。若标记处于激活状态,所有命令均作用于区域内的文本;若标记未激活,则作用于下一个自插入字符。若带前缀参数,即便区域处于激活状态,所有命令也仅作用于下一个自插入字符。
M-o d- 清除所有显示样式属性 (
facemenu-set-default) 。 M-o b- 应用粗体显示样式 (
facemenu-set-bold) 。 M-o i- 应用斜体显示样式 (
facemenu-set-italic) 。 M-o l- 应用粗斜体显示样式 (
facemenu-set-bold-italic) 。 M-o u- 应用下划线显示样式 (
facemenu-set-underline) 。 M-o o 样式名 RET- 应用指定名称的显示样式 (
facemenu-set-face) 。 M-x facemenu-set-foreground- 提示输入颜色(参见「显示样式的颜色设置」章节),并将其设为前景色。
M-x facemenu-set-background- 提示输入颜色,并将其设为背景色。
上述命令也可通过「Text Properties」菜单调用。
自插入字符通常会从缓冲区中前一个字符继承显示样式属性(及绝大多数其他文本属性)。若通过上述任一命令为下一个自插入字符指定了显示样式,该字符将不再继承前一个字符的显示样式属性,但仍会继承其他文本属性。
富文本模式额外定义了两种显示样式:摘录样式(excerpt)和等宽样式(fixed),二者对应 'text/enriched' 文件格式中使用的编码。摘录样式适用于引用文本,默认显示效果与斜体一致;等宽样式用于指定等宽文本,默认显示效果与 bold粗体 一致。
27.14.5. 富文本中的缩进
在富文本模式下,你可为一个段落或段落的某一部分,设置不同数值的左、右页边距缩进。这些页边距设置也会作用于 M-q 等自动换行命令(参见「文本自动换行」章节)。
「Text Properties」菜单的「indentation缩进」子菜单中,提供了用于设置缩进的相关命令:
增加左缩进- 将选中区域的左缩进增加 4 列 (
increase-left-margin) 。在富文本模式下,该命令也可通过快捷键C-x TAB调用;若为其指定数字参数,参数值即为左缩进需增加的列数(负参数表示减少对应列数的左缩进)。 Indent Less- 将选中区域的左缩进减少 4 列。
Indent Right More- 将右页边距缩进增加 4 列,使文本显示区域变窄。
Indent Right Less- 将右页边距的缩进减少 4 列。
变量 standard-indent 用于定义上述缩进命令每次增加或减少的列数,默认值为 4。和常规模式一致,富文本模式的默认右页边距由变量 fill-column 控制。
你也可通过快捷键 C-c [ (set-left-margin) 和 C-c ] (set-right-margin) 分别设置左、右页边距。可为这两个快捷键指定数字参数以直接定义页边距宽度;若未指定参数,命令会通过迷你缓冲区提示你输入数值。
若设置了自动换行前缀,其会在段落指定缩进的基础上生效:快捷键 C-x . 设置自动换行前缀的新值时,不会将指定缩进的空白字符包含在内;且自动换行命令会在每行的缩进之后,匹配对应的自动换行前缀(参见「自动换行前缀」章节)。
27.14.6. 富文本中的对齐方式
在富文本模式下,可使用以下命令为文本填充指定各类对齐样式。这些命令将作用于光标所在的段落;若选区处于激活状态,则作用于所有与选区重叠的段落。
M-j l- 向左边距对齐 (
set-justification-left) 。 M-j r- 向右边距对齐 (
set-justification-right) 。 M-j b- 左右边距两端对齐,通过在行中间插入空格实现该效果 (
set-justification-full) 。 M-j cM-S- 在左右边距之间居中对齐 (
set-justification-center) 。 M-j u- 完全关闭文本填充功能 (
set-justification-none) 。开启此设置后,填充类命令对文本将不再生效,但仍可对左边距进行缩进操作。
你也可通过「Text Properties 」菜单中的「justification对齐方式」子菜单来指定对齐样式。默认对齐样式由缓冲区级变量 default-justification 定义,其取值需为以下符号之一: left (左对齐)、 right (右对齐)、 full (两端对齐)、 center (居中对齐)或 none (关闭填充),各符号含义与上述对应命令一致。
27.14.7. 设置其他文本属性
'Text Properties' 菜单的 'Special Properties特殊属性' 子菜单中,包含用于添加或移除四种其他文本属性的选项,分别为:read-only只读(禁止修改文本)、invisible不可见(隐藏文本)、intangible不可触(禁止将光标移至文本内)以及 charset字符集(对选择合适的字体显示字符至关重要)。 'Remove Special移除特殊属性' 菜单项可将选区文本中的所有这类特殊属性全部清除。
invisible不可见和 intangible不可触属性不会被保存。
富文本模式还支持保存和恢复 display显示属性(参见《Emacs Lisp 参考手册》中的显示属性章节),这类属性会影响文本在屏幕上的显示方式,也支持显示来自缓冲区文本以外来源的图片和字符串。显示属性还支持在处理该属性以完成文本显示的过程中,执行任意的 Lisp 表达式,从而提供了一种根据仅在显示时才能确定的条件,动态调整文本显示效果的方法。由于执行任意 Lisp 表达式会使 Emacs 面临潜在的攻击风险 —— 尤其是当富文本的来源并非 Emacs 内部、甚至并非你的系统内部时(例如,通过电子邮件接收的富文本),因此富文本模式中默认禁用该功能。你可通过将变量 enriched-allow-eval-in-display-props 自定义设置为非空值,来启用此功能。
27.15. 文本表格编辑
表格包提供了一系列命令,可便捷编辑基于文本的表格。此类表格的示例如下:
+-----------------+--------------------------------+-----------------+ | Command | Description | Key Binding | +-----------------+--------------------------------+-----------------+ | forward-char |Move point right N characters | C-f | | |(left if N is negative). | | | | | | +-----------------+--------------------------------+-----------------+ | backward-char |Move point left N characters | C-b | | |(right if N is negative). | | | | | | +-----------------+--------------------------------+-----------------+
当 Emacs 将一段文本识别为表格时(参见表格识别章节),若表格单元格中的内容超出单元格可容纳范围,编辑各单元格内容的操作将自动调整表格尺寸。你可使用后续章节中定义的命令,进行表格布局的导航与编辑操作。
键入 M-x table-fixed-width-mode 可切换表格自动调整尺寸的功能开关。
27.15.1. 什么是文本表格?
基于文本的 talbe 表格 由一个被划分为若干 cells 单元格 的矩形文本区域构成。不计边框线的情况下,每个单元格的宽度和高度至少为一个字符。单元格可进一步拆分为多个子单元格,但所有单元格之间不得重叠。
单元格的边框线由三种特殊字符绘制,相关字符由以下变量指定:
table-cell-vertical-char- 用于绘制竖线的字符,默认值为 ‘|’ 。
table-cell-horizontal-chars- 用于绘制横线的字符,默认值为 “"-="” 。
table-cell-intersection-char- 用于绘制横竖线交叉处的字符,默认值为 ‘+’ 。
以下为无效表格的示例:
+-----+ +--+ +-++--+ | | | | | || | | | | | | || | +--+ | +--+--+ +-++--+ | | | | | | +-++--+ | | | | | | | || | +--+--+ +--+--+ +-++--+ a b c
上述示例从左至右分别违反了以下规则:
- 不允许出现单元格重叠或非矩形的单元格;
- 表格边框必须为矩形;
- 单元格的宽度和高度必须至少为一个字符。
27.15.2. 创建表格
若要从头创建一个基于文本的表格,键入 M-x table-insert 即可。该命令会依次提示你输入表格的columns列数、rows行数、width单元格宽度和 height单元格高度。 单元格的宽度和高度均不含边框 ;其取值既可以是单个整数(表示所有单元格使用相同的宽度 / 高度),也可以是一组由空格或逗号分隔的整数(用于指定每一列 或 每一行单元格的单独宽度 或 高度,列按从左到右的顺序、行按从上到下的顺序对应)。完成设置后,表格将插入到光标所在位置。
通过 M-x table-insert 插入的表格带有特殊的文本属性,正是这些属性让 Emacs 将其识别为特殊的基于文本的表格进行处理。若将缓冲区保存至文件并在后续重新打开,这些属性将会丢失,Emacs 会将该表格视作普通文本。如需将其恢复为表格形式,可参见下一节内容。
27.15.3. 表格识别
缓冲区中已存在的基于文本的表格,若未带有 M-x table-insert 命令添加的特殊文本属性,将不会被 Emacs 当作表格特殊处理。如需为其添加该类属性,可键入 M-x table-recognize 。该命令会扫描当前缓冲区,识别有效的表格单元格并为其应用相关文本属性。反之,键入 M-x table-unrecognize 可取消当前缓冲区中所有表格的识别状态,移除其特殊文本属性并将表格还原为纯文本。
你也可使用以下命令,对表格进行选择性的识别或取消识别:
M-x table-recognize-region- 识别当前选区范围内的表格
M-x table-unrecognize-region- 取消当前选区范围内的表格识别
M-x table-recognize-table- 识别光标所在位置的表格并激活该表格
M-x table-unrecognize-table- 取消光标所在位置表格的激活状态
M-x table-recognize-cell- 识别光标所在位置的单元格并激活该单元格
M-x table-unrecognize-cell- 取消光标所在位置单元格的激活状态
关于表格识别的另一种方法,可参见纯文本与表格的格式转换章节。
如果是org-mode模式文件,键入 C-c ' ,在内部可使用快捷键操作表格。
27.15.4. 表格单元格操作命令
M-x table-forward-cell 和 M-x table-backward-cell 命令可将光标从当前单元格移动至相邻单元格,移动顺序为循环式:当光标处于表格最后一个单元格时,执行 M-x table-forward-cell 会跳至第一个单元格;同理,光标处于第一个单元格时,执行 M-x table-backward-cell 会跳至最后一个单元格。
M-x table-span-cell (C-*) 命令会先提示选择合并方向 —— right向右、left向左、above向上或 below向下,随后将当前单元格与对应方向的相邻单元格合并。若该合并操作会导致单元格布局无效,此命令将抛出错误提示。
M-x table-split-cell 命令可对当前单元格进行 vertically垂直或 horizontally水平拆分,拆分方向需在迷你缓冲区中指定。若需直接按指定方向拆分,可使用 M-x table-split-cell-vertically (C–-) (垂直拆分)和 M-x table-split-cell-horizontally (C-|) (水平拆分)命令。执行垂直拆分时,原单元格的内容会自动分配至上下两个新单元格h;执行水平拆分时,若原单元格非空,命令会提示选择内容拆分方式,可选方式包括:'split' (从光标位置拆分内容)、'left' (将所有内容放入左侧新单元格)、 'right' (将所有内容放入右侧新单元格)。
以下命令用于 enlarge放大或 shrink缩小单元格,默认情况下每次调整 1 行或列的尺寸;若为命令指定数字参数,可自定义调整的行/列数。
M-x table-heighten-cell- 垂直放大当前单元格 (
C-}) M-x table-shorten-cell- 垂直缩小当前单元格 (
C-{) M-x table-widen-cell- 水平放大当前单元格 (
C-<) M-x table-narrow-cell- 水平缩小当前单元格 (
C->)
27.15.5. 单元格对齐方式
M-x table-justify (C-:) 命令可对基于文本的表格中一个或多个单元格设置 justification对齐方式 。对齐方式用于定义单元格内的文本相对单元格边缘的排布规则,表格中的每个单元格可单独设置对齐方式。
执行 M-x table-justify 命令后,会先提示选择对齐作用范围,可选值为 'cell' (仅当前单元格)、 'column' (当前表格列的所有单元格)和 'row' (当前表格行的所有单元格);随后会提示选择对齐样式,可选值为left左对齐、center居中对齐、right右对齐、top顶端对齐、middle中部对齐、bootom底端对齐,或none(表示不设置垂直对齐)。
水平对齐样式与垂直对齐样式为独立设置,且两种样式可同时生效。例如,可连续执行两次 M-x table-justify 命令,一次设置右对齐、一次设置底端对齐,即可将单元格内容调整为右下角对齐。
对齐样式会以文本属性的形式存储在缓冲区中,关闭缓冲区或退出 Emacs 后,该属性将会丢失。不过表格识别类命令(如 M-x table-recognize ,参见表格识别章节)会通过分析单元格内容,尝试识别并重新为每个单元格应用原有对齐样式。若要禁用此功能,可将变量 table-detect-cell-alignment 的值设为 nil 。
27.15.6. 表格行与列操作
M-x table-insert-row 命令会在当前 表格行上方 插入一行单元格,原当前行及光标位置会下移至新行之后。若要在表格最底部的最后一行下方插入新行,需将光标置于表格外、表格下边缘的正下方,再执行此命令。为该命令添加数字前缀参数,可一次性插入多行。
同理, M-x table-insert-column 命令会在当前 表格列左侧 插入一列单元格。若要在表格最右侧的最后一列右侧插入新列,需将光标置于表格外、表格最右侧列的右边,再执行此命令。数字前缀参数可指定一次性插入的列数。
M-x table-delete-column 命令用于删除光标所在位置的整列单元格, M-x table-delete-row 命令则用于删除光标所在位置的整行单元格。为这两个命令添加数字前缀参数,可指定一次性删除的列数或行数。
27.15.7. 纯文本与表格的相互转换
M-x table-capture 命令可提取选区内的纯文本并将其转换为表格。与 M-x table-recognize 命令不同(参见表格识别章节),待转换的原始文本无需具备表格的外观形式,仅需拥有符合逻辑的类表格结构即可。
例如,现有以下一组数字,分为三行排列,列之间以逗号分隔:
1, 2, 3, 4 5, 6, 7, 8 , 9, 10
对这段文本执行 M-x table-capture 命令,将生成如下表格:
- Column delimiter regexp: ,
- Row delimiter regexp: 换行
C-q C-j
+-----+-----+-----+-----+ |1 |2 |3 |4 | +-----+-----+-----+-----+ |5 |6 |7 |8 | +-----+-----+-----+-----+ | |9 |10 | | +-----+-----+-----+-----+
M-x table-release 命令的作用则与之相反:它会将表格还原为纯文本,并移除所有单元格边框。
这一组命令的一个实际应用场景是按版式编辑文本。例如有以下三段文本:
table-capture is a powerful command.
Here are some things it can do:
Parse Cell Items Using row and column delimiter regexps,
it parses the specified text area and
extracts cell items into a table.
将包含上述文本的选区执行 M-x table-capture 命令,并将行列分隔符正则表达式均设为空字符串,会生成一个仅含单个单元格的表格,如下所示:
+----------------------------------------------------------+ |table-capture is a powerful command. | |Here are some things it can do: | | | |Parse Cell Items Using row and column delimiter regexps,| | it parses the specified text area and | | extracts cell items into a table. | +----------------------------------------------------------+
随后我们可使用单元格拆分相关命令(参见表格单元格操作命令章节)对该表格进行拆分,让每一段文本各占一个单元格:
- 若为org mode文件,需要进入编辑模式操作:
C-c ' - 光标在第3行,空白单元格内。按
M-x table-split-cell-vertically(C–) 垂直分割单元格 - 光标在 Items 后空白处,
M-x table-split-cell-horizontally(C-|) 水平分割单元格- 按split分割,按光标位置自动将当前单元格分割。
- 注意光标这里与下文本宽度一样,有利切分
- 光标在最后一行内,按
M-x table-shorten-cell(C-{) 垂直收缩当前单元格
+----------------------------------------------------------+ |table-capture is a powerful command. | |Here are some things it can do: | +-----------------+----------------------------------------+ |Parse Cell Items | Using row and column delimiter regexps,| | | it parses the specified text area and | | | extracts cell items into a table. | +-----------------+----------------------------------------+
此时每个单元格均可独立编辑,且不会影响其他单元格的版式布局。编辑完成后,执行 M-x table-release 命令即可将表格重新转换为纯文本。
27.15.8. 表格杂项功能
table-query-dimension 命令会反馈光标所在位置的表格及单元格布局信息,以下是该命令的输出示例:
Cell: (21w, 6h), Table: (67w, 16h), Dim: (2c, 3r), Total Cells: 5
该输出表示:当前单元格宽度为 21 个字符、高度为 6 行;表格整体宽度为 67 个字符、高度为 16 行,包含 2 列 3 行,表格的单元格总数为 5 个。
M-x table-insert-sequence 命令会遍历表格的所有单元格,并在遍历过程中为每个单元格插入一组序列化的文本字符串。执行该命令时,需先指定序列的基础字符串,命令会通过对基础字符串进行 自增运算 生成后续序列 —— 若基础字符串以数字结尾,则按数字规则自增;否则按 ASCII 字符顺序自增。除基础字符串外,该命令还会依次提示输入序列元素数量、自增步长、单元格间隔,以及文本在单元格内的对齐方式。
M-x table-generate-source 命令可生成适配指定标记语言的表格格式。执行该命令时,需先指定目标标记语言(仅支持 html、latex、cals、wiki、mediawiki),再指定用于存放生成结果的目标缓冲区,以及表格标题,完成设置后命令会将生成的表格插入指定缓冲区。默认的目标缓冲区名称为 table.lang ,其中 lang 为你所指定的标记语言名称。
27.16. 双列编辑
two-column mode双列模式可让你便捷地编辑两列并排的文本,该模式会启用两个并排的窗口,每个窗口显示独立的缓冲区。进入双列模式共有三种方式:
F2 2或C-x 6 2- 以当前缓冲区作为左侧栏,右侧栏启用一个基于当前缓冲区名称命名的缓冲区 (
2C-two-columns) ,进入双列模式。若右侧缓冲区尚未创建,则初始为空白状态,当前缓冲区的内容不会发生任何改变。此命令适用于当前缓冲区为空白、或仅包含单列文本,且你需要新增另一列的场景。 F2 s或C-x 6 s将包含双列文本的当前缓冲区拆分为两个缓冲区,并将其并排显示 (
2C-split) 。原当前缓冲区成为左侧缓冲区,右侧栏的文本会被移至右侧缓冲区,拆分位置由当前光标所在列决定。拆分操作从光标所在行开始,直至缓冲区末尾。此命令适用于当前缓冲区已包含双列文本,且你需要临时将两列分离编辑的场景。
F2 b buffer RET或C-x 6 b buffer RET- 以当前缓冲区为左侧缓冲区、指定缓冲区为右侧缓冲区,进入双列模式 (
2C-associate-buffer) 。
执行 F2 s 或 C-x 6 s 时,命令会自动识别 列分隔符 —— 即每行中位于两列文本之间的字符串。你可通过为 F2 s 指定 数字参数 来定义分隔符的宽度:光标左侧对应数量的字符将被视作列分隔符。默认分隔符宽度为 1,即光标左侧的单个字符为列分隔符。
若某行的分隔符处于正确位置, F2 s 会将分隔符后的文本移至右侧缓冲区,并删除原分隔符;若某行未在正确位置包含列分隔符,则该行不会被拆分,仍保留在左侧缓冲区,且右侧缓冲区会在对应位置插入一空行。(双列模式下,若要编写跨两列的文本,可按此方法操作:在左侧缓冲区编写文本,在右侧缓冲区对应位置保留空行即可。)
命令 F2 RET 或 C-x 6 RET (2C-newline) ,会在两个缓冲区的对应位置各插入一个换行符。在拆分后的双缓冲区中编辑双列文本时,这是为文本新增一行最便捷的方式。
当你完成对两个缓冲区的编辑后,可执行 F2 1 或 C-x 6 1 (2C-merge) 合并两个缓冲区,该命令会将右侧缓冲区的文本作为第二列,合并至左侧缓冲区中。若需再次进入双列编辑模式,重新执行 F2 s 即可。
执行 F2 d 或 C-x 6 d 可解除两个缓冲区的关联关系,各自保留当前内容 (2C-dissociate) 。若执行该命令时,非当前的另一个缓冲区为空白状态,则该缓冲区会被直接删除。
28. 程序编辑
本章介绍 Emacs 中用于便捷编辑程序代码的各类功能,这些功能可实现的操作包括:
- 查找或跳转至顶层定义(参见「顶层定义(Defuns)」章节)
- 遵循编程语言的通用缩进规范进行缩进(参见「程序代码的缩进」章节)
- 匹配括号(参见「括号编辑相关命令」章节)
- 插入、删除或对齐注释(参见「注释的操作」章节)
- 高亮程序语法(参见「字体锁定模式」章节)
28.1. 编程语言相关主模式
Emacs 为多种编程语言提供了专用的主模式(参见「主模式」章节)。一款编程语言主模式通常会定义该语言的表达式语法、通用缩进规则、语法高亮的实现方式,以及函数定义的起止位置查找方法,通常还会集成程序编译和调试相关功能。各语言的主模式均以对应语言命名,例如 C 语言的主模式为 c-mode 。
Emacs 支持的编程语言主模式涵盖:Lisp、Scheme、基于 Scheme 的 DSSSL 表达式语言、Ada、汇编语言(ASM)、AWK、C、C++、C#、Elixir、Fortran、Icon、IDL(CORBA)、HEEx、IDLWAVE、Java、JavaScript、Lua、M4、Makefile、Metafont(TeX 的字体制作配套语言)、Modula2、Object Pascal、Objective-C、Octave、Pascal、Perl、PHP、Pike、PostScript、Prolog、Python、Ruby、Simula、SQL、Tcl、TypeScript、Verilog、VHDL。Perl 语言推荐使用的主模式为 CPerl mode。此外,Emacs 还为 GNU 和 Unix 通用 Shell 的脚本语言、MS-DOS/MS-Windows 的 BAT 批处理文件、JSON、DNS 主配置文件、层叠样式表(CSS)、Dockerfile、CMake 配置文件,以及各类系统配置文件提供了对应的主模式。
理想情况下,Emacs 应为所有可能需要编辑的编程语言都提供对应的主模式。若你常用的语言暂无内置主模式,该模式可能存在于 Emacs 官方分发包之外的扩展包中(参见「Emacs Lisp 扩展包」章节),你也可以自行开发并贡献对应的主模式。
若 Emacs 编译时集成了 'tree-sitter' 库,还会提供一系列基于该库的可选编辑模式。这类模式借助 'tree-sitter' 的增量解析能力实现,模式名中均包含 '-ts-',例如 C 语言的 c-ts-mode 、Python 语言的 python-ts-mode 等。
编程语言主模式可通过 Eglot 扩展包提供的功能,调用语言服务器的各类服务。Eglot 扩展包实现了语言服务器协议(LSP),让 Emacs 能够接收特定语言的专属信息与服务,从而丰富并扩展源代码的编辑能力。相关详情参见《Eglot:Emacs LSP 客户端》手册中的「Eglot 功能」章节。
在绝大多数编程语言中,代码的缩进需随行调整,以体现程序的结构层次。因此,在多数编程语言主模式下,按下 TAB 键会自动更新当前行的缩进格式(参见「程序代码的缩进」章节)。此外,删除键( DEL )通常绑定为 backward-delete-char-untabify 函数,该函数会向后删除字符,并将制表符( Tab )视作等效数量的空格处理;如此一来,你在删除一列缩进时,无需顾虑该空白区域是由空格还是制表符构成。
进入某一编程语言主模式时,Emacs 会先执行钩子变量 prog-mode-hook 中指定的自定义 Lisp 函数,再执行该模式自身的模式钩子中定义的函数(参见「主模式」章节)。例如,进入 C 模式时,会依次执行prog-mode-hook和c-mode-hook两个钩子中的函数。有关钩子的详细说明,参见「钩子」章节。
Ada、C/C++/Objective-C/Java/CORBA IDL/Pike/AWK、Octave、VHDL、IDLWAVE 等语言的主模式均配有独立的使用手册,分别参见《Ada 模式手册》中的「Ada 模式」、《CC 模式手册》中的「CC 模式」,以及《IDLWAVE 用户手册》中的「IDLWAVE」章节。
28.2. 顶层定义(或函数定义)
在 Emacs 中,缓冲区里顶层的主要定义(如函数)被称为函数定义块( defun )。该名称源自 Lisp 语言,但在 Emacs 中,这一术语适用于所有编程语言。
28.2.1. 左边距约定
许多编程语言模式历来遵循一项约定:凡出现在左侧边距的左括号或左大括号,均视为顶层定义(函数定义块)的起始位置。因此默认情况下,用于定位函数定义块起始位置的各类命令,会将此类分隔符视作该位置的标识。
若需取消该约定,可将用户选项 open-paren-in-column-0-is-defun-start 设为 nil 。当该选项设为 t (默认值)时,定位函数定义块起始位置的命令,会在第 0 列的左括号 / 左大括号处停止(注释或字符串内的括号除外);设为 nil 时,程序会通过检索最外层的括号来定位函数定义块。由于 Emacs 底层子程序已不再依赖该约定,通常无需修改此选项的默认值。
28.2.2. 按函数定义移动
下述命令可基于顶层主要定义(亦称作函数定义块 defuns )移动光标位置,或设定选区范围。
C-M-a- 移至当前或上一个函数定义块的起始位置 (
beginning-of-defun) 。 C-M-e- 移至当前或下一个函数定义块的结束位置 (
end-of-defun) 。 C-M-h- 将当前或下一个函数定义块整体选中为选区 (
mark-defun) 。
移至当前函数定义块起始和结束位置的命令分别为 C-M-a (beginning-of-defun) 和 C-M-e (end-of-defun) 。若重复执行其中任一命令,或传入正数值参数,每一次重复都会沿当前移动方向跳至下一个函数定义块。
为 C-M-a 传入负数值参数 −n 时,会向前跳 n 次,定位至第 n 个函数定义块的起始位置。该位置与为 C-M-e 传入参数 n 后定位的位置并非完全一致 —— 当前函数定义块的结束位置,通常与下一个函数定义块的起始位置之间存在间隔(可能包含空白字符、注释语句,甚至声明代码)。同理,为 C-M-e 传入负数值参数时,会向后跳至某一函数定义块的结束位置,该位置也与为 C-M-a 传入正数值参数后定位的位置略有差异。
若要对当前函数定义块执行操作,可使用 C-M-h (mark-defun) :该命令会将标记点设至当前函数定义块的结束位置,同时将光标移至其起始位置(详见《标记文本对象的相关命令》)。这是快速选中并删除函数定义块,以便将其移至文件其他位置的最简便方法。若该函数定义块前方紧邻注释语句(二者之间无空行分隔),注释也会被一并选中。若光标处于两个函数定义块之间时执行该命令,会选中下一个函数定义块;若选区标记已处于激活状态时执行该命令,会将选区的结束位置向后扩展,纳入再下一个函数定义块。传入前缀参数时,该命令会选中对应数量的函数定义块,或按参数值扩展选区范围;传入负前缀参数时,会向相反方向选中函数定义块,且后续执行 mark-defun 命令时,选区的选择方向也会随之改变。
在 C 语言模式下, C-M-h 会调用函数 c-mark-function ,其功能与 mark-defun 基本一致,唯一区别在于:该函数会将选中范围向前覆盖至参数声明、函数名和返回数据类型的起始位置,确保整个 C 语言函数都被包含在选区内。这是主模式对标准按键绑定进行适配的典型示例,让按键能以更贴合特定编程语言的方式,实现其标准功能。其他主模式也可能为此,替换上述全部或部分按键的绑定功能。
部分编程语言支持嵌套式函数定义块,即一个函数定义块(如函数、方法或类)可定义在另一个函数定义块的内部(即作为其函数体的一部分)。上述命令在默认情况下,会定位至光标所在位置周边最内层的函数定义块的起始和结束位置。基于 tree-sitter 库的主模式可对该行为进行控制:若将变量 treesit-defun-tactic 的值设为 top-level ,这些函数定义块相关命令会转而定位至最外层的函数定义块。
28.2.3. 按句子移动
此类命令会基于代码单元(也称作 sentences语句块 )移动光标位置或设置选区。尽管语句块的概念通常在编写自然语言时提及,但 Emacs 也可使用相同命令在编程语言的特定语法结构间移动(参见「语句块」、「按定义块移动」)。在编程语言中,一个语句块通常是完整的语法结构,规模小于定义块、大于 s表达式 (参见《Emacs Lisp 参考手册》中的「列表移动」)。具体何种结构属于语句块依编程语言而定,一般为完整的语句,例如变量定义与初始化语句、条件判断语句等。
C 语言中的语句块示例:
int x = 5;
JavaScript 语言中的语句块示例:
const thing = () => console.log("Hi"); const foo = [1] == '1' ? "No way" : "...";
M-a- 移动到当前或前一个语句块的开头 (
backward-sentence) 。 M-e- 移动到当前或后一个语句块的结尾 (
forward-sentence) 。
移动到当前语句块开头和结尾的命令分别为 M-a (backward-sentence) 和 M-e (forward-sentence) 。重复执行其中任一命令,或传入正数值参数,每一次执行都会沿当前移动方向跳至下一个语句块。
给 M-a 传入负数值参数 −n 时,会向前跳 n 次至对应语句块的结尾;同理,给 M-e 传入负数值参数时,会向后跳至对应语句块的开头。
28.2.5. 函数定位模式
Which Function mode函数定位模式是一种全局次要模式(参见「次要模式」相关内容),可在模式行或标题行中显示当前所在的函数名称,且会随你在缓冲区中移动光标实时更新显示内容。
启用或关闭函数定位模式,可使用命令 M-x which-function-mode 。该模式为全局次要模式,默认情况下,会在所有支持该功能的主模式中生效(即所有兼容索引菜单的主模式)。若需限定其生效的主模式范围,可将变量 which-func-modes 的取值从 t (表示支持所有可用主模式)修改为指定的主模式名称列表。
函数定位模式默认在模式行中显示当前函数名称。自定义变量 which-func-display 的取值,设为 header 则仅在标题行显示,设为 mode 则仅在模式行显示,设为 mode-and-header 则在模式行和标题行同时显示。
28.3. 程序缩进
保持程序代码缩进规范的最佳方式,是在修改代码时通过 Emacs 对其重新缩进。Emacs 提供了对应的缩进命令,可对单行代码、指定行数的代码,或是单个括号嵌套结构内的所有代码进行缩进处理。
关于缩进的通用说明,可参见「缩进」相关章节。本节将介绍编程语言模式下特有的缩进功能。
Emacs 还在 pp 包中提供了 Lisp 代码美化打印工具,该工具可对 Lisp 对象重新格式化并生成规范美观的缩进。相关详情可参见《Emacs Lisp 参考手册》中的 pp 工具章节。
28.3.1. 基本程序缩进命令
TAB- 调整当前行的缩进格式 (
indent-for-tab-command) 。 RET- 插入换行符,随后调整新行的缩进格式 (
newline) 。
缩进操作的基础命令为 TAB 键 (indent-for-tab-command) ,该命令的通用说明见「缩进」章节。在编程语言模式下, TAB 键会根据前序代码行的缩进格式和语法内容,为当前行自动缩进;若 选区处于激活状态 , TAB 键会为选区内的所有行统一缩进,而非仅处理当前行。
回车键 RET (newline) 的功能等同于先按 C-j 再按 TAB 键:先插入一个换行符,再为新生成的行自动调整缩进。
当为 括号嵌套结构内 的起始行做缩进时,Emacs 通常会将该行的起始位置对齐至结构内的前一行,或对齐至左括号后的文本位置。若你为这类行手动设置了非标准的缩进格式(例如出于排版美观的需求),其下方的代码行会自动沿用该缩进格式。
多数编程语言模式的缩进命令均遵循一个规则: 左边界处 的左括号、左大括号或其他左分隔符,会被认定为函数的起始位置。若你编辑的代码违背了这一规则 —— 即便这些分隔符出现在字符串或注释中 —— 你需要将变量 open-paren-in-column-0-is-defun-start 设为 nil ,才能让缩进功能正常工作。相关说明见「左边界约定」章节。
28.3.2. 多行缩进
有时你可能需要一次性为多行代码重新缩进,一种方法是使用标记:当标记激活且选区非空时,按下 TAB 键会为选区内的所有行统一缩进。此外,命令 C-M-\ (indent-region) 同样能为选区内所有行缩进,无论标记是否处于激活状态(参见「缩进命令」相关内容)。
Emacs 还提供了以下命令,用于对大段代码进行缩进处理:
C-M-q- 重新缩进单个括号嵌套结构内的所有代码行
M-q- 填充某个定义块内的单个段落,或重新缩进该定义块内的所有代码行
C-u TAB- 整体平移单个括号嵌套结构的缩进位置,使结构的首行缩进格式合规
M-x indent-code-rigidly- 整体平移选区内所有代码行的缩进位置,不改动注释和字符串内的起始行
若要为单个括号嵌套结构的内容重新缩进,将光标移至该结构起始位置前,按下 C-M-q 即可。该操作仅调整结构内代码的相对缩进,不会改变结构的整体缩进(即结构起始行的缩进格式)。 C-M-q 调用的具体函数由主模式决定:Lisp 模式下为 indent-pp-sexp ,C 模式下为 c-indent-exp 等。若需同时修正结构的整体缩进,可先按下 TAB 键。
若要为光标所在的整个定义块重新缩进,按下 M-q (prog-fill-reindent-defun) 即可。若光标处于注释或字符串内,该命令会改为对注释或字符串进行填充和缩进。注释、字符串或定义块的具体判定规则由主模式决定:定义块的边界由变量 beginning-of-defun-function 和 end-of-defun-function 定义(参见《Emacs Lisp 参考手册》中的「列表移动」章节),填充机制则由变量 fill-paragraph-function 定义(参见《Emacs Lisp 参考手册》中的「文本填充」章节)。
若你希望保留括号嵌套结构内的相对缩进,仅修正其首行的缩进格式,将光标移至该首行并按下 C-u TAB 即可。在 Lisp、C 及其他部分主模式下,带数字参数的 TAB 键会先按常规方式为当前行重新缩进,再将以当前行为起始的括号嵌套结构内所有行,按相同缩进量同步调整。该命令设计较为智能,不会改动字符串内的起始行;在 C 模式下,也不会改动 C 预处理器行,但会为其关联的所有续行重新缩进。
命令 M-x indent-code-rigidly 的功能与 indent-rigidly 类似,可整体平移选区内所有代码行的缩进位置(参见「缩进命令」相关内容)。该命令不会改动字符串内起始行的缩进,除非选区的起始位置也在该字符串内。前缀参数用于指定缩进的列数。
28.3.3. 定制 Lisp 缩进
Lisp 表达式的缩进格式可由表达式所调用的函数决定。对于每个 Lisp 函数,你既可以从多种预定义缩进格式中选择,也可以通过编写 Lisp 程序自定义任意缩进规则。
标准缩进格式定义如下:若表达式的第一个参数与表达式起始位置在同一行,表达式的第二行将缩进至该参数下方;若不在同一行,第二行则缩进至函数名下方。后续每一行,均缩进至前一个同级嵌套深度的行下方。
若将变量 lisp-indent-offset 设为非空值,该变量会覆盖表达式第二行的常规缩进格式,使该行始终比其所属列表多缩进 lisp-indent-offset 列。
部分函数会覆盖上述标准缩进格式。名称以 def 开头的函数,会将表达式的第二行视作代码体的起始行,该行的缩进位置会在表达式起始左括号的基础上,再额外缩进 lisp-body-indent 列。
你可通过为函数名设置 lisp-indent-function 属性,以多种方式为单个函数单独覆盖标准缩进格式。该操作通常结合 declare 结构为宏定义完成,相关详情参见《Emacs Lisp 参考手册》中的「宏定义」章节。
在 Emacs Lisp 中,列表通常会被当作函数式形式进行缩进,示例如下:
(setq foo '(bar zot
gazonk))
但如果在左括号后添加一个空格,会告知 Emacs 该列表为数据列表而非代码片段,此时 Emacs 会按如下格式缩进:
(setq foo '( bar zot
gazonk))
28.3.4. C 语言缩进命令
以下是 C 模式及相关模式中用于缩进的专属功能:
C-c C-q- 重新缩进当前顶层函数定义或聚合类型声明(CC 模式下调用
c-indent-defun函数,基于tree-sitter 的c-ts-mode模式下调用c-ts-mode-indent-defun函数)。 C-M-q- 重新缩进光标后方的平衡表达式(参见「带配对分隔符的表达式」相关内容,也称作 "sexp")中所有行。CC 模式下该命令调用
c-indent-exp函数;基于树解析的c-ts-mode模式下,会调用更通用的prog-indent-sexp函数。带前缀参数时,将抑制关于语法无效的警告信息。 TAB重新缩进当前行、已激活的选区,或以当前行为起始的代码块 (
c-indent-line-or-region) 。带前缀参数时,若当前行需要重新缩进,将对以当前行为起始的平衡表达式进行刚性重新缩进。若变量
c-tab-always-indent设为t,该命令始终仅对当前行重新缩进,无其他操作,此为默认设置。若该变量设为
nil,仅当光标位于左边界或当前行的缩进区域内时,命令才会对当前行重新缩进;否则将插入一个制表符(若变量indent-tabs-mode设为nil,则插入同等数量的空格)。若该变量设为
t和nil之外的任意值,命令会始终对当前行重新缩进;若光标位于注释或字符串内,还会额外插入一个制表符。
若要为当前整个缓冲区重新缩进,按下 C-x h C-M-\. 即可。该操作会先将整个缓冲区选为选区,再对选区执行重新缩进。
若要为当前代码块重新缩进,使用 C-M-u C-M-q 组合操作。该操作会先将光标移至代码块起始位置,再对整个代码块执行重新缩进。
28.3.5. 定制 C 语言缩进
C 模式及相关模式提供了灵活的缩进自定义机制。C 模式对源码行的缩进分为两步执行:第一步,根据行的内容和上下文,对该行进行语法分类;第二步,根据你选定的缩进风格,匹配该语法结构对应的缩进偏移量,并将该偏移量叠加至锚定语句的缩进值上。
C-c . 风格名 RET- 选定预定义的缩进风格(CC 模式下调用函数
c-set-style,基于 tree-sitter 的c-ts-mode 模式下调用 ~c-ts-mode-set-style)。
缩进风格是一组命名的自定义配置集合,可在 C 模式及相关模式中使用,关于风格的完整说明参见《CC 模式手册》的「缩进风格」章节。Emacs 内置了多种预定义缩进风格,包括 gnu 、 k&r 、 zbsd 、 stroustrup 、 linux 、 python 、 java 、 whitesmith 、 ellemtel 和 awk 。其中部分风格最初为特定语言设计,但所有风格均可用于这些模式支持的任意编程语言。若想查看某一风格的实际效果,可先选定该风格,再对部分代码重新缩进,例如在函数定义起始位置按下 C-M-q 。
为当前缓冲区选择缩进风格,可使用命令 C-c . ,并传入风格名作为参数(大小写不敏感)。该命令仅对当前缓冲区生效,且仅影响后续执行的缩进操作,不会对缓冲区中已有的代码重新缩进。若要按照新风格对整个缓冲区重新缩进,可按下 C-x h C-M-\ 。
使用 CC 模式时,可通过修改变量 c-default-style 为不同主模式指定默认缩进风格。该变量的值可以是风格名字符串,也可以是关联列表 —— 列表中的每个元素为一个主模式指定对应的缩进风格。示例如下:
(setq c-default-style '((java-mode . "java") (awk-mode . "awk") (other . "gnu")))
上述配置为 Java 模式和 AWK 模式显式指定了对应缩进风格,为其他类 C 模式设置了默认的 gnu 风格(该配置也是 Emacs 的默认设置)。该变量在你选定某一类 C 主模式时生效;因此,若为 Java 模式指定了新的默认风格,可在已打开的 Java 模式缓冲区中按下 M-x java-mode ,使新配置生效。
使用基于 tree-sitter 的 c-ts-mode 模式时,可通过自定义变量 c-ts-mode-indent-style 设置默认缩进风格。
gnu 风格是 GNU 项目推荐的 C 语言代码格式化风格,也是 Emacs 的默认缩进风格,旨在推广使用该推荐风格。
关于 C 模式及相关模式的缩进自定义更多细节,包括如何覆盖现有风格的部分配置、如何自定义全新缩进风格,可参见《CC 模式手册》中的「缩进引擎基础」和「自定义缩进」章节。
除了直接指定缩进风格,你也可在包含示例代码的缓冲区中按下 M-x c-guess ,让 Emacs 自动识别并匹配 代码的缩进风格;随后可使用 M-x c-guess-install ,将识别出的风格应用到其他缓冲区,具体细节参见《CC 模式手册》的「风格自动识别」章节。
28.4. 括号编辑相关命令
本节介绍利用程序中的括号结构实现的编辑命令与功能,也包含帮助你保持括号配对平衡的相关内容。
在介绍这类功能时,术语 “parenthesis括号” 也包含大括号、方括号,以及所有被定义为成对匹配的分隔符。主模式会通过语法表控制哪些分隔符为有效匹配分隔符(参见《Emacs Lisp 参考手册》中的「语法表」章节)。在 Lisp 模式下,仅圆括号参与匹配;在 C 模式下,这些命令同时适用于大括号和方括号。
你可以使用 M-x check-parens 命令,检查缓冲区中所有未配对的括号与未闭合的字符串引号。
配套内容:配对括号的表达式括号结构内的光标移动括号匹配
28.4.1. 配对括号的表达式
每种编程语言模式都有其自身对 配对括号表达式 的定义。这类表达式通常包含单个符号、数字、字符串常量,以及由成对匹配分隔符包裹的代码段。下述命令均针对配对括号表达式操作(在 Emacs 内部,这类表达式被称作 sexps14)。
核心快捷键与对应功能
C-M-f- 向前移动一个配对括号表达式 (
forward-sexp) 。 C-M-b- 向后移动一个配对括号表达式 (
backward-sexp) 。 C-M-k- 向前删除一个配对括号表达式 (
kill-sexp) 。 C-M-t- 交换表达式位置 (
transpose-sexps) 。 C-M-@C-M-SPC- 将标记点置于后续表达式末尾 (
mark-sexp) 。
命令详细说明
使用 C-M-f (forward-sexp) 可向前移动至下一个配对括号表达式的末尾。若光标后的第一个有效字符是左分隔符(例如 C 语言中的 ‘(’、‘[’、‘{’),该命令会直接跳至对应的右分隔符之后;若该字符为符号、字符串或数字的起始,命令则会移动至该内容的末尾。
C-M-b (backward-sexp) 与 C-M-f 功能相反,用于 向后 移动一个配对括号表达式。若目标表达式前带有前缀字符(如 Lisp 中的单引号、反引号和逗号),命令会同时跳过这些前缀字符。
给 C-M-f 或 C-M-b 加上数字参数,可按指定次数重复对应操作;若参数为负数,则向相反方向移动。在大多数模式下,这两个命令会将注释视作空白字符,直接跳过。需注意,这两个快捷键与按字符移动的 C-f=/ =C-b (参见「光标位置调整」)、按单词移动的 M-f / M-b (参见「单词操作」)用法类比,逻辑一致。
键入 C-M-k (kill-sexp) 可删除整个配对括号表达式,删除范围与 C-M-f 的移动范围完全一致。
C-M-t (transpose-sexps) 用于 交换光标前后相邻的两个配对括号表达式 ,与交换单个字符的 C-t 命令(参见「文本交换」)用法类比。给 C-M-t 加数字参数可作为重复次数:正数参数会将光标前的表达式向后交换过指定数量的后续表达式;负数参数则将光标前的表达式向前交换过指定数量的前方表达式;若参数为 0,并非无操作,而是交换 光标 / 标记点处及之后 的配对括号表达式。
若要结合 区域操作命令 处理配对括号表达式,可键入 C-M-SPC (mark-sexp) ,该命令会将标记点设为 C-M-f 移动的目标位置。当标记点处于激活状态时,重复调用此命令会将标记点再向后移动一个表达式,从而扩展选中区域;正 / 负数字参数可让标记点向前 / 向后移动指定数量的表达式。 C-M-@ 是 C-M-SPC 的别名,功能完全相同。更多相关命令说明可参见「文本对象标记命令」章节。
在 C 语言这类使用 infix operators中缀运算符 的语言中,Emacs 无法识别所有的配对括号表达式,因为同一位置可能存在多种表达式解析可能。例如,C 模式不会将 ‘foo + bar’ 视作单个表达式(尽管它在 C 语法中是一个完整表达式),而是将 ‘foo’ 和 ‘bar’ 分别识别为两个独立表达式,将+视作二者之间的标点符号;但对于 ‘(foo + bar)’ ,由于括号的包裹,C 模式会将其识别为单个完整的配对括号表达式。
28.4.2. 括号结构内的光标移动
下述命令可在括号分隔的代码块间移动(也适用于当前编辑语言中其他类型的配对分隔符)。这些命令会忽略字符串与注释内容(包括其中的所有括号),同时也会忽略经转义字符转义的括号。此类命令主要用于程序编辑,也可在编辑任何包含括号的文本时发挥作用;在 Emacs 内部它们被称 “list commands作列表命令”,原因是 Lisp 语言中这类括号分隔的代码块即为列表。
这些命令默认光标起始位置不在字符串或注释内部,若在字符串 / 注释内调用,执行结果可能不符合预期。
C-M-n- 向前跳过一个括号代码块 (
forward-list) 。 C-M-p- 向后跳过一个括号代码块 (
backward-list) 。 C-M-u- 向上跳转一级括号结构 (
backward-up-list) 。 C-M-d- 向下跳转一级括号结构 (
down-list) 。
列表命令 C-M-n (forward-list) 与 C-M-p (backward-list) ,可向前或向后跳过 1 个(或指定 n 个)括号代码块,且始终保持在当前的括号层级。
若要向上跳转 1 级(或指定 n 级)括号结构,可使用 C-M-u (backward-up-list) ,该命令会向后移动一个未配对的左分隔符,实现层级上移。带正数字参数表示重复跳转对应次数;带负数字参数则反转移动方向,向前并向上跳转对应层级。
若要向下跳转括号结构,可使用 C-M-d (down-list) 。在仅将左圆括号(作为起始分隔符的 Lisp 模式下,该命令的效果几乎等同于搜索左圆括号 ‘(’ ;给命令添加数字参数,可指定向下跳转的层级数。
28.4.3. 括号匹配
Emacs 提供了多项括号匹配功能,能让你轻松查看括号(或其他成对分隔符)的匹配方式与匹配状态。
当你键入可自动插入的闭合分隔符时,若对应的左分隔符处于当前屏幕范围内,Emacs 会短暂标记其位置;若左分隔符不在屏幕范围内,Emacs 则会在回显区显示其附近的部分文本。无论哪种情况,你都能明确当前闭合的是哪一个代码分组。若左、右分隔符出现不匹配的情况(如 ‘[x)’ ),回显区会弹出警告信息。
有三个变量用于控制括号匹配的显示效果:
blink-matching-paren:开启或关闭该功能,设为nil则禁用,默认值为t(开启)。设为jump时,会通过将光标短暂移至匹配的左分隔符处实现标记;设为jump-offscreen时,即便左分隔符不在屏幕范围内,光标也会跳转至对应位置。blink-matching-delay:设置匹配左分隔符的标记持续时间(单位:秒),可设为整数或浮点数,默认值为 1。blink-matching-paren-distance:指定向前搜索匹配左分隔符的字符范围,若超出该范围仍未找到匹配项,Emacs 会停止扫描且不显示任何标记,默认值为 102400。
括号显示模式(Show Paren mode) 是一种功能更强大的自动匹配次要模式。当光标位于左分隔符之前或右分隔符之后时,该分隔符、其匹配的分隔符,以及(可选的)二者之间的文本会被高亮显示。键入 M-x show-paren-mode 可在全局范围内切换该模式,键入 M-x show-paren-local-mode 则仅在当前缓冲区切换。
默认情况下,该模式会在所有用于编辑的缓冲区中开启,而在仅用于展示数据的缓冲区中禁用,此行为由用户选项 show-paren-predicate 控制。
若要自定义该模式,可键入 -M-x customize-group RET paren-showing= 。控制该模式运行的可自定义选项包括:
show-paren-highlight-openparen:控制当光标紧邻左分隔符前时,是否高亮该左分隔符(光标本身已标记其位置),默认值为非nil(开启)。show-paren-style:控制仅高亮一对分隔符,还是同时高亮二者之间的文本。可选值为parenthesis(仅显示匹配的分隔符)、expression(高亮括号包裹的整个表达式)、mixed(若匹配的分隔符在窗口内可见则高亮分隔符,否则高亮整个表达式)。show-paren-when-point-inside-paren:设为非nil时,即便光标位于括号内部,也会触发高亮,默认值为nil(关闭)。show-paren-when-point-in-periphery:设为非nil时,以下两种情况也会触发高亮:光标位于行首空白处,且该行首个或最后一个非空白字符为括号;光标位于行尾,且该行最后一个非空白字符为括号。show-paren-context-when-offscreen:设为非nil时,若光标位于右分隔符处且对应的左分隔符不在屏幕范围内,会在回显区显示相关上下文 —— 通常为包含左分隔符的整行,若左分隔符单独占一行,则上下文会包含其前一个非空行。
快速配对模式(Electric Pair mode) 是一款可快速插入成对匹配分隔符的次要模式,支持括号、大括号、方括号、引号等(具体的匹配分隔符由主模式决定)。键入 M-x electric-pair-mode 可在全局切换该模式,键入 M-x electric-pair-local-mode 仅在当前缓冲区切换。
开启该模式后,键入左分隔符时,Emacs 会同时插入对应的右分隔符,并将光标置于两个分隔符之间。多数情况下,你无需再手动键入闭合分隔符。若你仍手动键入了闭合分隔符,Emacs 会直接插入该字符;但如果光标紧邻同类型的右分隔符前,光标会直接跳至该右分隔符之后,且不会额外插入新的闭合分隔符。因此,键入「左分隔符→对应右分隔符」的操作,比「左分隔符→C-f」的操作更便捷。
当存在激活的选区时(参见「标记与选区」),Electric Pair mode 的行为会发生变化:键入左分隔符或右分隔符,都会将选区内的文本包裹在一对匹配的分隔符中,且光标会停在你键入的分隔符之后 —— 这一设计方便你在左分隔符后的文本前,或右分隔符后继续输入内容。
有多个用户选项可修改 Electric Pair mode 的行为:
electric-pair-preserve-balance:设为非nil(默认值)时,键入分隔符会保持左、右分隔符的配对平衡。例如,若你键入左分隔符,且缓冲区后方存在未配对的对应右分隔符,则仅插入左分隔符(不会立即跟随插入右分隔符);同理,若缓冲区前方存在未配对的左分隔符,那么在后方键入对应右分隔符时,即便其后紧邻另一个同类型右分隔符,也会正常插入该字符。设为
nil时,仅当光标紧邻同类型左分隔符前后,或紧邻字母 / 数字前时,键入左分隔符才会仅插入该字符;在其他所有位置键入左分隔符,都会自动在其后插入匹配的右分隔符,即便缓冲区后方存在未配对的对应右分隔符。且当光标紧邻同类型右分隔符前时,键入该右分隔符不会插入字符,而是按上述规则移动光标,即便缓冲区前方存在未配对的左分隔符。若存在激活的选区,该变量将失效。
electric-pair-delete-adjacent-pairs:设为非nil(默认值)时,按下删除键(DEL,通常为退格键BACKSPACE)删除左分隔符时,若其后紧邻匹配的右分隔符(且成对分隔符之间无任何字符,包括空白符),会自动同时删除该右分隔符;设为nil时,按下退格键仅删除左分隔符(按下退格键删除右分隔符时,始终仅删除该字符)。electric-pair-open-newline-between-pairs:设为非nil(默认值)时,若光标位于左分隔符和其后紧邻的匹配右分隔符之间,按下换行键会在光标后自动插入一个额外的换行符(光标所在的空行可能会根据主模式进行缩进);设为nil时,按下换行键会像常规操作一样,仅在光标前插入一个换行符。electric-pair-skip-whitespace:保持默认的非nil值时,若光标与右分隔符之间仅存在空白符,键入同类型右分隔符不会插入字符,而是将光标直接移至已存在的右分隔符之后;你也可将该选项设为同时删除光标移动过程中经过的所有空白符。设为nil时,键入右分隔符会直接插入该字符,即便这会导致其后的同类型右分隔符出现配对失衡。
28.5. 注释处理
注释是编程中至关重要的组成部分,因此 Emacs 提供了专门用于编辑和插入注释的命令。它还可通过 拼写检查模式 Flyspell Prog mode 对注释进行拼写检查(详见 “拼写检查与修正” 章节)。
部分主模式针对不同类型注释的缩进制定了特殊规则。例如,在 Lisp 代码中,以两个分号( ;; )开头的注释会按代码行的方式缩进,而以三个分号( ;;; )开头的注释则应左对齐到页边距,且常用于代码分段说明。Emacs 能够识别这些约定:例如,在注释行按 Tab 键时,会将注释缩进至合适位置。
;; 此函数仅为示例 ;;; 此处使用两个或三个分号均可 (defun foo (x) ;;; 接下来是函数的第一部分: ;; 下一行实现加一操作 (1+ x)) ; 此行实现加一操作
28.5.1. 注释命令
下述命令专用于注释的编辑操作:
M-;- 为当前行插入注释或重新对齐已有注释;若选区处于激活状态,则改为为选区添加注释或取消注释 (
comment-dwim) 。 C-x C-;- 为当前行添加注释或取消注释 (
comment-line) 。若选区处于激活状态,则改为为选区中的所有行添加 / 取消注释。 C-u M-;- 删除当前行的注释内容 (
comment-kill) 。 C-x ;- 设置注释的对齐列数 (
comment-set-column) 。 C-M-jM-j- 等效于按下
RET回车键后,自动插入并对齐注释 (default-indent-new-line) ,相关说明见「多行注释」章节。 M-x comment-regionC-c C-c(类 C 语言模式下的绑定快捷键)- 为选区中的所有行添加注释定界符。
用于创建注释或对齐注释的核心命令为 M-; (comment-dwim) 。其中 “dwim” 是英文 “Do What I Mean”(按我所想执行)的缩写,这意味着该命令会根据使用场景,自动适配完成与注释相关的多种操作。
当选区处于激活状态时, M-; 会自动判断为选区添加注释定界符,或移除已有注释的定界符:若选区中的所有行均已为注释行,则移除这些行的注释定界符以取消注释;反之,则添加注释定界符,将选区内的文本包裹为注释。
若在选区激活时为 M-; 添加前缀参数,该参数将指定要添加或删除的注释定界符数量:正整数参数 n 表示添加 n 个注释定界符,负整数参数 −n 表示删除 n 个注释定界符。
若选区未激活,且当前行无任何注释, M-; 会为当前行插入新注释:若当前行为空行(即无任何字符,或仅包含空白字符),则注释会缩进至按下 TAB 键后的对齐位置(参见「基础程序缩进命令」);若当前行为非空行,注释会被放置在该行最后一个非空白字符之后。若条件允许,Emacs 会尝试将注释置于 comment-column 和 comment-fill-column 两个变量指定的列数范围内;若无法满足,则会选择其他合适的位置,且注释与非注释文本之间通常至少保留一个空格的间距。无论哪种情况,Emacs 都会将光标置于注释起始定界符之后,方便你直接输入注释内容。
你也可使用 M-; 对已有注释进行重新对齐:若当前行已包含注释起始定界符, M-; 会将注释重新对齐至规范位置,并将光标移至注释起始定界符之后。例外情况:从第 0 列开始的注释不会被移动。即便已有注释的对齐位置规范, M-; 仍可用于快速将光标跳至注释文本的起始位置。
C-x C-; (comment-line) 用于对整行进行添加 / 取消注释操作:选区激活时,对选区内所有行执行该操作;选区未激活时,仅对光标所在行执行。为该命令添加正整数前缀参数 n ,会对从当前行开始的连续 n 行执行操作;添加负整数参数 −n ,则对当前行之前的 n 行执行操作。若首次使用负参数调用该命令,后续使用正参数再次调用时,仍会对当前行之前的行执行操作,效果等同于参数被取反。
当选区未激活时, C-u M-; (带前缀参数的 comment-dwim 命令)会删除当前行的所有注释内容,同时删除注释前的空白字符。由于被删除的注释内容会被保存至删除环,你可在其他行通过以下操作恢复该注释:将光标移至目标行末尾,按下 C-y 粘贴注释内容,再按下 M-; 重新对齐注释即可。直接执行 M-x comment-kill 命令,可实现与 C-u M-; 完全相同的效果(当 comment-dwim 命令接收到前缀参数时,实际会调用 comment-kill 作为子例程执行)。若选区未激活,且为 M-; 添加数字前缀参数(如 C-u n M-; ),则会让 comment-kill 命令一次性删除从当前行开始的 n 行注释。
M-x comment-region 命令的效果,等同于在激活选区的情况下按下 M-; ,区别在于该命令始终作用于选区,即便标记点处于非激活状态。在 C 语言模式及相关派生模式中,该命令被绑定至快捷键 C-c C-c 。与之对应的 M-x uncomment-region 命令,用于为选区中的所有行取消注释;为其添加数字前缀参数,可指定要移除的注释定界符数量(负参数则表示要添加的注释定界符数量)。
对于类 C 语言模式,你可通过设置 c-indent-comment-alist 和 c-indent-comments-syntactically-p 变量,自定义 M-; 命令的具体执行效果。例如,在以右大括号结尾的行中, M-; 会将注释置于大括号后一个空格的位置,而非 comment-column 变量指定的列数。详细配置说明参见《CC 模式手册》中的「注释相关命令」章节。
28.5.2. 多行注释
若你正在输入注释并希望换行继续编写,可按下 M-j 或 C-M-j (default-indent-new-line) 。该操作会换行,并自动插入续写注释所需的注释定界符,同时完成相应的缩进。
对于使用闭合注释定界符的编程语言(例如 C 语言中的 '*/'), M-j 命令的具体执行行为由 comment-multi-line 变量的值决定:若该变量设为 nil ,命令会先闭合上一行的注释,再在新行开启一个新注释;若为非nil值,则会在当前注释的定界符范围内直接换行续写。
当自动换行模式(Auto Fill mode)开启时,在输入注释的过程中,若内容超出换行列数,Emacs 也会自动续写注释,效果与手动按下 M-j 完全一致。
若要将已有的文本行转换为注释行,可激活选区后按下 M-; ,或使用上一节中介绍的 M-x comment-region 命令。
你可对 C 模式进行如下配置:在多行块注释中,若在行首键入 '/',即可闭合该注释。只需为该功能启用 comment-close-slash 清理规则即可,详细说明参见《CC 模式手册》中的「清理规则」章节。
28.5.3. 控制注释的相关选项
正如注释相关命令章节所述,当 M-j 命令为某行添加注释时,会尝试将注释置于缓冲区局部变量 comment-column 和 comment-fill-column 所指定的列数区间内(若 comment-fill-column 值为 nil ,则使用 fill-column 的取值,参见显式换行命令章节)。你可通过常规方式为这些缓冲区局部变量设置局部值或默认值(参见局部变量章节)。此外,也可键入 C-x ; (comment-set-column) ,将当前缓冲区的 comment-column 值设为光标当前所在的列数。执行 C-u C-x ; 会将注释列数匹配为缓冲区中光标位置之前的最后一个注释的列数,随后执行 M-; 命令,将当前行的注释与上一个注释对齐。
注释相关命令通过变量 comment-start-skip 的取值(一个正则表达式)识别注释,需确保该正则表达式不会匹配空字符串。从严格意义上讲,它匹配的内容可多于注释起始定界符:例如在 C 模式中,该变量的取值可为 "/\\*+[ \t]*\\|//+[ \t]*" ,此表达式既会匹配 '/*' 后多余的星号和空白符,也支持 C++ 风格的 '//' 注释。(注意:在 Lisp 语法中,需用 '\\' 表示字符串中的单个 '\' ,以此取消正则表达式中第一个星号的特殊含义,参见正则表达式中的反斜杠章节。)
当注释命令新建注释时,会插入 comment-start 的取值作为注释起始定界符,同时在光标后插入 comment-end 的取值作为注释结束定界符。例如在 Lisp 模式中, comment-start 的取值为 ";" , comment-end 的取值为空字符串 "" ;在 C 模式中, comment-start 为 "/* " , comment-end 为 " */" 。
变量 comment-padding 指定注释命令在注释定界符与注释文本之间插入的字符串,默认值为 " " (单个空格)。该变量也可设为数字,表示插入对应数量的空格;若设为 nil ,则表示不插入任何空格。
变量 comment-multi-line 控制 M-j 命令和自动换行模式如何实现注释的多行续写,具体说明参见多行注释章节。
变量 comment-indent-function 应指向一个函数,该函数用于计算新插入注释的对齐位置,或为已有注释重新计算对齐位置,不同的主模式会为其设置不同的函数。调用该函数时无需传入参数,调用时光标需位于注释起始位置;若为新建注释,则光标需位于行尾。该函数的返回值为注释应起始的列数,例如默认函数会根据已有注释的起始符数量来确定对齐位置。
Emacs 还会尝试将相邻行的注释对齐,若要取消该行为,上述函数可返回一个由两个整数(可相等)组成的点对,以此指定注释缩进的可接受范围。
28.6. 文档查阅
28.6.1. Info 文档查阅
对于对应编程语言的说明文档已收录在 Info 中的主模式,你可使用快捷键 C-h S (info-lookup-symbol) ,查阅程序中所用符号的 Info 说明文档。你需要在迷你缓冲区中指定待查询的符号,默认查询的是光标所在位置缓冲区中显示的符号。例如在 C 模式下,该命令会在《C 库手册》中检索目标符号。注意,该命令仅在对应的手册 Info 文件已完成安装时才能正常使用。
Emacs 会根据当前的主模式,确定符号文档的检索范围 —— 包括检索的 Info 文件及具体检索的索引项。你也可使用 M-x info-lookup-file 命令,查阅与文件名相关的说明文档。
若在不支持该功能的主模式中使用 C-h S ,Emacs 会提示你指定 符号帮助模式 。此时你需要输入一个支持 C-h S 功能的主模式对应命令,例如 c-mode 即可。
28.6.2. 手册页查阅
在 Unix 系统中,在线文档的主要形式为手册页( man page )。在 GNU 操作系统中,我们力求用组织结构更优的手册替代手册页,这类手册可通过 Info 工具浏览(参见其他帮助命令章节)。该替代工作尚未完成,因此查阅手册页仍有实际用处。
你可以使用 M-x man 命令,查阅操作系统命令、库函数或系统调用对应的手册页。该命令会在迷你缓冲区中提示你输入查询主题,支持补全功能(参见补全章节),并调用 man 程序对对应的手册页进行格式化。若系统支持,该命令会以异步方式运行 man 程序,让你在手册页格式化的同时继续进行编辑操作。查阅结果会显示在名为 *Man topic* 的缓冲区中,这类缓冲区使用专属的主模式 ——手册模式(Man mode),该模式可便捷地实现翻页和跳转到其他手册页的操作。如需了解该模式的详细用法,可在手册模式缓冲区中键入 C-h m 查看。
每个手册页都归属于十个及以上的 章节 ,各章节以单个数字或数字加字母命名。有时不同章节中会存在同名的手册页。若要查阅指定章节的手册页,可在 M-x man 命令提示输入主题时,键入‘topic(section)’或‘section topic’。例如,C 库函数 chmod 的手册页位于第 2 章节,而存在一个同名的 Shell 命令,其手册页在第 1 章节;若要查看前者,需键入 M-x man RET chmod(2) RET 。
若未指定章节, M-x man 命令默认仅显示找到的首个手册页。部分系统中,man 程序支持 '-a' 命令行选项,该选项可让程序显示指定主题的所有手册页。若要启用该功能,可将变量 Man-switches 的值修改为 "-a" 。设置完成后,在手册模式缓冲区中,你可键入 M-n 和 M-p 在不同章节的手册页间切换,模式行会显示当前可用的手册页总数。
默认情况下, M-x man 会以异步方式调用 man 程序。若要强制以同步方式执行,可将自定义变量 Man-prefer-synchronous-call 设为非nil值。
若用户选项 Man-support-remote-systems 为非nil值,且 default-directory 指向远程系统(参见远程文件章节),则手册页会从该远程系统中获取。在调用 man 命令时添加前缀参数(如 C-u M-x man ),会临时反转本次调用中 Man-support-remote-systems 的取值。
查阅手册页的另一种方式是使用 M-x woman 命令。与 M-x man 不同,该命令不会调用任何外部程序来格式化和显示手册页,所有格式化操作均由 Emacs 自身完成,因此该命令可在 MS-Windows 等未安装 man 程序的系统中使用。该命令会提示你输入手册页名称,并将查阅结果显示在名为 *WoMan section topic* 的缓冲区中。
M-x woman 命令会在你首次调用时,计算手册页的补全列表。若添加数字前缀参数调用该命令,会重新计算补全列表 —— 当你新增或删除手册页时,该操作会十分实用。
若你输入的手册页名称在不同章节中存在多个同名手册页, M-x woman 会弹出一个窗口,列出所有候选手册页供你选择。
请注意, M-x woman 目前暂不支持现代手册页的最新特性,因此若你的系统中可使用 M-x man ,建议优先使用该命令。
关于 M-x woman 的配置和使用详情,可参考随 Emacs 一同分发的 WoMan Info 手册。
28.6.3. 编程语言文档查阅
编辑 Emacs Lisp 代码时,可使用命令 C-h f (describe-function) 和 C-h v (describe-variable) ,查阅想要使用的 Lisp 函数与变量的内置文档,相关说明参见「按命令或变量名获取帮助」章节。
ElDoc15 是一款缓冲区局部次要模式,可辅助查阅程序中各类符号(函数、方法、类、变量等)的文档。开启该模式后,只要光标所在位置存在带文档的符号,回显区就会显示相关实用信息。例如在 Emacs Lisp 模式的缓冲区中,光标置于函数处时,会显示该函数的参数列表;光标置于 Lisp 变量处时,会显示该变量文档字符串的首行内容。
键入 M-x eldoc-mode 可切换 ElDoc 模式的开关状态。Emacs 还提供全局 ElDoc 模式,该模式默认开启,会为所有主模式配置了下述相关变量的缓冲区,自动启用 ElDoc 模式;键入 M-x global-eldoc-mode 可在全局范围内关闭该模式。
各类主模式会对全局 ElDoc 模式进行配置,使其调用各自的文档查询函数,Emacs Lisp 模式、Python 模式、Cfengine 模式均属此类。此外,为多款主模式提供支持的 Emacs 功能,也会配置 ElDoc 调用其自身的文档检索工具,例如:基于语言服务器信息提供文档的 Eglot(参见《Eglot:Emacs LSP 客户端》中的「Eglot 功能」章节)、Semantic 插件的空闲摘要模式(参见《Semantic 手册》中的「空闲摘要模式」章节),以及通过 ElDoc 在光标处显示诊断信息的 Flymake(参见《GNU Flymake 手册》中的「诊断信息查找」章节)。
ElDoc 模式的工作机制为:当 Emacs 处于空闲状态并达到设定的短时间阈值后,会自动调取光标所在符号的可用文档并显示。这一设计可避免快速输入时,回显区或模式行的文档信息频繁闪烁,造成使用干扰。
你也可通过命令 M-x eldoc-print-current-symbol-info ,手动触发光标所在符号的文档显示。
可通过以下变量对 ElDoc 模式进行自定义配置:
eldoc-idle-delay- 该用户选项用于控制光标处文档的空闲显示延迟时间,取值为等待的秒数,设为 0 表示无延迟即时显示,默认值为 0.5 秒。
eldoc-print-after-edit- 若该用户选项设为非 nil 值,ElDoc 仅会在执行插入、删除文本等编辑命令后显示文档。若你希望 Emacs 仅为手动键入的符号显示文档,而非缓冲区中已存在的符号(即仅阅读源代码时不显示文档),该配置会非常实用;默认值为
nil。修改该变量的值后,需先关闭再重新开启eldoc-mode,配置方可生效。 eldoc-echo-area-use-multiline-p该用户选项用于控制:当文档文本长度超过回显区单行可显示范围时,是否截断及如何截断文档。 取值为正数时,指定 ElDoc 在回显区中可无截断显示文档的屏幕行数;正整数表示绝对最大行数,浮点数表示占帧高度的比例。
取值为
t时,表示永不截断文档(回显区会自动调整高度,最大不超过max-mini-window-height限定的高度,参见「迷你缓冲区中的编辑」章节)。取值为
nil时,表示若文档超过单行长度则直接截断。特殊取值
truncate-sym-name-if-fit(默认值):若截断符号名部分即可让文档完整显示在单行中,则仅截断符号名。eldoc-echo-area-display-truncation-message- 若设为非 nil 值(默认),当回显区显示的文档因过长被截断时,会在文档末尾附上完整文档的查看指引;若设为
nil,则仅用省略号…标识文档已被截断。 eldoc-echo-area-prefer-doc-buffer该用户选项的取值决定回显区与 ElDoc 专属文档缓冲区的显示优先级:
取值为
t时,若承载该文档的 ElDoc 缓冲区已在任意窗口中显示,则不在回显区重复显示(可随时通过M-x eldoc-doc-buffer命令调出 ElDoc 文档缓冲区)。取值为符号
maybe时,若 ElDoc 缓冲区已显示,且文档在回显区中显示需要截断,则不在回显区显示。取值为
nil(默认)时,始终在回显区显示文档。eldoc-documentation-strategy- 该可自定义变量的取值为一个函数,该函数负责检索并显示光标所在符号的文档,文档内容由钩子
eldoc-documentation-functions中的函数生成。该变量的默认值指定:ElDoc 仅显示eldoc-documentation-functions钩子中第一个函数生成的文档文本;你可自定义该变量,实现其他展示逻辑,例如将所有函数生成的文档文本拼接后完整显示。 eldoc-documentation-functions- 这是一个异常钩子,其取值为函数列表,这些函数可根据当前缓冲区的主模式,为光标所在符号生成对应的文档,是 ElDoc 的底层文档检索后端集合。各类主模式会将自身的文档查询函数添加至该变量的缓冲区局部值中,完成与 ElDoc 的注册对接。
28.7. 代码折叠次模式
Hideshow mode代码折叠模式是一款缓冲区局部次要模式,支持你选择性地显示程序中的代码段(这类代码段被称作 blocks 代码块 )。键入 M-x hs-minor-mode 可切换该次要模式的开关状态(参见「次要模式」章节)。
使用代码折叠模式隐藏某一代码块时,该代码块会从屏幕中消失,取而代之的是一个省略号(连续三个句点)。代码块的判定规则由当前主模式决定:在 C 模式及相关派生模式中,代码块以大括号为分隔符;在 Lisp 模式中,代码块以圆括号为分隔符, 多行注释也会被视作代码块 。
代码折叠模式提供以下操作命令:
C-c @ C-hC-c @ C-d- 隐藏当前代码块 (
hs-hide-block) 。 C-c @ C-s- 显示当前代码块 (
hs-show-block) 。 C-c @ C-cC-c @ C-eS-mouse-2- 切换当前代码块的显示状态(隐藏 / 显示) (
hs-toggle-hiding) 。 C-c @ C-M-hC-c @ C-t- 隐藏所有顶层代码块 (
hs-hide-all) 。 C-c @ C-M-sC-c @ C-a- 显示缓冲区中的所有代码块 (
hs-show-all) 。 C-u n C-c @ C-l- 隐藏当前代码块下第 n 层的所有代码块 (
hs-hide-level) 。
可通过以下变量自定义代码折叠模式的行为:
hs-hide-comments-when-hiding-all- 若设为非nil值,执行
C-c @ C-M-h(hs-hide-all) 时会同时隐藏注释内容。 hs-isearch-open- 该变量用于指定增量搜索的匹配文本出现在隐藏代码块内时,解隐藏该代码块的触发条件。其取值可选:code(仅解隐藏代码块)、comment(仅解隐藏注释块)、t(同时解隐藏代码块和注释块)、nil(均不解隐藏),默认值为code。
28.8. 符号名称补全
补全功能通常在迷你缓冲区中使用(参见「补全」章节),但你也可在 Emacs 普通缓冲区中完成符号名的补全操作。
在大多数编程语言模式下,按下 C-M-i (或 M-TAB 16)会调用 completion-at-point 命令,该命令会为光标所在位置的符号生成所有可能的补全候选列表。此命令会调用各类可用的支持工具来生成补全候选,优先级规则如下:
- 若当前缓冲区所属项目及主模式已激活 Eglot 功能(参见「项目操作」章节),该命令会尝试调用对应的语言服务器生成补全候选列表(参见《Eglot:Emacs LSP 客户端》中的「Eglot 功能」章节);
- 若已启用 Semantic 模式(参见「Semantic 语义分析」章节),该命令会尝试使用 Semantic 解析器的数据分析结果进行补全;
- 若未启用 Semantic 模式,或该模式补全失败,命令会尝试通过已选中的标签表进行补全(参见「标签表」章节)—— 需先执行
M-x visit-tags-table命令加载标签表,此功能方可生效; - 在 Emacs Lisp 模式下,该命令会利用当前 Emacs 会话中已定义的函数、变量或属性名完成补全。
除上述规则外,缓冲区中的符号补全行为与迷你缓冲区补全完全一致。例如,若 Emacs 无法补全为唯一的符号,会在另一个窗口中显示所有补全候选选项。此时你可按下 M-DOWN 和 M-UP ,在不离开原缓冲区的前提下,浏览补全缓冲区中的候选内容;按下 M-RET ,即可将当前高亮的补全项插入原缓冲区(详见「补全」章节)。
在文本模式及相关派生模式中,按下 M-TAB 会基于拼写检查器的词典完成单词补全(参见「拼写检查与修正」章节)。
补全预览模式是一款次要模式,可在你输入的同时实时显示补全建议。执行 M-x completion-preview-mode 可在当前缓冲区启用该模式,执行 M-x global-completion-preview-mode 则可在全局启用。开启补全预览模式后,Emacs 会在光标后方以 行内预览 的形式,自动显示光标附近文本的推荐补全内容;按下 TAB 键即可确认并插入该补全建议。
28.9. 混合大小写单词处理
部分编程风格会使用混合大小写(即 “驼峰式”)的符号,例如 'unReadableSymbol' 。(在 GNU 项目中,我们建议在标识符中使用下划线分隔单词,而非通过大小写区分。)Emacs 提供了多种功能,方便处理这类符号。
Glasses 模式是一款缓冲区局部次要模式,它通过修改混合大小写符号的显示方式,提升其可读性。默认情况下,该模式会在小写字母与后续的大写字母之间,显示额外的下划线作为分隔。此操作仅改变符号的显示效果,不会修改缓冲区中的实际文本内容。
键入 M-x glasses-mode 可切换 Glasses 模式的开关状态(参见「次要模式」章节)。开启该模式后,模式行会出现次要模式标识 'o^o' 。如需了解 Glasses 模式的更多信息,可键入 C-h P glasses RET 查阅。
子词模式(Subword mode)是另一款缓冲区局部次要模式。开启该模式后,Emacs 的各类单词操作命令会将驼峰式标识符(如 'StudlyCapsIdentifiers' )中的大写字母识别为单词边界。启用子词模式后,模式行会显示次要模式标识,。你也可参考功能类似的超词模式(superword-mode)(参见「程序编辑的其他实用功能」章节)。
28.10. 语义分析(Semantic)
Semantic 是一个基于源代码解析器、提供 语言感知型编辑命令 的扩展包。本节仅对其做简要介绍,完整详情请参见《Semantic 参考手册》中的语义分析章节。
Emacs 中大部分语言感知功能(如语法高亮模式),均依赖于经验性规则17 实现 —— 这类规则通常能达到较好效果,但始终无法做到完全精准。与之不同的是,Semantic 所使用的解析器能 精准解析编程语言的语法规则 ,这让它可以提供功能强大且结果精准的搜索、导航与补全命令。
要开始使用 Semantic,可键入 M-x semantic-mode ,或在「Tools」菜单中点击「Source Code Parsers (Semantic)」选项,即可启用Semantic 模式(一款全局次要模式)。
启用 Semantic 模式后,Emacs 会自动尝试解析你打开的每一个文件。目前 Semantic 支持解析的语言包括:C、C++、HTML、Java、JavaScript、Make、Python、Scheme、SRecode 以及 Texinfo。在所有已完成解析的缓冲区中,可使用以下命令:
C-c , j- 提示输入当前文件中定义的函数名,并将光标跳转到该函数处 (
semantic-complete-jump-local) 。 C-c , J- 提示输入 Emacs 已解析的任意文件中定义的函数名,并将光标跳转到该函数处 (
semantic-complete-jump) 。 C-c , SPC- 为光标所在位置的符号显示所有可能的补全候选列表 (
semantic-complete-analyze-inline) 。该命令还会激活一套用于选择补全项的专用快捷键:按下RET回车键确认当前补全项,M-n和M-p循环切换补全候选,制表键(TAB)先完成最大程度的自动补全再循环候选,C-g或其他任意按键则终止补全操作。 C-c , l- 在新窗口中显示光标所在位置符号的所有补全候选列表 (
semantic-analyze-possible-completions) 。
除上述命令外,Semantic 扩展包还提供了多种利用解析器信息的方式,例如可配置为 Emacs 空闲时自动显示符号补全列表。相关详情请参见《Semantic 参考手册》中的语义分析章节。
28.11. 其他程序编辑实用功能
部分并非专为程序编辑设计的 Emacs 命令,在编写代码时同样能发挥实用价值。
Emacs 中用于操作单词、句子和段落的命令,均可用于代码编辑。多数符号名中包含多个语义单词(参见「单词操作」章节),而字符串与注释中则会出现完整的句子(参见「句子操作」章节)。至于段落,大多数编程语言模式中都将空行定义为段落的起止边界(参见「段落操作」章节)。因此,合理使用空行让程序代码的结构更清晰,同时也能为段落操作命令划分出便于处理的文本块。若在编程语言主模式中启用了自动换行模式,该模式会为其自动换行生成的新行完成缩进。
超词模式(Superword mode)是一款缓冲区局部次要模式,启用后,编辑与光标移动类命令会将编程符号(例如 this_is_a_symbol )视作单个单词处理。开启该模式后,模式行会显示次要模式标识 ‘²’。相关可参考功能类似的子词模式(参见「混合大小写单词」章节)。
快速排版模式(Electric Layout mode)( M-x electric-layout-mode )是一款全局次要模式,键入特定字符时会自动插入换行符;例如在 JavaScript 模式中,键入‘{’、‘}’和;时会触发该自动换行行为。
除代码折叠模式外(参见「代码折叠次要模式」章节),另一类选择性显示程序代码片段的方式是使用选择性显示功能(参见「选择性显示」章节)。编程相关模式通常还支持大纲次要模式(参见「大纲模式」章节),该模式可与 Foldout 扩展包配合使用(参见「折叠编辑」章节)。
符号美化模式(Prettify Symbols mode)是一款缓冲区局部次要模式,会将特定字符串替换为视觉上更美观的形式显示, 不会修改缓冲区实际文本 。例如在 Emacs Lisp 模式中,该模式会将字符串 ‘lambda’ 替换为希腊字母 ‘λ’ 显示;在 TeX 缓冲区中,会将 '\alpha ... \omega' 等数学宏命令替换为对应的 Unicode 字符显示。该模式同样可在非编程模式中启用。你可通过向 prettify-symbols-alist 变量中添加更多配置项,自定义该模式的替换规则;若默认的判定函数 prettify-symbols-default-compose-p 无法满足需求,还可通过自定义 prettify-symbols-compose-predicate 实现更复杂的配置。该模式也提供全局版本 global-prettify-symbols-mode ,可在所有支持该模式的缓冲区中启用。
光标所在位置的符号可强制显示其原始形式,该功能由变量 prettify-symbols-unprettify-at-point 控制:若设为非nil值,只要光标停留在某符号处,该符号就会恢复为原始形式显示。
28.12. C 语言及相关模式
本节简要介绍 C、C++、Objective-C、Java、CORBA 接口定义语言(IDL)、Pike 及 AWK 模式中提供的专属功能(这些模式统称C 语言及相关模式)。更多详细内容,可参阅随 Emacs 一同分发的 CC 模式 Info 手册。
28.12.1. C 模式移动命令
本节介绍 C 模式及相关模式中用于移动光标的专属命令。
C-M-aC-M-e- 将光标移至当前函数或顶层定义的起始 / 结束位置。在存在嵌套作用域的语言中(如 C++ 的类),「当前函数」指直接包含光标的函数(可位于某一作用域内);其他情况下,指由最内层闭合大括号定义的函数。(与之不同的是,
beginning-of-defun和end-of-defun命令会仅检索位于第 0 列的大括号。)参见「按函数单元移动」章节。 C-c C-u将光标回退至包含当前位置的预处理条件编译块处,同时在原位置留下标记。前缀参数表示重复执行次数;若为负参数,则将光标前移至该预处理条件编译块的结束位置。
‘#elif’ 等效于 ‘#else’ 后紧跟 ‘#if’,因此该命令回退时会在 ‘#elif’ 处停止,前移时则不会。
C-c C-p- 将光标回退过一个预处理条件编译块,同时在原位置留下标记。前缀参数表示重复执行次数;若为负参数,则执行前移操作。
C-c C-n- 将光标前移过一个预处理条件编译块,同时在原位置留下标记。前缀参数表示重复执行次数;若为负参数,则执行回退操作。
M-a将光标移至最内层 C 语句的起始位置 (
c-beginning-of-statement) 。若光标已位于某语句起始处,则移至上一条语句的起始位置。带前缀参数n时,回退过n-1条语句。若光标处于跨多行的注释或字符串内,该命令会按句子而非语句进行移动。
M-e- 将光标移至最内层 C 语句或句子的结束位置;功能与
M-a一致,仅移动方向相反 (c-end-of-statement) 。
28.12.2. C 语言自动缩进字符
在 C 模式及相关模式中,部分可打印字符为 电动electric 字符—— 这类字符被键入时,除自身插入到文本中外,还会对当前行重新进行缩进,亦可根据设置自动插入换行符。电动字符包含: { 、}、:、#、;、,、<、>、/、*、(、)。
若你编辑的代码缩进格式混乱,电动缩进功能可能会带来不便;对于初次使用 CC 模式的用户,该功能也可能让人感到不适。你可通过命令 C-c C-l 切换电动功能的开关状态:功能启用时,模式行的模式名称后方会显示标识 ‘/cl’ (其中字符 c 若存在,会根据注释风格显示为 ‘*’ 或 ‘/’,分别对应块注释和行注释)。关于 CC 模式下模式行的各类标识说明,详见《CC 模式手册》中的「次要模式」章节。
C-c C-l- 切换电动功能的启用状态 (
c-toggle-electric-state) 。带正前缀参数时启用该功能,带负前缀参数时禁用。
电动字符仅在电动功能开启,且自动换行功能同时启用的情况下,才会触发自动插入换行符的行为(该状态下模式行的模式名称后方会显示标识 ‘/cla’)。你可通过命令 C-c C-a 开关该功能:
C-c C-a- 切换自动换行功能的启用状态 (
c-toggle-auto-newline) 。带前缀参数时,正参数启用该功能,负参数禁用。
CC 模式的风格配置通常会定义 Emacs 触发自动换行的具体场景,你也可直接对该行为进行自定义配置,详见《CC 模式手册》中的「自定义自动换行」章节。
28.12.3. C 模式中的贪婪删除功能
若需删除光标位置处的整段空白字符,可使用 hungry deletion贪婪删除 功能。该功能能通过单次操作,删除光标前或光标后的所有连续空白字符。此处的空白字符包含制表符和换行符,但不包含注释或预处理命令。
C-c C-DELC-c DEL- 删除光标前方的整段空白字符 (
c-hungry-delete-backwards) 。 C-c C-dC-c C-DeleteC-c Delete- 删除光标后方的整段空白字符 (
c-hungry-delete-forward) 。
除上述命令外,你也可以启用 hungry delete 模式。该功能启用后,模式行的模式名称后方会在/后显示标识 ‘h’ ;此时按下单次删除键( DEL )会删除前方所有空白字符,而非仅单个空格;按下单次 C-d 键(普通 Delete 键无此效果)会删除后方所有空白字符。
M-x c-toggle-hungry-state- 切换贪婪删除功能的启用状态 (
c-toggle-hungry-state) 。带前缀参数时,正参数启用该功能,负参数禁用该功能。
变量 c-hungry-delete-key 用于控制贪婪删除功能的启用状态。
28.12.4. C 模式的其他命令
M-x c-context-line-break插入换行符,并根据上下文对新行进行适配性缩进。在普通代码中,该命令的作用等同于回车键(
RET);在 C 预处理行中,会在换行处额外插入一个反斜杠 '\' ;在注释内时,效果与M-j(c-indent-new-comment-line) 一致。该命令默认未绑定快捷键,需手动绑定才能便捷使用。以下代码可将其绑定至回车键,代码中使用
c-initialization-hook钩子,确保在修改键映射前该映射已完成加载:
(defun my-bind-clb () (keymap-set c-mode-base-map "RET" 'c-context-line-break)) (add-hook 'c-initialization-hook 'my-bind-clb)
C-M-h- 将标记点置于函数定义的末尾,光标置于函数定义的开头 (
c-mark-function 命令,选中整个函数) 。 M-q- 对段落进行自动换行,专门适配 C 和 C++ 注释格式 (
c-fill-paragraph) 。若当前行的任意部分属于注释或处于注释内,该命令会对光标所在的注释段 / 注释内段落进行换行处理,同时保留注释的缩进格式与注释定界符。 C-c C-e对选区内的文本执行 C 预处理器处理,并展示处理结果,其中包含所有宏调用的展开内容 (
c-macro-expand) 。为兼容宏的定义位置,选区之前的缓冲区文本也会参与预处理,但这部分内容的处理结果不会展示。调试使用了宏的 C 代码时,有时难以准确分析宏的展开逻辑,使用该命令可直接查看宏的展开结果,无需手动推导。
C-c C-\在选区的各行末尾插入或对齐反斜杠 '
\' (c-backslash-region) ,编写或编辑 C 宏定义后,该命令会非常实用。若某行末尾已存在反斜杠 '
\' ,该命令会调整其前方的空白字符数量以实现对齐;若不存在,则插入一个新的反斜杠 '\' 。特殊规则:选区的最后一行会被特殊处理,该行不会插入反斜杠 '\' ,若已有则会被删除。M-x cpp-highlight-buffer- 根据预处理条件编译指令,对文本不同部分进行高亮标记。该命令会打开一个名为
*CPP Edit*的缓冲区,作为图形化菜单,可在其中选择特定类型的条件编译指令及其内容的展示方式。完成各项设置后,点击 ‘[A]pply these settings’(或切换至该缓冲区并按下a键),C 模式缓冲区会根据设置重新高亮。 C-c C-s- 展示当前源代码行的语法结构信息 (
c-show-syntactic-information),这些信息是该行缩进规则的判定依据。 M-x cwarn-modeM-x global-cwarn-modeCWarn 次要模式会对 C 和 C++ 中部分可疑的代码写法进行高亮提醒,包括:
- 表达式内部的赋值操作;
- if、for、while语句后紧跟分号(‘do … while’语句除外);
- 包含引用参数的 C++ 函数。
可通过
M-x cwarn-mode命令为单个缓冲区启用该模式,或通过M-x global-cwarn-mode命令、自定义global-cwarn-mode变量,为所有适配的缓冲区全局启用。注意:该模式生效的前提是已启用语法高亮模式(Font Lock mode)。M-x hide-ifdef-mode- Hide-ifdef 次要模式会隐藏 ‘#if’ 和 ‘#ifdef’ 预处理块中选定的代码。若将变量
hide-ifdef-shadow设为t,该模式不会完全隐藏预处理块,而是将其以低醒目度的字体样式显示(虚显)。更多信息可查阅该模式的文档字符串。 M-x ff-find-related-file- 查找与当前缓冲区所打开文件存在特定关联的文件,通常为 C/C++ 源文件对应的头文件,或头文件对应的源文件。变量
ff-related-file-alist定义了关联文件名的匹配规则。
28.13. 汇编语言模式
Asm mode汇编模式是用于编辑汇编代码文件的主模式,该模式定义了以下命令:
TAB- 按制表位缩进。
C-j- 插入换行符,随后
tab-to-tab-stop缩进。 :- 插入冒号,接着移除冒号前方标签的前置缩进,然后
tab-to-tab-stop。 ;- 插入注释或对齐已有注释。
变量 asm-comment-char 用于指定汇编语法中作为注释起始符的字符。
28.14. Fortran 模式
Fortran 模式适用于编辑 固定格式(及制表符格式) 的源代码(通常为 Fortran 77)。若要编辑更现代的自由格式源代码(Fortran 90、95、2003、2008),请使用 F90 模式( f90-mode )。Emacs 默认对扩展名为‘.f’、‘.F’ 或‘.for’ 的文件启用 Fortran 模式,对扩展名为‘.f90’、‘.f95’、‘.f03’ 和‘.f08’ 的文件启用 F90 模式。可自定义 auto-mode-alist 变量添加更多关联扩展名。GNU Fortran 同时支持自由格式与固定格式。本手册主要介绍 Fortran 模式,相关的 F90 模式功能会在对应场景下提及。
Fortran 模式为 Fortran 语句和子程序提供了专属的光标移动命令,其缩进命令可识别 Fortran 的嵌套规则、行号及续行约定。该模式对自动换行模式提供适配支持,可将过长的代码行拆分为符合 Fortran 规范的续行;同时也支持代码折叠次要模式(参见代码折叠次要模式章节)和索引菜单功能(参见索引菜单章节)。
由于 Fortran 的注释规则与其他编程语言不同,该模式提供了注释专属操作命令;内置的缩写功能可按需减少你输入 Fortran 关键字的工作量。
键入 M-x fortran-mode 可切换至该主模式,该命令会执行 fortran-mode-hook 钩子函数(参见钩子章节)。
28.14.1. 移动命令
除了按函数单元移动和操作的常规命令(Fortran 子程序 —— 包含函数、子例程,F90 模式还包含模块,对应命令为 fortran-end-of-subprogram 和 fortran-beginning-of-subprogram )外,Fortran 模式还提供了按语句及其他程序单元移动的专属命令。
C-c C-n- 移至下一条语句的起始位置 (
fortran-next-statement/f90-next-statement命令)。 C-c C-p- 移至上一条语句的起始位置 (
fortran-previous-statement/f90-previous-statement命令)。若不存在上一条语句(即从缓冲区的第一条语句处调用该命令),则移至缓冲区起始位置。 C-c C-e- 将光标向前移至下一个代码块的起始位置,或当前代码块的结束位置(以先到达者为准)(执行
f90-next-block命令)。代码块包括子例程、if–endif语句等。该命令仅适用于 F90 模式,不适用于 Fortran 模式。带数字前缀参数时,向前移动指定数量的代码块。 C-c C-a- 将光标向后移至上一个代码块的位置 (
f90-previous-block)。功能与f90-next-block一致,仅移动方向相反。 C-M-n- 移至当前代码块的结束位置 (
fortran-end-of-block/f90-end-of-block) 。带数字前缀参数时,向前移动指定数量的代码块。移动光标前会先设置标记。该命令的 F90 模式版本会检查代码块类型和标签(若存在)的一致性,但不会检查最外层代码块,因其可能未编写完成。 C-M-p- 移至当前代码块的起始位置 (
fortran-beginning-of-block/f90-beginning-of-block) 。功能与fortran-end-of-block一致,仅移动方向相反。
fortran-beginning-of-subprogram 和 fortran-end-of-subprogram 命令分别移至当前子程序的起始和结束位置。 fortran-mark-do 和 fortran-mark-if 命令会标记当前 do 或 if 代码块的结束位置,并将光标移至其起始位置。
28.14.2. Fortran 缩进
对固定格式(或制表符格式)的 Fortran 代码进行缩进时,需要专用的命令和功能,以确保各类语法元素(行号、注释行标识、续行标记)出现在规定的列位置。
28.14.2.1. Fortran 缩进与填充命令
C-M-j- 在光标位置拆分当前行并设置续行格式 (
fortran-split-line) 。 M-^- 将当前行与上一行合并 (
fortran-join-line) 。 C-M-q- 为光标所在子程序的所有行进行缩进排版 (
fortran-indent-subprogram) 。 M-q- 对光标所在的注释块或语句进行自动换行处理(调用
fortran-fill-paragraph或fortran-fill-statement函数)。
快捷键 C-M-q 执行 fortran-indent-subprogram 命令,可对包含光标位置的 Fortran 子程序(函数或子例程)的所有代码行重新进行缩进。
快捷键 C-M-j 执行 fortran-split-line 命令,会按照 Fortran 的语法规范拆分代码行:若为非注释行,拆分后的后半部分会作为续行并按规则缩进;若为注释行,拆分后的两部分会成为独立的注释行。
M-^ 或 C-c C-d 执行 fortran-join-line 命令,可将续行合并回上一行,其功能大致为 fortran-split-line 命令的逆操作注意:调用该命令时,光标必须处于续行上。
在 Fortran 模式中, M-q 会对光标所在的注释块或语句执行自动换行,同时会清理语句中多余的续行标记。
28.14.2.2. 续行
大多数 Fortran 77 编译器支持两种编写续行的方式。若某行第一个非空格字符出现在第 5 列,该行即为上一行的续行,这种方式被称为 fixed form固定格式 。(GNU Emacs 中列数始终从 0 开始计数;但需注意,Fortran 标准中列数从 1 开始计数。你可自定义 column-number-indicator-zero-based 变量,让列数显示与 Fortran 规范一致,参见「可选的模式行功能」章节。)变量 fortran-continuation-string 用于指定在第 5 列填充的续行标识字符。此外,若某行以制表符开头,且后续紧跟非 0 的数字,该行也会被视为续行,这种续行方式被称为 tab format制表符格式 。(Fortran 90 中新增了自由格式的续行方式。)
Fortran 模式支持上述两种续行格式。进入该模式时,编辑器会自动从缓冲区内容中推导合适的续行格式:从缓冲区起始位置开始,扫描最多 fortran-analyze-depth 行(默认值为 100),首个以制表符开头或以 6 个空格开头的行,会决定当前使用的续行格式。若扫描失败(例如缓冲区为新建的空缓冲区),则会使用 fortran-tab-mode-default 的取值( nil 表示固定格式,非nil表示制表符格式)。当模式行中显示标识 ‘/t’ (由 fortran-tab-mode-string 定义)时,说明当前选中的是制表符格式,Fortran 模式会据此相应设置 indent-tabs-mode 的取值。
若某行文本以 Fortran 续行标记 '$' 开头,或第 5 列出现任意非空白字符,Fortran 模式会将其识别为续行。使用 TAB 键为续行缩进时,该行会被转换为当前的续行格式;使用 C-M-j 拆分 Fortran 语句时,新行的续行标记也会按照当前续行格式生成。
续行格式的设置会影响 Fortran 模式下的多项编辑行为:在固定格式下,语句主体的起始列数最小为 6,Fortran 代码块内缩进至更大列数的行,其空白部分只能使用空格字符;在制表符格式下,语句主体的起始列数最小为 8,且第 8 列之前的空白部分必须由一个制表符构成。
28.14.2.3. 行号显示
若某行中第一个非空白字符为数字,Fortran 缩进功能会将其认定为行号,并将该行号调整至 0 至 4 列的位置。(Emacs 中列数始终从 0 开始计数,若将 column-number-indicator-zero-based 设为 nil 可更改该计数方式,参见「可选的模式行功能」章节。)
四位及以下的行号,默认会缩进一个空格。该缩进规则由变量 fortran-line-number-indent 控制,该变量用于指定行号允许的最大缩进值,默认值为 1。Fortran 模式会避免行号的数字部分超出 4 列,必要时会将缩进值调至指定的最大值以下。若将 fortran-line-number-indent 设为 5,行号会以右对齐的方式排布,末尾对齐至 4 列。
只需直接插入行号,即可按照上述规则完成自动缩进。每插入一个数字,缩进值都会重新计算。若要关闭该功能,将变量 fortran-electric-line-number 设为 nil 即可。
28.14.2.4. 语法约定
Fortran 模式的缩进功能基于以下语法约定实现,遵循这些约定能让编辑器更准确地解析 Fortran 程序并完成合理缩进:
- 两层嵌套的 do 循环不可共用一条 continue 语句;
- if、else、then、do 等 Fortran 关键字书写时,内部不可包含空格或换行;
- 尽管 Fortran 编译器通常会忽略字符串常量外的空白字符,但如果关键字书写不连续,Fortran 模式将无法识别。else if、end do这类组合写法是允许的,但第二个单词必须与第一个单词在同一行,不可写在续行中。
若未遵循上述约定,缩进命令对部分代码行的缩进效果可能不够规整。但即便如此,对符合语法规范的 Fortran 程序重新缩进后,其程序语义也不会发生改变。
28.14.2.5. Fortran 缩进相关变量
另有多个变量用于控制 Fortran 代码的缩进行为:
fortran-do-indent- do语句每一层嵌套内的额外缩进值(默认值为 3)。
fortran-if-indent- if、select case或where语句每一层嵌套内的额外缩进值(默认值为 3)。
fortran-structure-indent- structure、union、map或interface语句每一层嵌套内的额外缩进值(默认值为 3)。
fortran-continuation-indent- 续行主体部分的额外缩进值(默认值为 5)。
fortran-check-all-num-for-matching-do- 在 Fortran 77 中,带行号的do语句可由任意行号匹配的语句终止,通常(非强制)使用continue语句完成该操作。若该变量设为非nil值,对任意带行号语句进行缩进时,都需检查是否存在以此为终止的do语句。若你始终使用continue语句终止do语句(或使用更现代的enddo语法),可将该变量设为nil(默认值)以提升缩进效率。
fortran-blink-matching-if- 若该变量设为t,对endif(或enddo)语句进行缩进时,光标会短暂跳转到匹配的if(或do)语句处,以显示其位置。默认值为nil。
fortran-minimum-statement-indent-fixed- 使用固定格式续行风格时,Fortran 语句的最小缩进值。语句主体的缩进值绝不会低于该数值,默认值为 6。
fortran-minimum-statement-indent-tab- 使用制表符格式续行风格时,Fortran 语句的最小缩进值。语句主体的缩进值绝不会低于该数值,默认值为 8。
下一节将介绍用于控制注释缩进的相关变量。
28.14.3. Fortran 注释
Emacs 的常规注释命令默认注释可跟随在代码行后方,但在 Fortran 77 中,标准的注释语法要求整行仅为注释内容。因此 Fortran 模式重写了 Emacs 的标准注释命令,并定义了部分新的相关变量。
Fortran 模式也支持 Fortran 90 的注释语法:以 ‘!’ 开头的注释可跟随在其他文本后方。由于仅有部分 Fortran 77 编译器支持该语法,除非你提前配置,否则 Fortran 模式不会自动插入此类注释。如需启用,将变量 fortran-comment-line-start 设为 "!" 即可;若你设置了特殊的注释起始符,可能还需要修改变量 fortran-comment-line-start-skip 。
M-;- 对齐现有注释,或插入新注释 (
comment-dwim) 。 C-x ;- 仅适用于非标准的!注释 (
comment-set-column) 。 C-c ;- 将选区中的所有行转换为注释;若带前缀参数,则将选区中的注释行还原为正常代码 (
fortran-comment-region) 。
Fortran 模式下的 M-; 会调用标准的 comment-dwim 命令,该命令能识别任意类型的现有注释并对注释文本进行适配性对齐;若当前行无注释,则插入注释并完成对齐。需要注意的是,Fortran 模式中注释的插入与对齐逻辑,与其他 Emacs 模式存在差异。
当需要插入新注释时,规则如下:若当前行为空行,直接插入整行注释;若当前行为非空行,若你已配置启用 ‘!’ 注释,则插入非标准的!注释,否则会在当前行上方新建一行并插入整行注释。
非标准的 ‘!’ 注释的对齐方式与其他编程语言的注释一致,而整行注释的对齐规则则有所不同。在标准的整行注释中,注释分隔符本身必须始终出现在第 0 列,可进行对齐操作的仅为注释内部的文本。你可通过将变量 fortran-comment-indent-style 设为以下任一值,选择三种注释文本对齐风格之一:
fixed- (固定列对齐)将注释文本对齐至固定列位置,该列数为
fortran-comment-line-extra-indent的取值与语句最小缩进值之和,为默认对齐风格。其中语句最小缩进值的规则为:使用制表符格式续行时,取fortran-minimum-statement-indent-tab的取值;使用固定格式续行时,取fortran-minimum-statement-indent-fixed的取值。 relative- (相对对齐)将注释文本按照代码行的缩进规则对齐,再额外增加
fortran-comment-line-extra-indent列的缩进量。 nil- (不自动对齐)不对整行注释中的文本进行自动对齐操作。
此外,你可通过设置变量 fortran-comment-indent-char 为单个字符的字符串,指定在整行注释内用于缩进的字符。
编译器指令行(或预处理器行)的显示形式与注释行十分相似,但无论 fortran-comment-indent-style 取何值,这类行始终不进行任何缩进,这一点至关重要。变量 fortran-directive-re 是一个正则表达式,用于指定哪些行属于编译器指令行;匹配的行不仅不会被缩进,还会被赋予独特的语法高亮样式。
Emacs 的常规注释命令 C-x ; (comment-set-column) 未被 Fortran 模式重写:若你使用 ‘!’ 注释,该命令可正常生效,否则在 Fortran 模式中此命令无实际作用。
命令 C-c ; (fortran-comment-region) 的作用是在选区每行的开头插入字符串 'c$$$' 将其转换为注释行;若带数字前缀参数,则删除选区每行开头的 'c$$$' ,将注释行还原为可执行代码。用于注释转换的该字符串可通过设置变量 fortran-comment-region 进行自定义。需要注意的是,此处出现了命令与变量同名的情况,但在 Lisp 语言和 Emacs 中,上下文会明确区分二者的含义,因此绝不会产生冲突。
28.14.4. Fortran 模式下的自动填充
Fortran 模式为自动换行模式提供了专属适配支持,该次要模式会在你输入的语句过长时,自动将其拆分。语句拆分过程会通过 fortran-continuation-string 生成续行(参见续行章节)。当你键入空格、回车键、制表符,或是执行 Fortran 缩进命令时,都会触发该拆分操作。你可通过常规方式在 Fortran 模式中启用自动换行模式,详见自动换行模式章节。
当代码行长度超过指定宽度(~fill-column~ 变量的取值)时,自动换行模式会在空格或分隔符处拆分代码行。除空白字符外,可触发拆分的分隔符包括+、-、/、*、=、<、>和,。若变量 fortran-break-before-delimiters 设为 nil ,换行操作会在 分隔符后 执行;该变量默认取非nil值,此时换行操作会在 分隔符前 执行。
若要在所有 Fortran 缓冲区中启用自动换行模式,可将 auto-fill-mode 添加至 fortran-mode-hook 钩子中,详见钩子章节。
28.14.5. Fortran 列检查
在标准 Fortran 77 语法中,第 72 列之后的所有内容都会被编译器忽略。多数编译器提供了修改该列数限制的选项(例如 GNU Fortran 中的 ‘-ffixed-line-length-N’ 参数)。可通过自定义变量 fortran-line-length ,修改 Fortran 模式中的代码行长度限制,超出该列数的内容会被语法高亮标记为注释样式(字符串内的内容除外:若字符串超出 fortran-line-length 设定的列数,会导致语法高亮功能识别异常)。
C-c C-r- 在当前行上方临时显示列数标尺 (
fortran-column-ruler) 。 C-c C-w- 临时水平拆分当前窗口,使新窗口宽度恰好为
fortran-line-length设定的列数 (fortran-window-create-momentarily) ,该功能可帮助你避免编写超出 Fortran 编译器列数限制的代码行。 C-u C-c C-w- 水平拆分当前窗口,使新窗口宽度为
fortran-line-length设定的列数 (fortran-window-create) ,拆分后可继续在该窗口中编辑代码。 M-x fortran-strip-sequence-nos- 删除所有行中
fortran-line-length设定列数及之后的所有文本内容。
命令 C-c C-r (fortran-column-ruler) 会在当前行上方临时显示列数标尺,该标尺由两行文本构成,标注出 Fortran 程序中具有特殊意义的列位置:方括号[]标记出行号的有效列范围,大括号 {} 标记出语句主体的有效列范围,列编号会显示在对应范围的上方。
请注意,与 GNU Emacs 的常规设定一致,此处的列编号从 0 开始计数(可通过自定义变量 column-number-indicator-zero-based ,将列数显示方式改为与 Fortran 标准一致的从 1 计数,参见「可选的模式行功能」章节)。因此,标尺上的列编号可能比你熟悉的编号小 1,但标尺标注的列位置完全符合 Fortran 的标准规范。
列数标尺的显示样式由变量 indent-tabs-mode 的取值决定:若该变量设为 nil ,会使用变量 fortran-column-ruler-fixed 定义的样式显示标尺;若为非nil值,则使用变量 fortran-column-ruler-tab 定义的样式,可通过修改这两个变量自定义标尺的显示效果。
命令 C-c C-w (fortran-window-create-momentarily) 会临时水平拆分窗口,使新窗口宽度匹配 fortran-line-length 的列数限制,方便你直观查看过长的代码行,键入空格键即可恢复窗口的原始宽度。
你也可水平拆分窗口后,在固定的窄窗口中持续编辑代码,只需执行 C-u C-c C-w (即 M-x fortran-window-create )即可。在该窄窗口中编辑时,能即时发现编写的代码行是否超出了 Fortran 的合法列数限制。
命令 M-x fortran-strip-sequence-nos 会删除当前缓冲区中,所有行里 fortran-line-length 设定列数及之后的全部文本,这是清除旧的程序行序号最简便的方法。
28.14.6. Fortran 关键字缩写
Fortran 模式为常用关键字和声明语句提供了诸多内置缩写,这类缩写与你自行定义的缩写类型完全相同。使用这些缩写前,你必须先开启缩写模式,详见缩写章节。
这些内置缩写有一个特殊之处:均以分号开头。例如,内置缩写 ‘;c’ 对应的完整内容为 ‘continue’。若已启用缩写模式,当你输入 ‘;c’ 后,再键入空格、换行等标点符号时, ‘;c’会自动展开为 ‘continue’。
键入 ‘;?’ 或 ‘;C-h’ ,即可查看所有 Fortran 内置缩写及其对应的完整内容列表。
29. 程序编译与测试
上一章介绍了在编辑程序时实用的 Emacs 命令,本章将讲解辅助程序编译和测试过程的相关命令。
29.1. 在 Emacs 中运行编译
Emacs 可为 C、Fortran 等语言调用编译器,并将编译日志输出至 Emacs 缓冲区,同时能解析错误信息并定位错误发生位置。
核心编译命令
M-x compile- 在 Emacs 中异步运行编译器,错误信息输出至
*compilation*缓冲区。 M-x recompileg(编译模式下)- 使用上一次
M-x compile调用的相同命令重新执行编译。 M-x kill-compilation- 终止正在运行的编译子进程。
编译命令的执行规则
执行 make 或其他编译命令时,键入 M-x compile ,Emacs 会通过迷你缓冲区读取一条 Shell 命令,随后以 Emacs 子进程( 或 inferior process 下级进程 )的方式启动 Shell 执行该命令,输出内容会插入至名为 *compilation* 的缓冲区。命令的工作目录为当前缓冲区的默认目录,因此编译操作默认在该目录下执行。
默认编译命令与自定义
默认编译命令为 'make -k' ,该命令通常适用于通过 make 工具编译的程序('-k' 参数表示编译发生错误后,尽可能继续完成后续编译)。相关细节可参考《GNU Make 手册》中的make命令说明。若此前执行过 M-x compile ,所指定的命令会自动保存至变量 compile-command ,并作为下一次 M-x compile 的默认命令。也可为单个文件设置 compile-command 的文件本地值(详见《文件中的本地变量》)。
编译过程的缓冲区与模式行提示
启动编译后, *compilation* 缓冲区会在新窗口中显示,但不会被选中。编译运行期间,该缓冲区的主模式指示器会显示 'run' 运行,所有缓冲区的模式行都会出现 'Compiling' 编译中字样。编译过程中无需保持 *compilation* 缓冲区可见,编译会持续执行。无论因何种原因结束编译, *compilation* 缓冲区的模式行会更新提示:正常退出显示 'exit' 退出(后跟随退出码, [0] 表示正常退出),因信号终止则显示 'signal' 信号。
编译输出的查看方式
若想实时查看编译输出,可切换至 *compilation* 缓冲区并将光标移至缓冲区末尾。光标位于末尾时,新的编译输出会插入在光标上方,且光标始终保持在末尾;若光标未在末尾,新输出会添加至缓冲区末尾,光标位置保持不变。
编译过程中,模式行会实时显示编译器当前输出的错误、警告及信息类提示的数量。
编译输出的自动滚动设置
将变量 compilation-scroll-output 设为非nil值时, *compilation* 缓冲区会自动滚动以跟随编译输出:
- 若值为
first-error,滚动会在首个错误出现时停止,且光标定位至该错误位置; - 若为其他非nil值,滚动会持续至编译输出结束。
重新编译与编译进程的管理
键入 M-x recompile 可使用上一次的命令重新编译,该命令会复用 *compilation* 缓冲区,并在该缓冲区的默认目录(即上一次编译的启动目录)中执行编译,且在 *compilation* 缓冲区中,该命令绑定至快捷键 g 。
启动新的编译时,会终止 *compilation* 缓冲区中正在运行的编译进程(该缓冲区同一时间仅能处理一个编译任务)。但 M-x compile 和 M-x recompile 在终止运行中的编译前会请求确认;若想跳过确认直接终止,可将变量 compilation-always-kill 设为 t ,也可直接使用 M-x kill-compilation 终止编译进程。
若需同时运行两个编译任务,可先启动第一个编译,再重命名 *compilation* 缓冲区(可使用 rename-uniquely 命令,详见《缓冲区的杂项操作》),随后切换缓冲区并启动第二个编译,Emacs 会自动创建新的 *compilation* 缓冲区。
编译的环境变量控制
可通过变量 compilation-environment 控制传递给编译命令的环境变量,该变量的值为环境变量设置列表,每个元素为 "envvarname=value" 格式的字符串,这些设置会覆盖系统默认的环境变量。
超长编译输出行的优化
在编译输出中显示极长的行可能会降低 Emacs 运行效率。长度超过 compilation-max-output-line-length 的行,其超出限制的部分会被隐藏,并显示一个可点击的按钮,点击后可展开隐藏内容;若将该变量设为 nil ,则不会隐藏任何内容。
29.2. 编译模式
*compilation* 缓冲区使用的主模式为 Compilation mode。该模式会将缓冲区中的每条 错误 信息 转换为超链接 ;将光标移至超链接处按下 RET 回车键,或用鼠标点击超链接(参见《通过鼠标跟踪引用》),即可在新窗口中跳转到错误信息对应的代码位置,该位置即错误发生的文件具体行位。
编译模式下 *compilation* 缓冲区的显示效果,可通过自定义相关高亮面版来控制,不同面版对应缓冲区不同内容的高亮样式,例如 compilation-error (编译错误面版)和 compilation-warning (编译警告面版)分别用于高亮错误和警告信息。需注意,这类面版均继承自 error (错误通用面版)和 warning (警告通用面版),因此也可直接自定义父级面版,实现对所有错误、警告类高亮的统一设置。
键入 M-x customize-group RET compilation 可打开编译模式的自定义组,查看所有相关的自定义变量和面版。
若将变量 compilation-auto-jump-to-first-error 设为非nil值,Emacs 会在 *compilation* 缓冲区出现第一条错误信息时,自动跳转到其对应的代码位置。(该变量还可设为 if-location-known 和 first-known ,两种值会分别修改自动跳转错误位置的触发条件。)
编译模式提供了以下附加命令,这些命令同样可在 *grep* 缓冲区中使用 —— 该缓冲区中,超链接对应的是搜索匹配结果而非错误信息(参见《在 Emacs 中使用 Grep 搜索》)。
编译模式核心快捷键与命令
- M-g M-n=
M-g nC-x `- 跳转到下一条错误信息或匹配结果对应的位置 (
next-error) 。 M-g M-pM-g p- 跳转到上一条错误信息或匹配结果对应的位置 (
previous-error) 。 M-n- 将光标移至下一条错误信息或匹配结果处,不跳转至其对应的代码位置 (
compilation-next-error) 。 M-p- 将光标移至上一条错误信息或匹配结果处,不跳转至其对应的代码位置 (
compilation-previous-error) 。 M-}- 将光标移至另一个文件中的下一条错误信息或匹配结果处 (
compilation-next-file) 。 M-{- 将光标移至另一个文件中的上一条错误信息或匹配结果处 (
compilation-previous-file) 。 C-c C-f- 切换下一条错误跟随次模式,开启后,在编译缓冲区中移动光标时,会自动在对应窗口显示光标所在错误的源代码位置。
g- 重新执行最后一次在
*compilation*缓冲区输出结果的命令(即重新编译 / 重新执行 grep 等)。 M-x next-error-select-buffer- 选择一个缓冲区,作为后续执行
next-error和previous-error命令时的操作对象。
错误信息的顺序遍历
要按顺序跳转到各错误位置,可键入 C-x ` (next-error) ,也可使用等效快捷键 M-g M-n 或 M-g n 。该命令可在 任意缓冲区 中调用,并非仅限编译模式缓冲区。 编译完成后首次调用该命令,会跳转到第一条错误信息对应的位置;后续每次键入 M-g M-n ,会依次跳转到下一条错误位置。若在 *compilation* 缓冲区中通过 RET 回车键或鼠标点击,跳转到了某条特定错误的位置,后续的 M-g M-n 会从该错误开始继续向后遍历。当 M-g M-n 遍历至最后一条错误无后续内容时,会触发错误提示;键入 C-u M-g M-n 可重置遍历位置,从编译缓冲区开头重新开始,跳转到第一条错误的位置。
M-g M-p 或 M-g p (previous-error) 则按反向顺序遍历错误信息。
next-error 和 previous-error 命令并非仅作用于 *compilation* 和 *grep* 缓冲区中的错误 / 匹配结果,还支持遍历其他命令生成的错误 / 匹配列表,例如 M-x occur (参见《其他搜索与循环命令》)生成的匹配结果。命令的遍历规则为:若当前缓冲区包含错误 / 匹配信息,则直接遍历该缓冲区;若没有,Emacs 会先在选中框架的所有窗口中查找包含错误 / 匹配信息的缓冲区(若变量 next-error-find-buffer-function 自定义为 next-error-buffer-on-selected-frame ),再查找此前被 next-error / previous-error 命令使用过的缓冲区,最后查找所有其他缓冲区。被遍历的缓冲区若未在任何窗口中显示,会自动在窗口中展示。可通过 next-error-select-buffer 命令切换操作缓冲区,让后续的 next-error 命令作用于新选择的缓冲区。
默认情况下, next-error 和 previous-error 会跳过重要性较低的信息,该行为由变量 compilation-skip-threshold 控制:
- 默认值1:跳过所有比警告级别低的信息;
- 值为2:跳过所有比错误级别低的信息(即仅遍历错误);
- 值为0:不跳过任何信息,遍历所有内容。
错误位置的高亮与定位提示
当 Emacs 跳转到错误信息对应的代码位置时,会对相关源代码行进行 临时高亮 ,高亮持续时长由两个变量控制:选中缓冲区中的错误位置,由 next-error-highlight 定义;非选中缓冲区中的错误位置,由 next-error-highlight-no-select 定义。此外,还可自定义变量 next-error-message-highlight ,设置在信息缓冲区中如何高亮当前选中的错误信息。
若显示 *compilation* 缓冲区的窗口带有 左侧边缘区 (参见《窗口边缘区》),跳转到错误位置的命令会在边缘区绘制一个箭头,指向当前的错误信息;若窗口无左侧边缘区(如文本终端中的窗口),命令会自动滚动窗口,让当前错误信息显示在窗口顶部。
- 若将变量
compilation-context-lines设为t,会在列号 0 前插入一个可见箭头,替代边缘区箭头作为定位标识; - 若将该变量设为整数值
n,无论窗口是否有边缘区,命令都会滚动窗口,让当前错误信息显示在距离窗口顶部n行的位置; - 变量默认值
nil,即上述默认的边缘区箭头 / 窗口顶部定位行为。
编译输出的过滤与隐藏
编译输出有时会非常冗长,其中大部分内容对用户无实际意义。可通过用户选项 compilation-hidden-output 过滤隐藏无关内容,该选项的值可为一个正则表达式,或正则表达式列表, 匹配正则的输出内容会被设为不可见 。例如,要隐藏递归执行 makefiles 时的冗长输出,可进行如下设置:
(setq compilation-hidden-output '("^make[^\n]+\n"))
错误信息的解析规则
编译模式通过变量 compilation-error-regexp-alist 解析编译器输出的信息,该变量中列出了多种错误信息的格式,并定义了 Emacs 如何从每种格式中提取错误对应的代码位置。与之类似的变量 grep-regexp-alist ,则定义了 Emacs 如何解析 grep 命令的输出结果(参见《在 Emacs 中使用 Grep 搜索》)。
有时 compilation-error-regexp-alist 无法正确识别错误对应的源文件名,此时可使用用户选项 compilation-transform-file-match-alist 进行必要调整,例如为文件名添加 / 修改目录组件,甚至将某些编译器信息直接排除在错误信息之外。
编译模式的额外按键与次模式
编译模式还为部分基础操作绑定了快捷键:
- 空格键(SPC)和删除键(DEL):按屏滚动缓冲区;
M-n(compilation-next-error) 和M-p(compilation-previous-error) :移动光标至下一条 / 上一条错误信息;M-{(compilation-next-file) 和M-}(compilation-previous-file) :移动光标至另一个源文件中的下一条 / 上一条错误信息。
键入 C-c C-f 可切换 下一条错误跟随模式 ,开启该次模式后,在编译缓冲区中进行普通的光标移动时,会自动更新源代码缓冲区 —— 即光标移至某条错误信息上时,会立即显示该错误对应的代码位置。
编译模式的所有功能,也可通过编译次模式在其他缓冲区中使用,该次模式允许在 任意缓冲区 中解析错误信息,而非仅限标准的编译输出缓冲区。键入 M-x compilation-minor-mode 即可启用该次模式。例如,在 Rlogin 缓冲区中(参见《远程主机 Shell》),启用编译次模式后,可通过 FTP 自动访问远程源文件(参见《文件名》),解析远程编译的错误信息并跳转到对应代码位置。
29.3. 编译用子 shell
本节介绍在编译缓冲区中使用 Shell 及其相关功能的各类技巧与注意事项。本部分内容仅适用于 本地编译 ,对于默认目录位于远程主机的编译缓冲区,这些方法大概率无法生效(或不具备实际意义)。
M-x compile 命令会通过 Shell 执行编译指令,但会为 Shell 指定 非交互式 运行选项。这意味着,该 Shell 启动时不应显示命令行提示符。若你发现常用的 Shell 提示符出现在 *compilation* 缓冲区中,造成显示混乱,原因是你在 Shell 的初始化文件中 无条件设置了提示符 (该初始化文件的名称依 Shell 类型而定,可能是 .bashrc 、 .profile 、 .cshrc 、 .shrc 等)。正确的做法是,仅当 Shell 检测到已存在提示符时,再对其进行设置。bash 中的设置方式:
sh if [ "${PS1+set}" = set ] then PS1=… fi
csh 中的设置方式:
csh if ($?prompt) set prompt = …
若需自定义传递给编译子 Shell 的 TERM 环境变量值,可通过自定义变量 comint-terminfo-terminal 实现(参见《Shell 模式选项》)。
Emacs 并不支持编译器进程启动 异步子进程 ;若编译器进程启动了此类异步子进程,且主编译器进程终止后这些子进程仍在运行,Emacs 可能会将其强制终止,或这些子进程的输出无法正常传入 Emacs 中。要避免该问题,需让主编译进程 等待其所有子进程执行完毕后再退出 。在 Shell 脚本中,可通过 '$!' 和 'wai' t命令实现该功能,示例如下:
(sleep 10; echo 2nd)& pid=$! # 记录子进程的进程号 echo first message wait $pid # 等待该子进程执行完毕
若后台子进程无需向编译缓冲区输出内容,仅需防止其在主编译进程终止时被 Emacs 杀死,执行以下命令即可实现:
nohup command; sleep 1
在 MS-DOS 系统中, 不支持异步子进程 ,因此 M-x compile 会 同步执行 编译命令(即必须等待编译命令执行完毕后,才能在 Emacs 中进行其他操作)。相关细节参见《Emacs 与 MS-DOS》。
29.4. 在 Emacs 中使用 Grep 搜索
如同在 Emacs 中运行编译器并跳转到含编译错误的代码行一样,你也可以在 Emacs 中运行 grep 命令,再跳转到匹配到结果的代码行。这一功能的实现原理是,将 grep 报告的匹配结果当作错误信息来处理。 grep 命令的输出缓冲区使用Grep mode,该模式是编译模式的一个变体(参见《编译模式》)。
核心 Grep 相关命令
M-x grepM-x lgrep- 在 Emacs 中异步运行
grep命令,在名为*grep*的缓冲区中列出匹配到的行。 M-x grep-findM-x find-grepM-x rgrep- 通过
find命令调用grep进行搜索,将输出结果汇总至*grep*缓冲区。 M-x zrgrep- 运行
zgrep命令,将输出结果汇总至*grep*缓冲区。 M-x kill-grep- 终止正在运行的
grep子进程。
基础 Grep 命令使用方法
键入=M-x grep= ,随后输入执行 grep 的命令行即可运行该工具,命令行参数与在终端中正常运行 grep 时一致:先输入 grep 风格的正则表达式(通常用单引号包裹,以转义 Shell 的特殊字符),后接文件名,文件名可使用通配符。若为 M-x grep 指定前缀参数,Emacs 会在光标所在缓冲区中查找标识符(参见《查找标识符引用》),并将该标识符作为 grep 命令的默认搜索内容。
你输入的命令并非只能单纯运行 grep ,也可使用任何能输出 相同格式结果 的 Shell 命令。例如,可将多个 grep 命令串联使用,示例如下:
grep -nH -e foo *.el | grep bar | grep toto
grep 命令的输出会写入 *grep* 缓冲区,你可以使用 M-g M-n 、 RET 回车键等方式跳转到原文件中对应的匹配行,操作方式与跳转到编译错误行完全相同。关于 *grep* 缓冲区中可用的命令和快捷键绑定细节,参见《编译模式》章节。
部分 grep 程序支持 '--color' 选项,该选项会在匹配结果两侧添加特殊标记以实现高亮效果。将变量 grep-highlight-matches 设为 t 即可启用该功能,此时在源代码缓冲区中显示匹配结果时, 仅会高亮精确匹配的内容 ,而非整行源代码。该高亮功能通过匹配 grep 输出的 ANSI 转义序列实现,序列的匹配规则由变量 grep-match-regexp 控制,可通过自定义该变量适配不同的 grep 程序。
与编译命令的行为一致(参见《在 Emacs 中运行编译》), grep 命令运行期间,模式行会实时显示当前已找到并高亮的匹配结果数量。
Grep 运行前的缓冲区保存设置
grep 命令运行前,Emacs 会提示是否保存相关缓冲区,该行为由变量 grep-save-buffers 控制,其可选值如下:
nil:不保存任何缓冲区;ask:保存前进行询问(默认值);- 自定义函数:将该函数作为判定谓词(函数会以文件名为参数被调用,返回非nil时表示保存对应缓冲区);
- 其他非nil值:无需询问,直接保存所有缓冲区。
Grep 结果的显示格式定制
默认情况下,grep 匹配结果的每一行前都会添加文件名前缀。若将变量 grep-use-headings 自定义为非nil值,匹配结果会按文件进行分块展示 —— 每个包含匹配结果的文件对应一个分块,文件名会以 grep-heading 专用面版样式显示在分块标题处。
高级 Grep 搜索命令
命令 M-x grep-find (也可通过 M-x find-grep 调用)与 M-x grep 功能类似,但会提供不同的初始默认命令 —— 该默认命令会同时运行 find 和 grep ,从而实现对 整个目录树 中的所有文件进行搜索。相关还可参见《Dired 与 find》中的 find-grep-dired 命令。
命令 M-x lgrep (本地 grep )和 M-x rgrep (递归 grep )是grep和grep-find的 更友好版本 ,这两个命令会分别提示你输入待匹配的正则表达式、待搜索的文件、搜索的基准目录。搜索的大小写敏感性由变量 case-fold-search 的当前值控制。命令 M-x zrgrep 与 M-x rgrep 功能类似,区别在于其调用 zgrep 而非 grep,用于搜索压缩文件(gzipped) 中的内容。
上述这些命令会基于以下变量构建对应的 Shell 命令:
grep-template:为lgrep提供命令模板;grep-find-template:为rgrep提供命令模板。- 待搜索的文件名可使用变量
grep-files-aliases中定义的别名。
变量 grep-find-ignored-directories 中列出的目录,会被 M-x rgrep 自动跳过,该变量的默认值包含各类版本控制系统使用的数据目录。
Grep 命令行的缩写显示
默认情况下,为lgrep、rgrep、zrgrep构建的 Shell 命令会进行缩写显示 —— 命令中包含待忽略的长文件 / 目录列表的部分会被隐藏,仅显示省略号按钮。点击该省略号按钮即可展开隐藏的内容,也可键入 M-x grep-find-toggle-abbreviation 交互式切换是否显示隐藏部分。若要禁用 Shell 命令的缩写功能,将选项 grep-find-abbreviate 自定义为 nil 即可。
29.5. 实时语法错误检测
Flymake 模式是一款次模式,可为 C、C++、Perl、HTML、TeX/LaTeX 等多种编程语言和标记语言执行实时语法检查。该模式与拼写检查的 Flyspell 模式原理相似,后者以同类实时方式为自然语言做拼写检查(参见《拼写检查与修正》)。在你编辑文件时,Flymake 模式会在后台利用缓冲区的临时副本,调用适配的语法检查工具;随后解析工具输出的错误与警告信息,并在缓冲区中高亮显示存在错误的代码行。所使用的语法检查工具依语言而定,例如对 C/C++ 文件,默认使用 C 语言编译器;对于复杂的项目工程,Flymake 也可调用 make 等构建工具完成语法检查。
启用 Flymake 模式可键入命令 M-x flymake-mode 。你可通过 M-x flymake-goto-next-error 和 M-x flymake-goto-prev-error 命令,跳转到模式检测到的错误位置。若要查看当前缓冲区的语法诊断详细汇总,可使用 M-x flymake-show-buffer-diagnostics 命令;若要查看整个项目的语法诊断汇总(参见《项目操作》),可使用 M-x flymake-show-project-diagnostics 命令。
关于 Flymake 模式的更多使用细节,参见《Flymake 手册》中的相关章节。
29.6. 在 Emacs 中运行调试器
GUD(统一调试器,Grand Unified Debugger)库为各类符号调试器提供了 Emacs 交互接口,可运行 GNU 调试器(GDB),同时也支持 LLDB、DBX、SDB、XDB、Guile 交互式解释器调试命令、Perl 调试模式、Python 调试器 PDB 以及 Java 调试器 JDB。
Emacs 为 GDB 提供了专用交互接口,该接口会通过额外的 Emacs 窗口展示被调试程序的运行状态,详见GDB 图形化接口章节。
Emacs 还内置了针对 Emacs Lisp 程序的调试器,详见《Emacs Lisp 参考手册》中的Lisp 调试器章节。
29.6.1. 启动 GUD 调试器
Emacs 提供了多个用于启动调试器子进程的命令,每个命令对应一款特定的调试器程序。
M-x gdb- 将 GDB 作为子进程运行,并通过类集成开发环境(IDE)的 Emacs 交互界面与其交互。有关该命令的更多信息,参见GDB 图形化接口章节。
M-x gud-gdb- 运行 GDB,通过 GUD 交互缓冲区实现与 GDB 子进程的输入输出交互(参见调试器操作章节)。若该类缓冲区已存在,则直接切换至该缓冲区;若不存在,则创建并切换至该缓冲区。
下述命令的工作逻辑与上述一致,分别对应其他调试器程序:
M-x lldb- 运行 LLDB 调试器。
M-x perldb- 以调试模式运行 Perl 解释器。
M-x jdb- 运行 Java 调试器。
M-x pdb- 运行 Python 调试器。
M-x guiler- 启动 Guile 交互式解释器,用于调试 Guile Scheme 程序。
M-x dbx- 运行 DBX 调试器。
M-x xdb- 运行 XDB 调试器。
M-x sdb- 运行 SDB 调试器。
上述每一个命令都会通过迷你缓冲区读取一条用于调用调试器的命令行。迷你缓冲区的初始内容包含该调试器的标准可执行文件名、运行选项,有时还会自动填充待调试可执行文件的猜测名称。该命令行中不允许使用 Shell 通配符和环境变量。Emacs 会将命令行中第一个不以短横线 "-" 开头的参数识别为待调试的可执行文件名。
Tramp 工具提供了远程调试功能,可让调试器与待调试程序运行在同一台远程主机上。具体细节参见《Tramp 手册》中的在远程主机上运行调试器章节。此功能与 GDB 自身的远程调试特性相区分 ——GDB 原生远程调试支持调试器与待调试程序运行在不同的机器上(参见《GNU 调试器手册》中的调试远程程序章节)。
29.6.2. 调试器操作
GUD interaction buffer GUD 交互缓冲区是 Emacs 的专用缓冲区,用于向调试器子进程发送文本命令并记录其输出结果。这是与调试器交互的基础接口, M-x gud-gdb 及《启动 GUD》章节中列出的其他命令均会使用该接口。而 M-x gdb 命令对该接口进行了扩展,新增了多个专用缓冲区,分别用于控制断点、栈帧及调试器状态的其他相关项(参见GDB 图形化接口章节)。
GUD 交互缓冲区采用 Shell 模式的变体,因此 Shell 模式下定义的所有 Emacs 命令均能在此使用(参见《Shell 模式》)。绝大多数调试器命令都支持补全功能(参见《命令补全》),你也可以使用 Shell 模式常规的历史命令功能重复执行调试命令。关于可在 GUD 交互缓冲区中使用的专用命令,详见GUD相关命令章节。
调试程序时,Emacs 会通过在缓冲区中打开相关源文件的方式展示代码, 左侧边缘区 会显示一个箭头,指向当前程序的执行行(在文本终端中,该箭头会以 "=>" 的形式显示,覆盖在文本的前两列位置)。在该缓冲区中移动光标,并不会改变箭头的位置。你可以随意编辑这些源文件,但需注意:插入或删除代码行会导致箭头定位失效,因为 Emacs 无法判断编辑后的源代码行,与调试器子进程报告的执行行存在怎样的对应关系。若要更新执行行的定位信息,通常需要重新编译并重启程序。
此外,GUD 还能通过两种方式之一,在窗口文本内为当前执行行添加视觉标记:
- 若启用用户选项
gud-highlight-current-line,会为当前执行行添加一个覆盖层,其显示样式由gud-highlight-current-line-face面版定义; - 若启用高亮行模式(HL Line Mode,参见《光标显示》),会将该模式生成的覆盖层临时移至当前执行行,直至执行后续的编辑命令,覆盖层才会重新回到光标所在行。
GUD 工具提示模式是一款全局次模式,为 GUD 增加了工具提示支持,键入 M-x gud-tooltip-mode 可切换该模式的开启 / 关闭状态,其默认处于禁用状态。启用后,将鼠标指针移至变量、函数或宏(统称标识符)上方时,会在工具提示中显示其对应的值(参见《工具提示》)。若仅将鼠标指针悬停在表达式上,未显示预期的表达式值,可通过鼠标拖动选中该表达式,且保持鼠标指针停留在选中区域内,以此明确告知 Emacs 需要计算的表达式。GUD 工具提示模式会在 GUD 交互缓冲区,以及所有主模式包含在变量 gud-tooltip-modes 中的源文件缓冲区中生效。若禁用该模式,标识符的值会显示在回显区,而非工具提示中。
在 M-x gud-gdb 中使用 GUD 工具提示模式时,GDB 中显示表达式的值有时会展开宏,这可能会对被调试的程序造成 副作用 ,因此 gud-gdb 中默认禁用了工具提示功能。而在 M-x gdb 接口中则不存在该问题 —— 该接口包含专门的代码以避免副作用,此外,即便程序未处于执行状态,你也能通过该接口查看与标识符关联的宏定义。
29.6.3. GUD 调试器命令
GUD 提供了设置 / 清除断点、选择栈帧以及单步执行程序的相关命令。
C-x C-a C-b- 在光标所在的源代码行设置断点(
gud-break)。
在源码缓冲区中调用该命令 (gud-break) ,会在当前源代码行设置调试器断点,此命令仅在启动 GUD 后可用;若在未关联任何调试器子进程的缓冲区中调用,将触发错误。
以下命令可在 GUD 交互缓冲区和全局环境中使用,但绑定的快捷键不同:以 C-c 开头的快捷键仅在 GUD 交互缓冲区生效,以 C-x C-a 开头的快捷键为全局生效。其中部分命令也可通过工具栏调用,部分命令并非所有调试器都支持。
C-c C-lC-x C-a C-l- 在新窗口中显示 GUD 交互缓冲区中最近引用的源代码行 (
gud-refresh) 。 C-c C-sC-x C-a C-s- 执行下一行代码 (
gud-step,单步进入) 。若该行包含函数调用,程序会在进入被调用函数后暂停。 C-c C-nC-x C-a C-n- 执行下一行代码,跳过函数调用且不在函数内部暂停 (
gud-next,单步跳过) 。 C-c C-iC-x C-a C-i- 执行单条机器指令(gud-stepi,单步执行指令)。
C-c C-pC-x C-a C-p- 计算光标所在位置的表达式值(gud-print,打印表达式)。若 Emacs 未能识别你需要计算的准确表达式,可先将其标记为选区。
C-c C-rC-x C-a C-r- 继续执行程序,无需指定暂停点(gud-cont,继续执行)。程序会持续运行,直至触发断点、执行终止,或接收到调试器正在监听的信号。
C-c C-dC-x C-a C-d- 删除当前源代码行上的所有断点(若存在)(gud-remove,移除断点)。若在 GUD 交互缓冲区中调用该命令,将作用于程序上一次暂停的代码行。
C-c C-tC-x C-a C-t- 在当前源代码行设置临时断点(若存在)(gud-tbreak,临时断点)。若在 GUD 交互缓冲区中调用该命令,将作用于程序上一次暂停的代码行。
C-c <C-x C-a <- 选择外层的下一个栈帧(gud-up,上移栈帧),等效于 GDB 中的 up 命令。
C-c >C-x C-a >- 选择内层的下一个栈帧(gud-down,下移栈帧),等效于 GDB 中的 down 命令。
C-c C-uC-x C-a C-u- 继续执行程序至当前光标所在行(gud-until,执行至指定行)。程序会持续运行,直至触发断点、执行终止、接收到调试器正在监听的信号,或运行到光标当前所在行。
C-c C-fC-x C-a C-f- 运行程序,直至选中的栈帧执行返回,或因其他原因暂停(gud-finish,执行至函数结束)。
GDB 专属快捷键
若使用 GDB 调试器,还可使用以下附加快捷键:
C-x C-a C-j- 仅在源码缓冲区中生效,
gud-jump会将程序的执行点跳转到当前光标所在行。即程序下一行执行的代码,为调用该命令时光标所在的行。若新的执行行与上一行属于不同函数,GDB 会弹出确认提示(因该操作可能导致程序执行结果异常),详细说明可参考 GDB 手册中关于jump命令的章节。 TAB- 在 GDB 中补全符号名(gud-gdb-complete-command,符号补全),该快捷键仅在 GUD 交互缓冲区中生效。
补充说明
- 上述命令中,若数字参数具备实际意义,可将其视为命令的重复执行次数。
- 由于 TAB 键被用作补全命令,在使用 GDB 调试时,无法直接用其向被调试程序输入制表符,需输入
C-q TAB来插入制表符。
29.6.4. GUD 调试器定制
GUD 启动时会运行下述钩子函数之一:使用 GDB 时执行 gdb-mode-hook ;使用 DBX 时执行 dbx-mode-hook ;使用 SDB 时执行 sdb-mode-hook ;使用 XDB 时执行 xdb-mode-hook ;调试 Guile 交互式解释器时执行 guiler-mode-hook ;Perl 调试模式下执行 perldb-mode-hook ;PDB 调试模式下执行 pdb-mode-hook 。详见钩子函数相关说明。
Lisp 宏 gud-def (参见《Emacs Lisp 参考手册》中宏定义相关章节)提供了一种便捷方式,可定义向调试器发送指定命令字符串的 Emacs 命令,并为其在 GUD 交互缓冲区中配置按键绑定,语法如下:
(gud-def function cmdstring binding docstring)
该宏定义一个名为 function 的命令,功能为向调试器进程发送字符串 cmdstring ,并为其添加说明文档字符串 docstring 。定义完成后,可在任意缓冲区中调用该 function 命令。若 binding 参数非空, gud-def 还会将该命令绑定至 GUD 缓冲区模式下的 C-c binding 快捷键,同时全局绑定为 C-x C-a binding 快捷键。
命令字符串 cmdstring 中可包含特定的 % 格式序列,这类序列会在 function 命令被调用时填充对应数据,各格式序列含义如下:
%f- 当前源文件的文件名。若当前缓冲区为 GUD 缓冲区,则指程序暂停执行时所在的源文件。
%l- 当前源文件的行号。若当前缓冲区为 GUD 缓冲区,则指程序暂停执行时所在的代码行号。
%e- 若开启临时标记模式(Transient Mark mode)且选区处于激活状态,为选区中的文本;否则为光标所在位置或相邻位置的 C 语言左值或函数调用表达式文本。
%a- 光标所在位置或相邻位置的十六进制地址文本。
%p被调用函数的数字参数,以十进制数表示。若调用命令时未传入数字参数,
%p将被替换为空字符串。若命令字符串中未使用
%p,则所定义的命令会忽略所有传入的数字参数。%d- 当前源文件所在的目录名。
%c- 由光标周围的表达式推导得到的全限定类名(仅适用于 JDB 调试器)。
29.6.5. GDB 图形界面
M-x gdb 命令会以类集成开发环境(IDE)的界面启动 GDB,同时创建专用缓冲区,用于管理断点、栈帧及调试器状态的其他相关内容。该界面还支持通过鼠标操控调试会话,例如在源码缓冲区的页边区域点击,即可在对应位置设置断点。
若仅需使用 GUD 交互缓冲区界面运行 GDB,而不启用上述附加功能,可执行 M-x gud-gdb 命令(参见启动 GUD相关章节)。
在内部实现上, M-x gdb 会告知 GDB 其屏幕显示尺寸无限制;为保证调试正常运行,在调试会话期间请勿修改 GDB 的屏幕高度和宽度参数。
29.6.5.1. GDB 图形界面布局
若变量gdb-many-windows的值为nil(默认设置),执行M-x gdb通常仅显示 GUD 交互缓冲区。而若变量gdb-show-main的值非nil,启动时会打开两个窗口:一个显示 GUD 交互缓冲区,另一个展示待调试程序中main函数的源码。
若gdb-many-windows的值非nil,执行M-x gdb会显示如下窗口布局
+-------------------------+--------------------------------+ | GUD 交互缓冲区 | 本地变量 / 寄存器缓冲区 | +-------------------------+--------------------------------+ | 主源码缓冲区 | 被调试程序的输入 / 输出缓冲区 | +-------------------------+--------------------------------+ | 栈缓冲区 | 断点 / 线程缓冲区 | +-------------------------+--------------------------------+
你可基于上述布局自定义窗口样式,并通过 gdb-save-window-configuration 将该布局保存至文件,后续可使用 gdb-load-window-configuration 重新加载该布局。(Emacs 内部使用 term window configuration 窗口配置 一词,而非 window layout窗口布局 。)通过自定义变量 gdb-default-window-configuration-file ,可将你的自定义布局设为 gdb-many-windows 使用的默认布局。若该变量指定的并非绝对文件名,GDB 会在 gdb-window-configuration-directory 目录下查找该文件,此目录的默认值为 user-emacs-directory (参见《Emacs 如何查找初始化文件》章节)。
若你修改了窗口布局,可输入 M-x gdb-restore-windows 恢复默认布局。输入 M-x gdb-many-windows 可在 多窗口布局 与 简易布局 间切换,简易布局仅包含 GUD 交互缓冲区和一个源码文件窗口。
若你已有复杂的窗口配置,且不想让 gdb-many-windows 破坏该配置,建议在独立的 Emacs 框架中执行 M-x gdb ,这样原有框架的窗口布局就不会受到影响。在纯文本模式终端中工作时,为 GDB 调试会话单独创建一个框架会格外实用 —— 这类终端的窗口显示空间通常十分宝贵。若你选择在原有框架中启动 GDB,可将变量 gdb-restore-window-configuration-after-quit 设为非nil值,这样 GDB 退出后,Emacs 会自动恢复你原本的窗口布局。该变量可设为以下值:设为 t 则始终恢复原有布局;设为 if-gdb-many-windows 则仅当 gdb-many-windows 非nil时恢复;设为 if-gdb-show-main 则仅当 gdb-show-main 非nil时恢复。
你也可指定额外的 GDB 相关缓冲区进行显示,这些缓冲区可置于当前框架,也可置于其他独立框架。输入 M-x gdb-display-<buffertype>-buffer 或 M-x gdb-frame-<buffertype>-buffer 即可选择要显示的缓冲区(其中 <buffertype> 为缓冲区类型,如breakpoints(断点)、io(输入输出)等)。你也可通过菜单栏操作:在GUD菜单下的 'GDB-Windows' 和 'GDB-Frames' 子菜单中完成上述操作。
默认情况下,GDB 最多仅使用一个窗口显示源码文件。通过自定义变量 gdb-max-source-window-count ,可让 GDB 使用更多窗口显示源码;同时也可自定义 gdb-display-source-buffer-action ,以此控制 GDB 显示源码文件的方式。
调试结束后,可使用 C-x k 关闭 GUD 交互缓冲区,该操作会同时关闭所有与本次调试会话关联的缓冲区。但如果在 Emacs 中编辑并重新编译源码后,你希望继续调试,则无需执行此关闭操作:重启程序执行时,GDB 会自动找到新的可执行文件。保留 GUD 交互缓冲区的优势在于,可同时保留 Shell 命令历史记录和 GDB 中设置的所有断点。你仅需检查近期编辑过的源码文件中,断点是否仍处于正确位置即可。
29.6.5.2. 源代码缓冲区
mouse-1(点击页边区)- 在对应行设置或清除断点 (
gdb-mouse-set-clear-breakpoint) 。 C-mouse-1(点击页边区)- 启用或禁用对应行的断点 (
gdb-mouse-toggle-breakpoint-margin) 。 mouse-3(点击页边区)- 继续执行程序至对应行 (
gdb-mouse-until) 。 C-mouse-3(点击页边区)- 将程序执行点跳转到对应行 (
gdb-mouse-jump) 。
在图形化显示界面中,可在源码缓冲区的页边区点击鼠标左键(mouse-1),为对应行设置断点(参见《窗口页边》相关章节)。点击位置的页边区会出现一个红点,若该位置已存在断点,点击操作会将其清除。 C-mouse-1 可启用或禁用已存在的断点;已禁用但未清除的断点,会在页边区以灰点标识。
在文本终端中,或页边区被禁用时,已启用的断点会在窗口左边界以字符 'B' 标识,已禁用的断点则以字符 'b' 标识(仅当存在断点时,该边界标识才会显示)。
源码缓冲区左页边区的 实心箭头 ,标识被调试程序暂停时,最内层栈帧对应的代码行; 空心箭头 则标识高层级栈帧的当前执行行。若按住 mouse-1 鼠标左键拖动页边区的箭头,释放鼠标时,程序会执行至箭头释放位置对应的行。也可在页边区点击鼠标右键(mouse-3),让程序执行至对应行;按住 C-mouse-3 可直接跳转到对应行,无需执行中间的代码行。该跳转命令支持回退执行,可用于重新运行已执行过的代码,以便更细致地检查其执行过程。
默认情况下,被调试程序中的源文件名和非 ASCII 字符串,会通过 Emacs 默认编码系统解码。若需要使用其他编码解码(例如被调试程序采用了不同的字符编码),可将变量 gdb-mi-decode-strings 设为对应的编码系统;若将该变量设为 nil ,非 ASCII 字符会保留为未解码的八进制转义序列。
29.6.5.3. 断点缓冲区
GDB 断点缓冲区会显示调试会话中的断点、观察点和捕获点(详见《GNU 调试器》中的断点相关章节)。该缓冲区提供下述命令,且这些命令大多作用于当前断点(即光标所在位置的断点):
SPC- 启用 / 禁用当前断点 (
gdb-toggle-breakpoint) 。在图形化显示界面中,此操作会改变对应源码行页边区断点标识点的颜色:断点启用时标识点为红色,禁用时为灰色。 D- 删除当前断点 (
gdb-delete-breakpoint) 。 RET- 跳转到当前断点对应的源码行 (
gdb-goto-breakpoint) 。 mouse-2- 跳转到鼠标点击位置的断点对应的源码行 (
gdb-goto-breakpoint) 。
当变量 gdb-many-windows 值为非nil时,GDB 断点缓冲区与 GDB 线程缓冲区共用同一个窗口。可通过在窗口标题行的对应按钮上点击 mouse-1 ,实现两个缓冲区之间的切换。若变量 gdb-show-threads-by-default 值为非nil,则默认显示 GDB 线程缓冲区。
29.6.5.4. 线程缓冲区
GDB 线程缓冲区会展示被调试程序中所有线程的概览信息(详见《GNU 调试器》中的多线程程序调试章节)。选中线程的操作方式为:将光标移至对应线程行并按下 RET 回车键 (gdb-select-thread) ,或使用 mouse-2 鼠标中键点击该线程行。执行选中操作后,Emacs 会同步显示该线程对应的源码缓冲区,并更新其他所有 GDB 相关缓冲区的内容。
你可通过自定义 gdb-buffers 配置组中的相关变量,选择线程缓冲区中需要展示的信息字段:
gdb-thread-buffer-verbose-names- 展示线程的完整名称,格式示例: 'Thread 0x4e2ab70 (LWP 1983)' 。
gdb-thread-buffer-arguments- 展示各线程顶层栈帧的入参信息。
gdb-thread-buffer-locations- 展示线程对应的文件信息或库名称。
gdb-thread-buffer-addresses- 在线程缓冲区中展示各线程栈帧的内存地址。
若需同时查看多个线程的详细信息,可在 GDB 线程缓冲区中执行以下命令,所有命令均作用于光标当前所在的线程行:
d- 为当前线程展示反汇编缓冲区 (
gdb-display-disassembly-for-thread) 。 f- 为当前线程展示 GDB 栈缓冲区 (
gdb-display-stack-for-thread) 。 l- 为当前线程展示 GDB 本地变量缓冲区 (
gdb-display-locals-for-thread) 。 r- 为当前线程展示 GDB 寄存器缓冲区 (
gdb-display-registers-for-thread) 。
上述命令对应的大写形式D、F、L、R,会在新的 Emacs 帧中打开对应的缓冲区。
当你为某一特定线程创建专属信息缓冲区后,该缓冲区会与该线程建立绑定关系;在后续调试过程中,该缓冲区会持续展示此线程的实时信息。每个 GDB 缓冲区的模式指示区,都会标注出该缓冲区所绑定的线程编号;同时,该线程编号也会包含在所有绑定缓冲区的名称中,便于识别。
GDB 线程缓冲区中还提供了其他调试命令,这类命令的功能取决于用于控制程序执行的 GDB 运行模式,相关说明详见多线程调试章节。
29.6.5.5. 堆栈缓冲区
GDB 栈缓冲区用于展示程序的调用栈,调试会话中每一层嵌套的子程序调用( stack frames栈帧 )各占一行显示(详见《GNU 调试器》中的回溯信息相关章节)。
在图形化显示界面中,已选中的栈帧会在页边区以箭头标识;在文本终端中,或页边区被禁用时,已选中的栈帧会以反色显示。选中栈帧的操作方式为:将光标移至对应栈帧行并按下 RET 回车键 (gdb-frames-select) ,或用 mouse-2 鼠标中键点击该栈帧行。执行此操作后,本地变量缓冲区的内容也会同步更新(参见其他 GDB 缓冲区章节)。
若希望在每个栈帧行旁显示帧地址,可将变量 gdb-stack-buffer-addresses 自定义设为非 nil 值。
29.6.5.6. 其他 GDB 缓冲区
执行 M-x gdb 后可按需调出的其他缓冲区包括以下几类:
- Locals Buffer 本地变量缓冲区
该缓冲区展示当前栈帧中简单数据类型的本地变量值(详见《GNU 调试器》中帧的相关信息章节)。若需编辑变量值,可将光标移至对应值处按下回车键,或用鼠标中键点击该值。
数组与结构体类型仅展示类型信息,不显示具体值。在 GDB 6.4 及更高版本中,将光标移至本地变量处按回车键或鼠标中键点击,即可查看其具体值;在更早的 GDB 版本中,需在类型描述('
[struct/union]' (结构体 / 联合体)或 '[array]' (数组))上执行RET回车键或mouse-2鼠标中键点击操作。相关说明详见监视表达式章节。调出本地变量缓冲区的命令:
M-x gdb-display-locals-buffer。- I/O Buffer 输入输出缓冲区
- 若被调试程序通过标准输入输出流与用户交互,或向标准输出打印大量内容,可将程序的输入输出与 GDB 的交互操作分离。执行命令
M-x gdb-display-io-buffer,Emacs 会调出独立窗口和缓冲区,并重定向被调试程序的所有输入输出至该缓冲区。若变量gdb-display-io-buffer设为nil,Emacs 则不会创建和显示独立的输入输出缓冲区,程序的输入输出会直接重定向至 GUD 交互缓冲区。 - Registers Buffer 寄存器缓冲区
- 该缓冲区展示处理器寄存器中存储的值(详见《GNU 调试器》中寄存器章节),调出命令为
M-x gdb-display-registers-buffer。若需编辑寄存器值,可将光标移至对应寄存器处按回车键,或用鼠标中键点击。在 GDB 6.4 及更高版本中,近期被修改的寄存器值会以语法高亮警告样式显示。 - Assembler Buffer 汇编缓冲区
- 该缓冲区以机器码形式展示当前栈帧的内容,其中有箭头指向当前执行的指令,且支持像源码缓冲区一样设置和清除断点,断点标识也会显示在页边区或边界处。调出该缓冲区的命令:
M-x gdb-display-disassembly-buffer。 - Memory Buffer 内存缓冲区
- 该缓冲区可用于查看程序内存的指定区域(详见《GNU 调试器》中查看内存章节)。点击缓冲区标题行的对应区域,可修改缓冲区展示的内存起始地址或数据项数量,也可分别通过快捷键
S和N完成上述操作;在标题行点击mouse-3鼠标右键,可选择数据项的展示格式和单位大小。调出内存缓冲区的命令:M-x gdb-display-memory-buffer。
当变量 gdb-many-windows 设为非nil值时,本地变量缓冲区与寄存器缓冲区会共用一个窗口,与断点缓冲区和线程缓冲区的窗口复用方式一致。可通过在窗口标题行的对应按钮上点击 mouse-1 鼠标左键,实现两个缓冲区之间的切换。
29.6.5.7. 监视表达式
若你希望查看程序每次暂停时某个变量的变化情况,可将光标移至该变量名处,点击工具栏中的监视图标(gud-watch) ,或按下快捷键 C-x C-a C-w 。若指定前缀参数,可在迷你缓冲区中手动输入待监视的变量名。
所有监视表达式均会显示在速览栏中(参见《速览栏》章节)。数组、结构体、联合体等复杂数据类型会以树形结构展示:叶子节点与简单数据类型会显示表达式名称及其值,且当选中速览栏时,鼠标悬停会以工具提示形式显示其类型;上层节点中,指针类型会显示名称、类型和地址值,其他类型则仅显示名称与类型;根表达式还会将帧地址作为工具提示展示,助力识别其定义所在的栈帧。
展开或折叠复杂数据类型的操作:在表达式左侧的标签处点击 mouse-2 鼠标中键,或按下 SPC 空格键即可。若某表达式的直接子节点数量超过变量 gdb-max-children 的取值,Emacs 会在展开该表达式前弹出确认提示。
删除复杂监视表达式的操作:将光标移至速度条中该表达式的根节点处,按下 D 键 (gdb-var-delete) 即可。
编辑简单数据类型变量或复杂数据类型中简单元素的操作:将光标移至速度条中对应位置并按下 RET 回车键 (gdb-edit-value) ,或用 mouse-2 鼠标中键点击对应值,两种方式均会通过迷你缓冲区读取你输入的新值。
若将变量 gdb-show-changed-values 设为非nil值(默认配置),Emacs 会用语法高亮警告样式突出显示近期发生变化的值,并使用阴影样式弱化显示已超出作用域的变量。变量超出作用域后,其值将无法被编辑。
若将变量 gdb-delete-out-of-scope 设为非nil值(默认配置),Emacs 会自动删除已超出作用域的监视表达式。当程序多次重新进入同一函数时,可将该变量设为nil,无需重新创建监视表达式,该设置会更实用。
若将变量 gdb-use-colon-colon-notation 设为非nil值,Emacs 会采用 'function::variable' 的命名格式,支持用户显示同名的监视表达式,该变量默认值为 nil 。
若希望监视表达式的显示内容每次更新时,速度条都自动置顶显示,可将变量 gdb-speedbar-auto-raise 设为非nil值。在使用全屏 Emacs 帧进行调试时,该设置会尤为实用。
29.6.5.8. 多线程调试
在 GDB 的全停止模式下,程序暂停时所有执行线程都会停止;同理,重启程序时所有线程也会一并开始执行。详见《GNU 调试器手册》中的「全停止模式」章节。对于部分多线程目标程序,GDB 还支持一种进阶运行模式 —— 非停止模式,该模式下调试器可查看已暂停的程序线程,同时其他线程能不受干扰地继续执行。详见《GNU 调试器手册》中的「非停止模式」章节。GDB 7.0 之前的版本不支持非停止模式,且该模式并非对所有目标程序都适用。
变量 gdb-non-stop-setting 用于决定 Emacs 以全停止模式还是非停止模式运行 GDB,默认值为 t ,即若目标环境支持非停止模式,则优先启用。若将该值改为 nil ,或当前环境不支持非停止模式,Emacs 会以全停止模式运行 GDB。此变量仅在 Emacs 启动调试会话时生效,若修改其值,需重启当前所有活跃的调试会话。
非停止模式下,当某个线程暂停时,Emacs 通常会自动切换至该线程。若希望当已有其他暂停线程被选中时,Emacs 不再执行此自动切换操作,可将变量 gdb-switch-when-another-stopped 设为 nil 。
Emacs 可根据线程的暂停原因,决定是否自动切换至该暂停线程。可自定义变量 gdb-switch-reasons ,指定触发线程自动切换的暂停原因。
变量 gdb-stopped-functions 支持在任意线程暂停时,执行自定义的函数。
非停止模式下,可切换 GUD 执行控制命令的作用模式,主要分为以下两种:
- Non-stop/A
- 当变量
gdb-gud-control-all-threads为t(默认值)时,中断和继续类命令会作用于所有线程:可分别使用gud-stop-subjob和gud-cont命令,一键暂停或继续所有线程。工具栏中,当至少有一个线程处于暂停状态时会显示「Go」按钮;当至少有一个线程处于运行状态时会显示「Stop」按钮。 - Non-stop/T
- 当变量
gdb-gud-control-all-threads为nil时,中断和继续类命令仅作用于当前线程。GUD 工具栏的「Go」和「Stop」按钮,会根据当前线程的状态显示。
可通过工具栏,或「GUD -> GDB-MI」菜单修改 gdb-gud-control-all-threads 的当前值。
单步调试命令始终仅作用于当前线程。
非停止模式下,无需选中线程即可对其执行中断 / 继续操作:在线程缓冲区中,按下 i 键可中断光标所在的线程,按下 c 键可继续该线程,按下 s 键可对该线程执行单步调试。未来可能会新增更多此类快捷命令。
注意:中断线程时,该线程会以「singnal received」为原因暂停。若该原因被包含在 gdb-switch-reasons 的配置中(默认包含),Emacs 会自动切换至该线程。
29.7. 执行 Lisp 表达式
Emacs 为多款 Lisp 方言提供了主模式,这些模式与其他编程语言模式使用相同的编辑命令(参见《编辑程序》章节),并额外提供了执行 Lisp 表达式的专用命令。
- Emacs Lisp mode
- 用于编辑 Emacs Lisp 源文件的模式,该模式将组合键
C-M-x定义为求值当前顶级 Lisp 表达式的快捷键(参见《求值 Emacs Lisp 表达式》章节)。 - Lisp Interaction mode
- 用于 Emacs Lisp 交互式会话的模式,该模式将组合键
C-j定义为求值光标前方的表达式,并将求值结果插入至缓冲区中(参见《Lisp 交互缓冲区》章节)。 - Lisp mode
- 用于编辑非 Emacs Lisp 方言的 Lisp 程序源文件的模式,该模式将组合键
C-M-x定义为在外部 Lisp 环境中求值当前顶级表达式的快捷键(参见《运行外部 Lisp 环境》章节)。 - Inferior Lisp mode
- 用于与外部 Lisp 进行交互式会话的模式,该外部 Lisp 作为 Emacs 的子进程(下级进程)运行(参见《运行外部 Lisp 环境》章节)。
- Scheme mode
- 功能与 Lisp 模式一致,专为 Scheme 程序设计。
- Inferior Scheme mode
- 功能与下级 Lisp 模式一致,专为 Scheme 语言设计。
29.8. Emacs Lisp 代码库
Emacs Lisp 代码存储在惯例以 .el 为后缀的文件中,此类文件被打开时会自动启用 Emacs Lisp 模式。
Emacs Lisp 代码可编译为 byte-code字节码 ,字节码具有加载速度更快、占用空间更小、执行效率更高的特点。按照惯例,编译后的 Emacs Lisp 字节码会单独存放在以 '.elc' 为后缀的文件中,例如 foo.el 对应的字节码文件为 foo.elc 。相关详情参见《Emacs Lisp 参考手册》中的字节编译章节。
Emacs Lisp 代码也可编译为 native code原生代码 :这种机器码与 C 或 Fortran 编译器生成的代码并无本质区别,运行速度比字节码更快。原生编译后的 Emacs Lisp 代码存储在以 '.eln' 为后缀的文件中。相关详情参见《Emacs Lisp 参考手册》中的原生编译章节。
加载 Emacs Lisp 文件
键入 M-x load-file 可 load加载 Emacs Lisp 文件,该命令会通过迷你缓冲区读取文件名,并将文件内容作为 Emacs Lisp 代码执行。执行此命令无需提前打开文件,它会直接从磁盘读取文件,而非从 Emacs 已有的缓冲区中读取。
若 Emacs Lisp 文件被安装在 Emacs Lisp load path加载路径 (下文定义)中,可直接键入 M-x load-library 加载,无需使用 load-file 。 load-library 命令会提示输入 libray name库名 而非文件名,并在 Emacs Lisp 加载路径的所有目录中依次搜索,匹配与库名对应的文件。若库名为 'foo' ,该命令会依次查找 foo.elc 、 foo.el 和 foo 文件(若 Emacs 编译时启用了原生编译功能, load-library 会优先查找 foo.el 对应的 .eln 文件,并优先加载该文件而非 foo.elc )。该命令的默认行为是加载 第一个找到的文件 ,且优先级为: .eln 文件 > .elc 文件 > .el 文件,原因是编译后的文件加载和运行速度更快。
若检测到 lib.el 的修改时间晚于 lib.elc ,该命令会发出警告(防止开发者修改了 .el 源文件后忘记重新编译),但仍会加载 lib.elc 文件。借助这一特性,你可将未完成的编辑保存至 Emacs Lisp 源文件,待修改完成并准备投入使用后再进行编译。若将选项 load-prefer-newer 设置为非 nil 值,Emacs 会放弃上述查找规则,转而加载 版本最新 的文件。
若 Emacs 启用了原生编译功能,且未找到 lib.el 对应的 '.eln' 文件,会先加载 lib.elc ,并在后台启动 lib.el 的原生编译;待编译完成后,会自动加载生成的 .eln 文件。
Emacs Lisp 程序通常通过 load 函数加载 Emacs Lisp 文件,该函数与 load-library 功能类似,但属于更底层的实现,且支持传入额外参数。相关详情参见《Emacs Lisp 参考手册》中的程序的加载方式章节。
Emacs Lisp 加载路径
Emacs Lisp 加载路径由变量 load-path 指定,其值应为 目录字符串构成的列表 。 M-x load-library 命令、底层的 load 函数,以及其他用于查找 Emacs Lisp 库的 Emacs 函数,都会按照该列表指定的顺序搜索这些目录。
load-path 中的项也可使用特殊值 nil ,代表当前默认目录,但 几乎不建议使用该值 —— 因为其实际指向的目录会随 Emacs 中当前激活的缓冲区变化而改变。若你希望加载当前目录的文件,更合适的方式是使用 M-x load-file 。
load-path 的默认值为存储 Emacs 自身 Lisp 代码的目录列表。若你在其他目录中有自定义的代码库,可将该目录添加至加载路径。与本手册介绍的大多数变量不同, load-path 无法通过自定义界面修改(参见简易自定义界面章节),但可在初始化文件中添加如下语句实现目录添加(参见Emacs 初始化文件章节):
(add-to-list 'load-path "/path/to/my/lisp/library")
按照惯例,本地安装的代码库可放在 site-lisp 目录(该目录已包含在 load-path 的默认值中)或其任意子目录下,这样就无需修改 load-path 的默认值。
与 load-path 类似,Emacs 搜索原生编译 Lisp 代码( *.eln 文件)的目录列表,由变量 native-comp-eln-load-path 指定。
自动加载
部分命令为 autoloaded自动加载命令 :当你运行此类命令时,Emacs 会先自动加载其关联的代码库。例如 M-x compile 命令(参见在 Emacs 中运行编译章节)即为自动加载命令,调用该命令时,Emacs 会先自动加载编译相关的代码库。与之相反, M-x recompile 命令并非自动加载命令,因此在手动加载编译代码库前,该命令无法使用。
当你查阅自动加载命令的文档时(参见按命令或变量名获取帮助章节),若文档中引用了其代码库中的其他函数和变量,Emacs 也会触发自动加载(加载代码库后,Emacs 才能正确设置 *Help* 缓冲区中的超链接)。若要禁用该功能,可将变量 help-enable-autoload 设为 nil。
在为 describe-variable 和 describe-function 命令补全名称时,Emacs 也会根据补全的前缀触发自动加载。若要禁用该功能,可将变量 help-enable-completion-autoload 设为 nil 。
让代码库在启动时可用
将自定义代码库放在 Emacs 可查找并加载的目录后,你可能希望其在 Emacs 启动时即处于可用状态。当代码库定义的功能需要按需自动可用、手动加载又较为繁琐时,这一设置会非常实用。
可在初始化文件中添加合适的表达式,确保代码库被加载:若需要 启动时始终加载 ,可使用 load 或 require ;若需要调用特定命令 / 函数时才加载,则使用 autoload 。示例如下:
;; 无条件加载 my-shining-package.elc (require 'my-shining-package) ;; 调用 my-func 时才加载 my-shining-package.elc (autoload 'my-func "my-shining-package")
注意:使用 package-install 安装包时(参见包安装章节),Emacs 会自动将该包的 Lisp 文件放在可查找的目录中,并根据需要扩展 load-path ,因此对于通过该方式安装的包,无需进行上述手动自定义操作。
29.9. 求值 Emacs Lisp 表达式
Emacs Lisp mode 是编辑 Emacs Lisp 代码的主模式,其模式调用命令为 M-x emacs-lisp-mode 。
Emacs 提供了多款用于求值 Emacs Lisp 表达式的命令,你可在 Emacs Lisp 模式中使用这些命令,在编写代码的同时完成测试。例如,重写某个函数后,可对该函数定义进行求值,使其在后续的函数调用中生效。这些命令同样支持全局调用,可在 Emacs Lisp 模式外使用。
核心求值命令
M-:- 在迷你缓冲区中读取单个 Emacs Lisp 表达式并求值,最终在回显区打印求值结果 (
eval-expression) 。 C-x C-e- 求值得光标前方的 Emacs Lisp 表达式,在回显区打印求值结果 (
eval-last-sexp) 。 C-M-x(Emacs Lisp 模式下)M-x eval-defun- 求值得包含光标或光标后方的整个顶级函数定义,在回显区打印求值结果 (
eval-defun) 。 M-x eval-region- 求值得选中区域内的所有 Emacs Lisp 表达式。
M-x eval-buffer- 求值得当前缓冲区中的所有 Emacs Lisp 表达式。
命令细节说明
M-: (eval-expression) 会通过迷你缓冲区读取表达式并完成求值, 在求值前,当前缓冲区会切回按下 M-: 时的原缓冲区 ,而非输入表达式的迷你缓冲区。
命令 C-x C-e (eval-last-sexp) 对缓冲区中光标前方的表达式求值后,会在回显区展示结果。若求值结果为整数,会同时以其他格式展示该整数值(八进制、十六进制;若满足下文所述 eval-expression-print-maximum-character 的限制条件,还会展示对应的字符形式)。
若为 M-: 或 C-x C-e 添加 前缀参数 ,求值结果将插入到当前缓冲区的光标位置,而非在回显区展示;若前缀参数为 0,整数结果会连带其八进制、十六进制、字符形式一同插入。添加此类前缀参数后,求值结果的输出也将不再受变量 eval-expression-print-level 和 eval-expression-print-length 的缩写限制(下文将介绍这两个变量);同理,前缀参数设为 -1 时,将单独取消 eval-expression-print-length 的缩写限制。
C-x C-e (eval-last-sexp) 对 defvar 表达式做特殊处理:常规情况下,若 defvar 定义的变量已存在值,对该表达式求值将无任何效果;而该命令会 无条件将变量重置为 defvar 中指定的初始值 ,这一特性对调试 Emacs Lisp 程序十分便捷。 defcustom 和 defface 表达式也会被做同样的特殊处理。注意:本节介绍的其他命令( eval-defun 除外)均无此特殊处理机制。
eval-defun 命令在 Emacs Lisp 模式中绑定为 C-M-x ,用于求值包 含光标或光标后方的顶级 Lisp 表达式 并在回显区打印结果。此语境下的顶级表达式虽被称作「defun」,但并非必须是实际的函数定义。
该命令对 defvar/defcustom/defface 形式的处理方式,与 eval-last-sexp 完全一致。
若为 C-M-x 添加前缀参数,该命令会为函数定义添加 Edebug(Emacs Lisp 调试器)的插桩代码 ,开启调试支持。详情参见《Emacs Lisp 参考手册》中的「为 Edebug 插桩」章节。
命令 M-x eval-region 会将选中区域的文本解析为一个或多个 Lisp 表达式,并依次对其求值; M-x eval-buffer 功能与之类似,仅求值对象为整个缓冲区的内容。
相关配置选项
变量 eval-expression-print-level 和 eval-expression-print-length :用于控制求值命令的结果输出时,列表被缩写前的最大嵌套深度和最大长度。为 eval-expression 或 eval-last-sexp 添加前缀参数 0,列表将以完整形式打印,不做任何缩写。
变量 eval-expression-debug-on-error :控制使用上述求值命令时,若发生求值错误,是否调用调试器,其默认值为 t (开启调试)。
变量 eval-expression-print-maximum-character :用于限制整数的字符形式展示,数值超过该变量设定值的整数,将不会被转换为字符形式展示。
29.10. Lisp 交互缓冲区
Emacs 启动时会自动创建一个名为 *scratch* 的缓冲区,该缓冲区用于交互式求值 Emacs Lisp 表达式,其主模式为 Lisp Interaction mode。你也可以通过键入 M-x lisp-interaction-mode 手动启用该模式。
若你关闭了 *scratch* 缓冲区,可通过 M-x scratch-buffer 命令重新创建。
在 *scratch* 缓冲区及其他 Lisp 交互模式的缓冲区中,按下 C-j (eval-print-last-sexp) 会求值得光标前方的 Lisp 表达式,并将求值结果插入到光标位置。因此,当你在缓冲区中输入表达式后,为每个表达式按下一次 C-j ,缓冲区会自动记录下所有被求值的表达式及其结果,形成一份完整的操作记录。Lisp 交互模式下的所有其他命令,与 Emacs Lisp mode完全一致。
Emacs 启动时, *scratch* 缓冲区中会包含一段简短的提示信息,该信息以 Lisp 注释的形式呈现,用于说明此缓冲区的用途。这段提示信息由变量 initial-scratch-message 控制,该变量的值可以是一个文档字符串,也可以设为 nil (表示隐藏该提示信息)。
交互式求值 Emacs Lisp 表达式还有另一种方式:使用下级 Emacs Lisp 模式。该模式为 Emacs Lisp 表达式求值提供了一套类似 Shell 模式(参见《Shell 模式》章节)的操作界面。键入 M-x ielm 可创建一个使用该模式的 *ielm* 缓冲区,更多相关信息可查阅该命令的说明文档。
29.11. 运行外部 Lisp 解释器
Lisp mode是编辑通用 Lisp 方言程序(如通用 Lisp)的主模式,其模式命令为 M-x lisp-mode 。Emacs 会为后缀为 .l 、 .lsp 或 .lisp 的文件自动启用 Lisp 模式。
你可以将外部 Lisp 会话作为 Emacs 的子进程( inferior 下级进程 )运行,并向其传递表达式进行求值。要启动外部 Lisp 会话,键入 M-x run-lisp 即可。该命令会运行名为 lisp 的程序,并将其输入输出均定向至 Emacs 的 *inferior-lisp* 缓冲区。若要修改 M-x run-lisp 所运行的 Lisp 程序名称,可更改变量 inferior-lisp-program 的值。
*lisp* 缓冲区的主模式为 Inferior Lisp 模式,该模式融合了 Lisp 模式与 Shell 模式的特性(参见《Shell 模式》章节)。向 Lisp 会话发送输入时,只需移至 *lisp* 缓冲区末尾,输入内容后按下 RET 回车键即可;Lisp 会话的终端输出会自动插入到该缓冲区中。
在 Lisp 模式下编辑 Lisp 程序时,可键入 C-M-x (lisp-eval-defun) ,将 Lisp 模式缓冲区中的表达式发送至通过 M-x run-lisp 启动的 Lisp 会话。被发送的表达式为光标所在位置或光标后方的顶级 Lisp 表达式,求值结果会按常规方式输出至 *inferior-lisp* 缓冲区。需注意,Lisp 模式下 C-M-x 的作用与 Emacs Lisp 模式中极为相似(参见《求值 Emacs Lisp 表达式》章节),唯一区别是表达式会被发送至外部 Lisp 环境求值,而非在 Emacs 内部求值。
编辑 Scheme 代码并向 Scheme 子进程发送表达式的相关功能与之高度相似。Scheme 源文件在 Scheme 模式下编辑,该模式可通过 M-x scheme-mode 显式启用;键入 M-x run-scheme 可启动 Scheme 会话(与 Scheme 交互的缓冲区名为 *scheme* ),同样通过 C-M-x 向其发送表达式。
30. 大型程序维护
本章介绍 Emacs 中用于维护中大型程序及软件包的功能,这些功能包括:
- 版本控制系统(VCS)的统一操作界面,该系统用于记录源文件的修改历史
- 处理编程项目的专用命令
- 维护变更日志文件的专属模式,此类文件用于按时间顺序记录程序的修改内容
- 交叉引用(Xref)功能:一组用于显示符号(又称 “标识符”)定义及引用位置的命令
- Emacs 开发环境(EDE):Emacs 内置的集成开发环境
- 分支合并模式:用于合并在不同开发分支上对程序源码做出的修改
- 缺陷引用次要模式:可高亮显示缺陷引用信息,并在对应的问题追踪系统中打开相关缺陷报告
若你正在维护大型 Lisp 程序,除本章介绍的功能外,Emacs Lisp 回归测试库(ERT)也会为你提供帮助(参见《Emacs Lisp 回归测试》中的 ERT 相关章节)。
30.1. 版本控制
version control 版本控制 系统是一款可记录源文件多个版本的程序,能存储各版本的创建时间、修改人以及修改内容说明等信息。
Emacs 的版本控制界面名为 VC ,VC 命令可适配多款不同的版本控制系统;目前支持的系统包括 Bazaar、CVS、Git、Mercurial、Monotone、RCS、SRC、SCCS/CSSC 以及 Subversion。其中,CVS、RCS 和 Bazaar 由 GNU 项目发布维护。
当你打开受版本控制系统管理的文件时,VC 会自动启用。若要完全禁用 VC,可将可自定义变量 vc-handled-backends 的值设为 nil (参见《自定义 VC》章节)。
若要更新当前缓冲区中已打开文件的 VC 状态信息,可使用命令 vc-refresh-state 。当你在 Emacs 外部执行版本控制命令(如在 Shell 提示符下操作)、将缓冲区文件切换至其他版本控制系统管理,或把文件从版本控制中彻底移除时,该命令会非常实用。
在展示受版本控制系统管理文件所在目录的 Dired 缓冲区中(参见《目录编辑器 Dired》章节),VC 也会自动启用。本节介绍的所有 VC 命令,均可在任意展示 VC 受控文件目录的 Dired 缓冲区中调用;Dired 缓冲区中被标记的所有文件(参见《Dired 标记与标志》章节)会被视为属于 当前文件集 ,VC 命令将对该文件集中的文件执行操作。这一特性允许你自定义构建 VC 文件集,包含任意所需文件,不受其 VC 状态限制。(若在 Dired 缓冲区中调用 VC 命令时无文件被标记,则缓冲区当前行显示的文件会被视为文件集中的唯一文件。)
30.1.1. 版本控制简介
VC 允许你在 Emacs 中使用版本控制系统,将版本控制操作与编辑操作无缝融合,同时为多款版本控制系统的通用操作提供了统一的操作界面。
部分不常用或复杂的版本控制操作(例如修改代码库配置)并非 VC 支持的功能,你需要在 VC 外部执行此类操作,比如通过命令行完成。
本节将对版本控制进行整体概述,并介绍 VC 所支持的版本控制系统。若你已熟悉将要使用的版本控制系统,可跳过本节内容。
30.1.1.1. 理解版本控制解决的问题
版本控制系统为使用者提供三项核心能力:
- 可回滚性:当发现某次修改存在错误或考虑不周时,能够将文件恢复至先前的状态。
- 并发性:支持多人同时修改同一组文件,且能检测并解决修改过程中出现的冲突。
- 历史追溯性:可为文件数据附加历史相关信息,例如为每次修改添加说明其设计意图的注释。即便对于独立开发的程序员,修改历史也是辅助记忆的重要工具;而在多人协作的项目中,修改历史更是开发者之间至关重要的沟通形式。
30.1.1.2. VC 支持的版本控制系统
VC 目前兼容多款不同的版本控制系统,并称这些系统为后端,具体如下:
- Git 是一款分布式版本控制系统,最初由林纳斯・托瓦兹为支持 Linux 内核的开发而设计。VC 支持 Git 的多数常用操作,而代码库同步等部分操作仍需通过命令行执行。
- CVS 是一款免费的版本控制系统,2008 年前后之前,曾被绝大多数自由软件项目采用,后被新一代版本控制系统取代。CVS 支持多用户在本地或通过网络同时协作开发,但与新一代系统不同,该系统不支持原子提交,也无文件移动 / 重命名的相关功能。VC 支持 CVS 环境下的所有基础编辑操作。
- Subversion(简称 svn)是一款免费的版本控制系统,设计初衷为兼容 CVS 并解决其固有问题,例如支持文件集的原子提交,同时可对目录、符号链接、元数据进行版本管理,也支持文件的重命名、复制与删除操作。
- SCCS 是史上首款版本控制系统,早已被功能更完善的同类系统取代。VC 会自行实现 SCCS 缺失的部分功能以做补充,例如版本发布的标签命名功能;而多分支管理等其他 VC 功能,在 SCCS 环境下则完全无法使用。由于 SCCS 为非免费软件,我们建议避免使用。
- CSSC 是 SCCS 的免费替代方案,仅当因特殊原因无法使用设计更优的新一代版本控制系统时,才建议选用该系统。
- RCS 是一款免费的版本控制系统,也是 VC 最初的开发基础,该系统功能相对基础:不支持网络远程操作,仅能对单个文件进行版本管理。几乎所有可通过 RCS 完成的操作,都能在 VC 中实现。
- Mercurial(简称 hg)是一款分布式版本控制系统,功能与 Git 高度相似。VC 支持 Mercurial 的绝大多数命令,仅不支持代码库同步操作。
- Bazaar(简称 bzr)是一款分布式版本控制系统,同时支持基于代码库的版本管理与分布式版本管理两种模式。VC 支持 Bazaar 环境下的多数基础编辑操作。
- SRC(简称 src)是 RCS 的升级优化版本,是专为单人开发的单文件项目设计的专用版本控制系统。该系统允许单个目录中存在多个版本控制历史相互独立的文件,因此特别适用于小型文档、脚本与配置点文件的维护。SRC 基于 RCS 实现版本存储,同时提供了现代化的操作界面,支持无锁操作与整数连续版本号功能。VC 支持 SRC 的几乎所有操作。
30.1.1.3. 版本控制的核心概念
当一个文件被纳入版本控制管理时,我们称该文件已在版本控制系统中 registered注册 。版本控制系统会维护一个 repository代码库 ,其中既存储文件的当前状态,也保存其完整的修改历史 —— 这些信息足以重建文件的当前版本或任意历史版本。代码库中还会存储其他相关信息,例如描述各文件修改内容的日志条目。
你实际编辑的、受版本控制的文件副本被称作 work file工作文件 。工作文件的编辑方式与普通文件完全一致,当完成一系列修改后,你可以对这些修改执行 commit提交(check in签入) 操作;该操作会将修改记录至代码库中,同时附带一段描述性的日志条目。
由工作文件构成的目录树被称作 working tree工作树 。
每一次提交操作都会在代码库中生成一个新的 revison版本 。版本控制系统会追踪所有历史版本,以及每个版本中发生的具体修改。每个版本都会有一个 revison ID版本标识 ,其格式由对应的版本控制系统决定;最简单的形式就是一个整数。
若要深入理解版本控制的进阶用法,你需要掌握版本控制系统的三大核心差异维度。后续三节将详细说明:版本控制系统分为基于锁定和基于合并两类、基于文件和基于变更集两类,以及集中式和分布式两类。VC 支持所有这些运作模式,但无法完全屏蔽不同模式之间的本质差异。
30.1.1.4. 基于合并与基于锁定的版本控制
版本控制系统通常会提供相应机制,协调多位用户对同一文件的修改操作,实现方式主要分为两种: 合并式 与 锁定式 。
在采用合并机制的版本控制系统中,每位用户可随时修改工作文件。系统支持将你本地包含未提交修改的工作文件,与其他用户已提交的最新修改进行合并。
早期的版本控制系统则采用 locking 锁定 机制,该模式下的工作文件默认处于只读状态。若要编辑文件,需向版本控制系统发起锁定请求,解锁后文件才会变为可写状态;同一文件在同一时间仅能被一位用户锁定。此锁定机制与 Emacs 中用于检测普通文件被多人同时编辑的锁定功能原理类似,但实现方式不同(参见《防止同时编辑》章节)。当你提交修改后,文件会自动解锁并恢复为只读状态,其他用户此时方可锁定该文件进行修改。
当多位用户尝试同时修改同一文件时,锁定式和合并式系统均可能出现冲突问题。锁定式系统会产生 lock conflicsts锁定冲突 :用户尝试检出文件时,可能因文件已被锁定而操作失败。合并式系统会产生 merge conflicts合并冲突 :当你提交的文件修改,与其他用户在你检出文件后提交的修改存在冲突时,就会触发该问题。这两类冲突均需开发者通过人工判断与沟通来解决。实践证明,无论是从开发者的使用便捷性,还是从减少实际冲突的发生次数、降低冲突严重程度来看, 合并机制都优于锁定机制 。
SCCS 系统始终采用锁定机制;RCS 默认使用锁定机制,但可配置为合并模式运行;CVS 与 Subversion 默认采用合并机制,也可切换至锁定模式;Git、Mercurial 等分布式版本控制系统则 仅支持合并机制 。
VC 模式同时兼容锁定式和合并式版本控制系统。新一代版本控制系统中常用 commit提交 和 update更新 表述操作,而早期的锁定式系统则使用 check in签入 和 check out检出 。VC 会尽可能屏蔽这些表述上的差异,提供统一的操作体验。
30.1.1.5. 基于变更集与基于文件的版本控制
在 SCCS、RCS、CVS 等早期版本控制系统(以及 SRC 系统)中,版本控制操作均为 file-base 基于文件 的模式:每个文件都拥有独立的注释和版本修订历史,与其他所有文件互不关联。而以 Subversion 为开端的新一代版本控制系统,则采用 changeset-based 基于变更集 的模式:一次提交操作可包含对多个文件的修改,且整组修改会作为一个独立单元被系统处理。与此次修改相关的注释不再归属单个文件,而是附属于该变更集本身。
基于变更集的版本控制相比基于文件的版本控制,具备更高的灵活性和功能性;在实际开发中,当需要回滚对多个文件的批量修改时,基于变更集的模式能让开发者更便捷地定位并移除整组修改,这一优势尤为突出。
30.1.1.6. 分布式与集中式代码仓库
早期的版本控制系统均基于 centralized model集中式模型 设计,即一个项目仅配备一个代码库,供所有开发者共同使用。SCCS、RCS、CVS、Subversion 和 SRC 均采用该类模型,其弊端之一是代码库会成为影响系统可靠性与运行效率的 瓶颈节点 。
GNU Arch 率先提出了 distributed or decentralized分布式版本控制 的概念,后续 Git、Mercurial 和 Bazaar 均实现了该技术。分布式模式下,一个项目可拥有多个独立的代码库,这类系统支持在不同代码库间执行一种超级合并操作,能够对各代码库的修改历史进行调和统一。实际使用中,每位开发者均可拥有专属代码库,而代码库之间的合并操作则替代了传统的提交操作。
VC 可协助你管理个人工作文件与代码库之间的交互操作。无论对应的代码库是单一的主代码库,还是对等代码库网络中的其中一个,均无需 VC 做额外适配处理。
30.1.1.7. 日志文件类型
使用版本控制系统的项目可维护两类修改日志。一类是 版本控制系统日志 ,由版本控制系统自行维护:每次提交修改时,你需要为该次修改填写一条 /log entry日志条目. (参见《日志条目缓冲区的功能》章节)。
另一类是 ChangeLog 文件日志 (参见《变更日志》章节),该日志会按时间顺序记录程序中一大部分内容的所有修改 —— 通常为一个目录及其子目录下的内容。小型程序一般只需一个 ChangeLog 文件,大型程序则可在每个主要目录下各放置一个 ChangeLog 文件(详见《变更日志》章节)。早在版本控制系统出现之前,程序员就已开始使用这类变更日志。
基于变更集的版本控制系统,通常会为整个系统维护一份 基于变更集的修改日志 ,这在一定程度上让 ChangeLog 文件显得有些多余。但 ChangeLog 文件仍保留着两点优势:其一,有时需要将单个目录的修改历史与其他目录的修改历史分开查看,该文件可满足这一需求;其二,许多版本控制系统中,提交日志的内容无法修改,而 ChangeLog 文件则支持编辑。
采用版本控制的项目,可仅使用版本控制系统日志,也可同时使用两类日志,还能对不同文件分别采用不同的日志记录方式。每个项目都有对应的日志使用规范,你需遵循项目的既定规则。
若项目规范要求同时使用两类日志,通常的做法是为每次修改只编写一次日志条目,再将其同步至两类日志中。你可先在 ChangeLog 文件中编写条目,提交修改时按下 C-c C-a 将条目复制到版本控制的日志缓冲区(参见《日志条目缓冲区的功能》章节);也可在提交修改时,在日志缓冲区中编写条目(可借助 C-c C-w 快捷键辅助),后续再使用 C-x v a 命令将条目复制到 ChangeLog 文件中(参见《变更日志与版本控制》章节)。
30.1.2. 版本控制与模式行
当你打开受版本控制的文件时,Emacs 会在 模式行 中显示相关标识。例如,标识 'Bzr-1223' 表示该文件由 Bazaar 版本控制系统管理,且当前版本标识为 1223。
版本控制系统后端名称与版本标识之间的 字符 ,用于表示工作文件的版本控制状态。
在 基于合并 的版本控制系统中:
- 字符 '-' 表示工作文件未被修改;
- 字符 ':' 表示工作文件已被修改;
- 字符 '!' 表示文件因近期的合并操作产生了冲突(参见《合并分支》章节),或文件已从版本控制中移除,亦或是文件虽被版本化但同时被标记为 ignored忽略 (该情况通常为异常状态,参见《忽略版本控制文件》章节);
- 字符 '?' 表示文件受版本控制管理,但在工作树中缺失。
在 基于锁定 的版本控制系统中:
- 字符 '-' 表示文件未被锁定;
- 字符 ':' 表示文件已被锁定;
- 若文件被其他用户锁定(例如用户jim),模式行会显示为 'RCS:jim:1.3' ;
- 字符 '@' 表示文件已在本地添加,但尚未提交至主代码库。
在图形化显示界面中,将鼠标悬停在模式行的版本控制标识上,会弹出 工具提示框 ,展示版本控制状态的详细描述信息。在该标识上单击 mouse-1 鼠标左键,会弹出 VC 命令菜单,与菜单栏中「Tools / Version Control」选项的内容完全一致。
当 Auto Revert mode自动恢复模式(参见《恢复缓冲区》章节)恢复受版本控制的缓冲区时,会同步更新模式行中的版本控制信息。但如果在当前 Emacs 会话外,版本控制状态发生了变更且未对工作文件做任何修改,自动恢复模式可能无法正确更新该信息。若将变量 auto-revert-check-vc-info 设为 t ,自动恢复模式会每隔 auto-revert-interval 秒更新一次版本控制状态信息,即便工作文件本身无任何变化。该操作带来的 CPU 占用率因版本控制系统而异,通常不会过高。
30.1.3. 版本控制下的基本编辑
绝大多数 VC 命令均作用于 VC filesets(VC 文件集) 。VC 文件集是指一次 VC 操作所针对的一个或多个文件的集合。在打开受版本控制文件的缓冲区中执行 VC 命令时,VC 文件集即为该单个文件;在 VC 目录缓冲区中执行命令且其中部分文件被标记时,VC 文件集由所有被标记的文件组成(参见《VC 目录模式》章节)。同理,在 Dired 缓冲区中调用 VC 命令时,VC 文件集也由被标记的文件构成(参见《Dired 标记与标志》章节),若未标记任何文件,则默认以缓冲区当前行显示的文件作为唯一文件集。
对于 Git、Mercurial、Bazaar 等现代 基于变更集的版本控制系统 (参见《基于变更集与基于文件的版本控制》章节),VC 命令会将多文件 VC 文件集作为一个整体处理。例如,提交一个多文件 VC 文件集只会生成一个版本,该版本包含所有文件的修改内容。而在 CVS 等早期基于文件的版本控制系统中,多文件 VC 文件集中的每个文件会被单独处理;因此,提交一个文件集会为其中每个被修改的文件各生成一个版本。
C-x v v- 对当前 VC 文件集执行下一步相应的版本控制操作。
VC 的核心命令是多功能命令 C-x v v (vc-next-action) ,该命令会对当前 VC 文件集执行最贴合场景的操作:可为文件集在版本控制系统中完成注册,也可执行提交、解锁,或是将外部修改合并至文件集等操作。不同场景下的具体执行行为将在后续小节中详细说明。你可在文件访问缓冲区、Dired 缓冲区或 VC 目录缓冲区中使用 C-x v v 命令;在后两种缓冲区中,该命令将作用于由被标记文件组成的文件集。此外,在 Diff 模式下的补丁缓冲区中(参见《Diff 模式》章节)也可使用该命令,此时命令会作用于缓冲区中展示差异对比的所有文件。
请注意, VC 文件集 与用于按功能组查看和打开文件的 命名文件集 并非同一概念(参见《文件集》章节)。与命名文件集不同,VC 文件集无命名标识,且不会在不同 Emacs 会话间持久化保存。
30.1.3.1. 基于合并的基本版本控制
在 Git、Mercurial 等现代基于合并的版本控制系统中(参见《基于合并与基于锁定的版本控制》章节),若在打开受版本控制文件的缓冲区、VC 目录缓冲区或 Dired 缓冲区中调用 C-x v v 命令,该命令将按以下规则执行操作:
- 若 VC 文件集中包含多个文件,且各文件的版本控制状态不一致,触发错误提示。(但需注意,文件集允许同时包含新添加的文件和已修改的文件,参见《将文件注册到版本控制系统》章节。)若文件集中的文件已缺失(从文件系统中移除但仍被版本控制系统追踪),或被版本控制系统标记为忽略,同样触发错误提示。
- 若 VC 文件集中的所有文件均已完成注册,且相较于最新版本无任何修改,不执行任何操作。
- 若 VC 文件集中的所有文件均未在版本控制系统中注册,将为文件集中的新添加文件完成注册,即纳入版本控制系统管理(参见《将文件注册到版本控制系统》章节)。若 Emacs 无法找到可用的注册系统,会提示用户选择代码库类型、创建新的代码库,并将该 VC 文件集注册至新代码库。你也可显式指定使用的版本控制系统,参见《C-x v v 的高级用法》章节。注意:文件注册操作不会触发提交,需再次调用
C-x v v方可执行提交,详见下文。 - 若 VC 文件集中的所有文件均为新添加文件或已修改文件,提交其中的已修改文件。执行该操作时,Emacs 会弹出
*vc-log*缓冲区,用户需为本次修改编写日志条目,完成后按下C-c C-c即可提交(参见《日志条目缓冲区的功能》章节)。- 对于 Git、Mercurial 等现代分布式版本控制系统,修改会被提交至本地代码库,且不会自动同步至上游代码库(通常部署在远程主机)。若自你上次更新后,上游代码库已发生变更,此次提交可能失败。遇此情况,你需先从上游代码库更新内容后重试,可使用
C-x v +命令(参见《将修改拉取 / 推送至分支 / 从分支拉取》章节)或C-x v m命令(参见《合并分支》章节)完成更新。 - 对于集中式版本控制系统,若因上游代码库变更导致提交失败,再次调用
C-x v v即可合并上游代码库的修改内容。
- 对于 Git、Mercurial 等现代分布式版本控制系统,修改会被提交至本地代码库,且不会自动同步至上游代码库(通常部署在远程主机)。若自你上次更新后,上游代码库已发生变更,此次提交可能失败。遇此情况,你需先从上游代码库更新内容后重试,可使用
- 若使用集中式版本控制系统,且 VC 文件集中存在任意文件相较于上游代码库已过期,将提示用户从代码库更新该文件集。
上述规则同样适用于非锁定模式下的 RCS 系统,例外情况为:该模式下不会从代码库自动合并修改内容。若自你开始编辑文件后,其他用户已提交了对同一文件的修改,系统不会向你发出任何通知;当你提交自身的版本时,其他用户的修改会被覆盖(但这些修改仍保存在代码库中,并非永久性丢失)。因此,你必须在提交前确认当前版本未发生变更。此外,即便在非锁定模式下,RCS 仍支持锁定操作:对未修改文件执行 C-x v v 命令会锁定该文件,与 RCS 在常规锁定模式下的操作一致(参见《基于锁定的基础版本控制》章节)。
若在 Diff 模式的缓冲区中调用 C-x v v 命令,该命令会默认此缓冲区中保存了一个或多个文件的补丁集,随后将补丁中的修改应用至对应文件,并弹出 *vc-log* 缓冲区供用户编写合适的提交日志,完成后执行提交操作。
30.1.3.2. 基于锁定的基本版本控制
在 SCCS、默认模式下的 RCS 等基于 锁定的版本控制系统 中, C-x v v 命令将执行以下操作:
- 若 VC 文件集中包含多个文件且各文件的版本控制状态不一致,触发错误提示;若文件集中的文件已缺失(从文件系统中移除但仍被版本控制系统追踪),同样触发错误提示。
- 若 VC 文件集中的所有文件均未在版本控制系统中完成注册,将为文件集中的新添加文件完成注册(参见《将文件注册到版本控制系统》章节)。若 Emacs 无法找到可用的注册系统,会提示用户选择代码库类型、创建新的代码库并将该 VC 文件集注册至新代码库。你也可显式指定使用的版本控制系统,详见《C-x v v 的高级用法》章节。
- 若 VC 文件集中的所有文件均已注册且处于未锁定状态,将执行 check out检出 操作:为每个文件加锁并将其设为可写状态,以便你开始编辑。
- 若 VC 文件集中的所有文件均由你锁定且包含修改内容,将 commit提交(check-in签入) 本次修改。执行该操作时,Emacs 会弹出
*vc-log*缓冲区,你需为新版本编写日志条目,完成后按下C-c C-c即可提交(参见《日志条目缓冲区的功能》章节)。 - 若 VC 文件集中的所有文件均由你锁定,但你未对其做出任何修改,将 释放锁 并将文件恢复为只读状态。此操作可撤销此前对未做修改文件执行的检出操作。
- 若 VC 文件集中的所有文件均被其他用户锁定,会询问你是否要 抢占该锁 。若选择是,文件将转为由你锁定,且系统会向原锁定用户发送警告信息。
- 若 VC 文件集中的文件处于未锁定状态,但相较于最新版本存在修改内容,会提示你为每个该类文件 获取锁 ,或将文件 恢复至最近一次签入的版本 (该情况属于异常情况,正常使用中不应出现)。
上述规则同样适用于 锁定模式下的 CVS 系统 ,唯一例外为:CVS 不支持抢占锁的操作。
30.1.3.3. C-x v v 中的高级控制
为 vc-next-action 命令 (C-u C-x v v) 添加前缀参数后,该命令仍会执行逻辑上的下一个版本控制操作,同时会接收额外参数,用于精准指定操作的执行方式。
你可通过额外参数指定版本控制系统的名称。当某个文件集可被多个版本控制系统管理,而 Emacs 未能检测出正确的系统时,该用法会非常实用。
除此之外,若使用的是 CVS、RCS 或 SRC 系统,你还可指定版本标识,具体行为分以下两种情况:
- 若文件集已被修改(或处于锁定状态),指定版本标识后,Emacs 会以该标识提交文件。你可通过传入合适的版本标识来创建新的分支(参见《版本控制分支》章节)。
- 若文件集未被修改(且处于未锁定状态),指定版本标识后,会将该版本检出至工作树中。你也可通过传入其他分支的版本标识或分支标识,检出该分支上的对应版本(参见《分支间的切换》章节)。输入空参数(即执行
C-u C-x v v后按RET回车键),则会检出当前分支的最新(又称 head主干)版本。
注意:分布式版本控制系统会直接忽略以这种方式指定的 版本标识 。这类系统既不允许用户自定义版本标识,也不采用「检出单个文件」的设计逻辑。
30.1.4. 日志条目缓冲区的功能
当你通过 VC 执行提交修改操作时,Emacs 会弹出一个名为 *vc-log* 的缓冲区。你需要在该缓冲区中编写日志条目,描述本次所做的修改内容(参见《版本控制的解决场景》章节)。编写完成后,键入 C-c C-c (log-edit-done) 即可退出该缓冲区,同时将修改与编写的日志条目一并提交。
*vc-log* 缓冲区的主模式为 Log Edite mode日志编辑模式 ,该模式是文本模式的一个变体(参见《文本模式》章节)。进入日志编辑模式时,Emacs 会运行 text-mode-hook 和 vc-log-mode-hook 两个钩子函数(参见《钩子函数》章节)。
在 *vc-log* 缓冲区中,你可以编写一行或多行 header lines(头信息行) ,用于向版本控制系统提供额外的说明信息。每一行头信息都必须单独位于缓冲区的顶部;缓冲区中第一行非头信息的内容,将被视为日志条目的起始位置。例如,以下头信息行表明本次修改并非由你编写,而是由另一位开发者完成:
Author: J. R. Hacker <[email protected]>
除 'Author' 头信息外,Emacs 还支持识别 'Summary' (对本次变更集的单行摘要)、 'Date' (手动指定的提交时间)和 'Fixes' (本次修改所修复的缺陷引用)这几种头信息。并非所有版本控制系统都支持上述全部头信息,若你为某一版本控制系统指定了其不支持的头信息,该头信息会被当作日志条目的一部分处理。
在 *vc-log* 缓冲区中,当前的 VC 文件集即为执行 C-c C-c 后将被提交的文件集。键入 C-c C-f (log-edit-show-files) ,可查看该 VC 文件集中的所有文件列表;键入 C-c C-d (log-edit-show-diff) ,可查看 VC 文件集与你开始编辑时的版本之间的修改差异对比(参见《查看并比较旧版本》章节)。
为辅助生成 ChangeLog 条目,你可键入 C-c C-w (log-edit-generate-changelog-from-diff) ,基于 VC 文件集的修改差异,自动生成 ChangeLog 条目框架,其中会列出所有被修改的文件名和函数名。连续的空条目可通过 M-q (fill-paragraph) 合并。默认情况下,该框架仅包含文件名,不带任何上级目录;若你希望在文件名前添加直至 VC 根目录的上级目录,可自定义变量 diff-add-log-use-relative-names 实现。
若当前 VC 文件集中包含一个或多个 ChangeLog 文件(参见《变更日志》章节),键入 C-c C-a (log-edit-insert-changelog) ,可将相关的 ChangeLog 条目提取并插入到 *vc-log* 缓冲区中。如果各 ChangeLog 文件中最上方的条目是你以当前用户名在当日创建的,该命令会在该条目中检索与待提交文件匹配的内容,并将其插入。若你使用的是 CVS 或 RCS 系统,可参考《变更日志与版本控制》章节,了解反向操作 —— 从日志编辑缓冲区生成 ChangeLog 条目。
若要中止本次提交,只需不在 *vc-log* 缓冲区中键入 C-c C-c 即可。你可以切换至其他缓冲区进行其他编辑操作,只要不尝试执行新的提交操作,正在编辑的日志条目就会保留在 *vc-log* 缓冲区中,你可随时返回该缓冲区完成提交。
你也可以浏览历史日志条目,复用已有提交注释,这在需要多次提交且注释内容相近时非常实用。用于浏览历史的 M-n 、 M-p 、 M-s 和 M-r 命令,其使用方式与迷你缓冲区的历史记录命令完全一致(参见《迷你缓冲区历史记录》章节),区别仅在于这些命令可在迷你缓冲区外使用。
30.1.5. 为文件注册版本控制
C-x v i- 将当前打开的文件注册至版本控制系统。
命令 C-x v i (vc-register) 会将当前 VC 文件集中的所有文件完成 register 注册 ,纳入版本控制系统管理。该操作本质上与对未注册的 VC 文件集执行 C-x v v 的效果一致(参见《版本控制下的基础编辑》章节),唯一区别是:若 VC 文件集已完成注册,执行 C-x v i 会触发错误提示,而 C-x v v 会执行其他对应的版本控制操作。
注册文件时,Emacs 需要先选定对应的版本控制系统。针对多文件 VC 文件集,由 VC 目录缓冲区指定所使用的版本控制系统(参见《VC 目录模式》章节);针对单文件 VC 文件集,若该文件所在目录中已有文件完成版本控制注册,或该目录属于某一版本控制系统管理的目录树,Emacs 会选用该系统。若存在多个适用的版本控制系统,Emacs 将采用变量 vc-handled-backends 中排在首位的系统(参见《自定义 VC》章节)。若 Emacs 未找到可用于注册文件的版本控制系统,会提示用户选择代码库类型,创建新的代码库并将文件注册至该代码库中。
对于大多数版本控制系统,通过 C-x v i 或 C-x v v 注册文件,仅会将文件添加至工作树,不会执行提交操作 —— 即不会将文件纳入代码库管理。此类文件在 VC 目录缓冲区中会被标记为已添加,打开该文件的缓冲区模式行中,版本标识会显示为 '@@' 。若要让注册操作在代码库中生效,你必须提交这些新添加的文件(参见《版本控制下的基础编辑》章节)。请注意,单次提交操作可同时包含新添加的文件,以及对版本控制系统中已存在文件的编辑修改。
在基于锁定的版本控制系统中(参见《基于合并与基于锁定的版本控制》章节),文件完成注册后会处于未锁定、只读的状态。需键入 C-x v v 执行检出操作,方可开始编辑文件。
30.1.6. 查看与比较旧版本
C-x v =- 将当前版本控制文件集中的工作文件与编辑起始版本进行对比 (
vc-diff) 。带前缀参数执行时,会提示输入当前文件集的两个版本并对其进行对比,该命令也可在 Dired 缓冲区中调用(参见《目录编辑器 Dired》章节)。 M-x vc-ediff- 功能与
C-x v =类似,差异为使用 Ediff 工具进行对比,详见《Ediff 使用手册》中的 Ediff 相关章节。 C-x v D- 将整个工作树与编辑起始版本进行对比 (
vc-root-diff) 。带前缀参数执行时,会提示输入两个版本并对比其对应的工作树。 C-x v ~- 提示输入当前文件的指定版本,并在独立缓冲区中打开该版本 (
vc-revision-other-window) 。 C-x v g- 显示当前文件的带注释版本:为每一行标注其最后一次被修改的版本号 (
vc-annotate) 。
C-x v = (vc-diff) 会生成差异对比结果,将当前版本控制文件集中每个工作文件与编辑起始版本进行比对,对比结果会在另一个窗口的 *vc-diff* 缓冲区中显示,该缓冲区采用 Diff 模式(参见《Diff 模式》章节),可使用 Diff 模式的所有常规命令。其中 g (revert-buffer) 命令会重新执行文件对比并生成新的差异结果。
若要对比当前版本控制文件集的任意两个版本,可带前缀参数调用 vc-diff : (C-u C-x v =) ,该操作会提示输入两个版本标识(参见《版本控制的核心概念》章节),并展示这两个版本间的差异。注意:若所使用的版本控制系统为基于文件的类型(如 CVS)而非基于变更集,该操作对多文件版本控制文件集的对比结果可能不可靠,因为不同文件的版本标识之间无实际关联意义。
部分版本控制系统支持以非版本标识的格式指定版本,例如在 Bazaar 系统中,执行 C-u C-x v = (及相关命令)时,可输入 'date:yesterday' 作为参数,指定为昨日之后提交的第一个版本。具体格式可参考对应版本控制系统的官方文档。
在 Dired 缓冲区中(参见《目录编辑器 Dired》章节)调用 C-x v = 或 C-u C-x v = 时,缓冲区当前行显示的文件会被视为当前版本控制文件集,文件集也可包含多个被标记的文件。
M-x vc-ediff 的功能与 C-x v = 基本一致,区别在于该命令会启动 Ediff 会话进行对比,详见《Ediff 使用手册》中的 Ediff 相关章节。
C-x v D (vc-root-diff) 与 C-x v = 功能类似,差异为该命令会展示当前整个工作树(即包含当前版本控制文件集的工作树)中的所有修改。在 Dired 缓冲区中调用该命令时,对比范围为包含该目录的整个工作树。
若要对比整个工作树的任意两个版本,可带前缀参数调用 vc-root-diff : C-u C-x v D ,该操作会提示输入两个版本标识(参见《版本控制的核心概念》章节),并展示这两个版本对应的整个版本控制目录树之间的差异(RCS、SCCS、CVS 和 SRC 系统不支持此功能)。
你可自定义 C-x v = 和 C-x v D 生成差异对比时使用的 Diff 参数,所用参数会按顺序从 vc-backend-diff-switches 、 vc-diff-switches 和 diff-switches 这三个变量中,选取第一个非空值(参见《文件对比》章节)。其中 backend 代表对应的版本控制系统,例如 Bazaar 系统对应 bzr 。由于空值表示继续检查下一个变量,前两个变量均可将值设为 t ,表示不使用任何参数。大多数 vc-backend-diff-switches 变量的默认值为空,部分变量默认值为 t ,这类变量适用于 Subversion 等自身 Diff 实现不支持通用 Diff 参数的版本控制系统。
若要直接查看文件的旧版本,可先打开该文件的工作版本,再键入 C-x v ~ 版本号 RET (vc-revision-other-window) 。该操作会提取对应版本的文件内容,保存为文件名 .~revision~ 的文件,并在独立窗口中打开。
多数版本控制系统支持通过键入 C-x v g (vc-annotate) ,查看带逐行版本信息注释的文件。该命令会创建一个新的 “注释缓冲区”,展示文件内容的同时,为每一行按修改时间上色:红色代表最新修改,蓝色代表最早修改,中间色调对应中间修改时间。默认情况下,颜色会覆盖整个时间跨度,最早的修改为蓝色,最新的修改为红色。若将变量 vc-annotate-background-mode 设为非空值,行的修改时间会通过背景色体现,前景色保持默认。
你可通过自定义 vc-backend-annotate-switches 和 vc-annotate-switches 变量,设置 C-x v g 的注释展示参数,这两个变量的作用方式与前文所述的 vc-backend-diff-switches 和 vc-diff-switches 类似。
带前缀参数执行 C-x v g 时,Emacs 会通过迷你缓冲区读取两个参数:一是要展示并添加注释的目标版本(替代当前文件内容),二是颜色跨度对应的天数。
在 “注释缓冲区” 中,可通过「VC-Annotate」菜单设置上述及其他颜色缩放相关选项,同时可使用以下快捷键浏览历史版本注释、查看差异或查看日志条目:
p- 为上一个版本添加注释(即当前注释版本的前一个版本)。数字前缀参数表示重复次数,例如
C-u 10 p会跳回前 10 个版本并添加注释。 n- 为下一个版本添加注释(即当前注释版本的后一个版本)。数字前缀参数表示重复次数。
j- 为当前行标注的版本添加注释。
a- 为当前行标注版本的前一个版本添加注释,便于查看当前行修改前的文件状态。
f- 在缓冲区中打开当前行标注的文件版本。
d- 展示当前行标注版本与其前一个版本之间的差异,便于查看该版本对当前行的具体修改内容。
D- 展示当前行标注版本与其前一个版本在变更集中所有文件的差异(适用于支持变更集的版本控制系统),便于查看该版本对整个工作树的具体修改内容。
l- 展示当前行标注版本的日志,便于查看开发者对该版本修改内容的描述。
w- 为工作版本添加注释(即正在编辑的版本)。若通过p和n浏览了其他版本,可通过该快捷键返回工作版本。
v- 切换注释的显示 / 隐藏状态,便于专注查看文件内容,不受注释干扰。
30.1.7. 版本控制变更日志
C-x v l- 显示当前文件集的修改历史 (
vc-print-log) 。 C-x v L- 显示当前代码库的修改历史 (
vc-print-root-log) 。 C-u 1 C-x v L 版本标识 RET- 显示单个版本的日志条目及修改内容(差异对比) (
vc-print-root-log) 。 C-x v b l- 显示其他分支的修改历史 (
vc-print-branch-log) 。 C-x v I- 显示 “pull拉取” 操作将获取的修改内容 (
vc-log-incoming) 。 C-x v O- 显示下一次 “push推送” 操作将发送的修改内容 (
vc-log-outgoing) 。 C-x v h- 显示当前缓冲区所打开文件中,选定区域的修改历史 (
vc-region-history) 。 M-x vc-log-search RET- 在修改历史中搜索指定匹配模式。
C-x v l (vc-print-log) 会打开名为 *vc-change-log* 的缓冲区,以详细格式展示当前文件集的修改历史,包括修改人、修改日期,以及每次修改对应的日志条目(即你在 *vc-log* 缓冲区中填写的日志条目,参见《日志条目缓冲区的功能》章节)。若在文件访问缓冲区中调用该命令,当前文件集即为该单个文件,且打开的 *vc-change-log* 缓冲区中,光标会定位在该文件对应版本的位置。若在版本控制目录缓冲区(参见《版本控制目录模式》章节)或目录编辑器缓冲区(参见《目录编辑器 Dired》章节)中调用,文件集包含所有被标记的文件;若未标记任何文件,则默认以目录缓冲区当前行显示的文件为文件集。
若文件集中包含一个或多个目录,若版本控制后端支持,生成的 *vc-change-log* 缓冲区会以简易格式展示修改日志(每条修改占一行);若不支持,则以详细格式展示。
带前缀参数调用该命令时,会提示输入 *vc-change-log* 缓冲区中光标要定位的版本,以及需要展示的最大版本数量。
C-x v L (vc-print-root-log) 会打开 *vc-change-log* 缓冲区,展示整个版本控制目录树的修改历史(RCS、SCCS、CVS 和 SRC 系统不支持此功能)。带前缀参数调用时,会提示输入需要展示的最大版本数量;数字型前缀参数可直接指定该最大值,无需再次提示。
若数字型前缀参数设为 1 (如执行 C-1 C-x v L 或 C-u 1 C-x v L ),该命令会提示输入版本标识,并展示该版本的日志条目,以及其引入的修改内容(差异对比)。(部分功能相对简单的版本控制系统,如 RCS 和 CVS,无命令可同时展示版本日志与差异对比;对于这类系统,该命令仅展示日志条目,你可通过键入 d 或 D 查看差异对比,详见下文。)
C-x v L 展示的历史记录为精简格式,通常仅显示每条日志条目的第一行。你可在 *vc-change-log* 缓冲区中键入 RET 回车键 (log-view-toggle-entry-display) ,展开光标所在版本的完整日志条目;再次按下 RET 回车键则会重新隐藏。
C-x v b l branch-name RET (vc-print-branch-log) 会打开 *vc-change-log* 缓冲区展示版本控制目录树的修改历史,功能与 vc-print-root-log 一致,区别在于该命令展示的是当前分支之外的其他分支历史,且会先提示输入要查看的分支名称。
在分布式版本控制系统中, C-x v I (vc-log-incoming) 命令会打开日志缓冲区,展示下一次执行版本控制系统的拉取命令、从其他远程位置获取新版本时,将应用的修改内容(参见《将修改拉取 / 推送至分支 / 从分支拉取》章节)。该远程位置为版本控制系统定义的、默认拉取修改的源地址;带前缀参数调用 vc-log-incoming 时,会提示输入具体的远程位置。同理, C-x v O (vc-log-outgoing) 会展示下一次执行推送命令时,将发送至其他远程位置的修改内容;带前缀参数调用时,会提示输入具体的目标地址,部分版本控制系统中该地址也可设为分支名。
在 *vc-change-log* 缓冲区中,你可使用以下快捷键在不同版本和文件的日志间导航,以及查看、对比历史版本(参见《查看并比较旧版本》章节):
p- 跳至前一条版本条目(日志缓冲区中的版本条目通常按时间倒序排列,因此前一条条目一般对应更新的版本),数字型前缀参数表示重复跳转次数。
n- 跳至下一条版本条目,数字型前缀参数表示重复跳转次数。
a- 为当前行对应的版本生成注释标注(参见《查看并比较旧版本》章节)。
e- 修改光标所在位置的修改注释,注意并非所有版本控制系统都支持修改注释功能。
f- 打开当前行所指向的版本文件。
d- 展示光标所在版本与上一个更早版本之间,对应单个文件的差异对比。
D- 展示光标所在版本与上一个更早版本之间的变更集差异对比,即该版本中所有文件的修改内容。
RET- 在精简格式的日志缓冲区中(如
C-x v L创建的缓冲区),切换光标所在版本完整日志条目的显示 / 隐藏状态。
由于获取大量日志条目可能耗时, *vc-change-log* 缓冲区默认最多展示 2000 个版本。变量 vc-log-show-limit 用于指定该上限值,若设为 0 则表示取消限制。你也可点击缓冲区末尾的「显示 2 倍条目」或「显示无限制条目」按钮,增加已有 *vc-change-log* 缓冲区中展示的版本数量(RCS、SCCS、CVS 和 SRC 系统不支持此功能)。
vc-region-history 命令(默认绑定至 C-x v h )提供了查看修改历史的实用变体功能,该命令会打开 *VC-history* 缓冲区,展示当前缓冲区文件中,光标与标记之间选定区域的修改历史(参见《标记与区域》章节)。该修改历史包含提交日志信息,同时也会以差异对比格式展示修改内容本身。
先在当前缓冲区中标记出你想要查看修改历史的区域,再调用该命令即可。其弹出的 *VC-history* 缓冲区中,可使用上述 *vc-change-log* 缓冲区的所有命令,同时也支持差异对比模式的各类命令(参见《差异对比模式》章节)。
该命令目前仅适用于 Git 和 Mercurial(hg)系统。
vc-log-search 命令可在修改日志中搜索指定匹配模式,该命令会提示输入匹配模式(正则表达式),并展示所有日志信息匹配该模式的修改历史条目。带前缀参数调用时,还会提示输入用于执行该搜索的、版本控制系统的具体 Shell 命令。
30.1.8. 撤销版本控制操作
C-x v u- 将当前版本控制文件集中的工作文件恢复至最新版本 (
vc-revert) 。
若你想要舍弃对当前版本控制文件集所做的所有修改,可键入 C-x v u (vc-revert) 。该命令会在舍弃修改前向你确认,确认后文件集将被恢复至原有状态。
若变量 vc-revert-show-diff 设为非空值,该命令会先展示工作文件与编辑起始版本之间的差异对比。对比完成后,差异对比缓冲区会被直接关闭(若该变量值设为 kill ),或被隐藏至后台(若设为其他任意非空值)。若你不希望 C-x v u 展示差异对比,可将该变量设为空值(你仍可通过 C-x v = 直接查看差异,参见《查看并比较旧版本》章节)。
在基于锁定的版本控制系统中,执行 C-x v u 后文件会保持未锁定状态;若要继续编辑,你需要重新为文件加锁。若你已锁定文件但后续决定不做任何修改,也可通过 C-x v u 为文件解锁。
30.1.9. 忽略版本控制文件
C-x v G- 将文件设为当前版本控制系统的忽略文件 (
vc-ignore) 。
许多源码目录中会包含无需纳入版本管理的文件,例如编辑器备份文件、目标文件、字节码文件以及编译生成的程序文件等。你可以选择不将这些文件添加到版本控制中,但这样它们会始终以未知文件的形式显示。你也可以将这些文件添加至目录树根目录的忽略文件中,告知版本控制系统忽略它们, C-x v G (vc-ignore) 命令可协助你完成这一操作。带前缀参数调用该命令时,可将文件从忽略文件列表中移除。
30.1.10. 版本控制目录模式
VC Directory buffer版本控制目录缓冲区 是一款专用缓冲区,用于查看目录树中各文件的版本控制状态,并对这些文件执行版本控制操作。该缓冲区的核心用途之一,是为 C-x v v 等命令指定待操作的多文件版本控制文件集(参见《版本控制目录缓冲区命令》章节)。
要使用版本控制目录缓冲区,键入 C-x v d (vc-dir) 即可。该命令会通过迷你缓冲区读取目录名称,并切换至对应目录的版本控制目录缓冲区。缓冲区默认命名为 *vc-dir* ,其具体内容说明参见《版本控制目录缓冲区》章节。
vc-dir 命令会自动检测指定目录中使用的版本控制系统。若该目录中同时使用了多个版本控制系统,需带前缀参数调用此命令( C-u C-x v d ),命令会提示你指定版本控制目录缓冲区要使用的版本控制系统。
除版本控制目录缓冲区外,Emacs 还提供了一款功能类似的工具 ——PCL-CVS,该工具是专为 CVS 系统设计的专用工具,详见《PCL-CVS 简介:CVS 的 Emacs 前端》章节。
你也可在 Dired 缓冲区中调用版本控制相关命令(参见《目录编辑器 Dired》章节)。在此场景下,你调用的所有版本控制命令都会将被标记的文件视作当前文件集(参见《版本控制下的基础编辑》章节);若未标记任何文件,则默认以当前行的文件作为操作对象。
30.1.10.1. 版本控制目录缓冲区
版本控制目录缓冲区中会列出受版本控制的文件及其版本控制状态,仅展示当前目录(即调用 C-x v d 时指定的目录)及其子目录中 状态特殊 的文件,与代码库内容保持一致的最新文件会被省略;若某个子目录下的所有文件均为最新状态,该子目录也不会被列出。例外情况为:若某文件因执行 VC 命令直接变为最新状态,该文件仍会被列出。
以下是 VC 目录缓冲区的列表示例:
./
edited configure.ac
* added README
unregistered temp.txt
src/
* edited src/main.c
有两个工作文件已修改但未提交:当前目录下的 configure.ac 和 src/ 子目录下的 main.c ; README 文件已添加至版本控制但尚未提交;而 temp.txt 文件未被纳入版本控制(参见《将文件注册到版本控制系统》章节)。
README 和 src/main.c 条目旁的 '*' 符号,表示用户已将这些文件标记为 当前 VC 文件集 (参见《版本控制目录缓冲区命令》章节)。
上述示例是 Bazaar、Git、Mercurial 等分布式版本控制系统的典型显示形式,其他系统会展示不同的状态标识。例如,若代码库中的修改尚未同步至工作文件,CVS 会为该文件标注 'needs-update' (需更新)状态;RCS 和 SCCS 则会将锁定文件的用户名作为该文件的状态展示。
在 CVS 系统中, vc-dir 命令通常会连接代码库(该代码库可能位于远程服务器)检查更新。若将变量 vc-cvs-stay-local 设为 nil (参见《CVS 专属配置项》章节),Emacs 在生成版本控制目录缓冲区时会避免连接远程代码库(仅在必要时仍会建立连接,例如执行提交操作时)。该设置适用于离线工作或网络速度较慢的场景。
版本控制目录缓冲区会忽略变量 vc-directory-exclusion-list 中列出的子目录,该变量的默认值包含版本控制系统内部使用的各类目录。
30.1.10.2. 版本控制目录命令
Emacs 提供了多个命令,用于在版本控制目录缓冲区中导航,以及将文件标记为当前版本控制文件集的成员。
nSPC- 将光标移至下一个条目 (
vc-dir-next-line) 。 p- 将光标移至上一个条目 (
vc-dir-previous-line) 。 TAB- 移至下一个目录条目 (
vc-dir-next-directory) 。 S-TAB- 移至上一个目录条目 (
vc-dir-previous-directory) 。 RETf- 打开当前行列出的文件或目录 (
vc-dir-find-file) 。 o- 在独立窗口中打开当前行的文件或目录 (
vc-dir-find-file-other-window) 。 m标记当前行的文件或目录 (
vc-dir-mark) ,将其加入当前版本控制文件集。若区域处于激活状态,则标记区域内的所有文件。若文件已位于被标记的目录或其子目录中,无法通过此命令标记该文件;同理,若目录树中存在已标记的文件,也无法通过此命令标记该目录。
M- 若光标位于文件条目上,标记所有同状态的文件;若光标位于目录条目上,标记该目录树中的所有文件 (
vc-dir-mark-all-files) 。带前缀参数时,标记所有列出的文件和目录。 % m* %- 通过正则表达式标记文件 (
vc-dir-mark-by-regexp) 。带前缀参数时,执行反向取消标记操作。 * r- 标记所有处于已注册状态的文件,包括已编辑、已添加或已移除的文件 (
vc-dir-mark-registered-files) 。 G- 将光标所在文件加入版本控制的忽略文件列表 (
vc-dir-ignore) 。例如,若使用 Git 作为版本控制系统,该命令会将此文件追加至.gitignore文件中。带前缀参数时,对所有已标记文件执行此操作。 q- 退出版本控制目录缓冲区并将其隐藏至后台 (
quit-window) 。 u- 取消标记当前行的文件或目录。若区域处于激活状态,则取消标记区域内的所有文件 (
vc-dir-unmark) 。 U- 若光标位于文件条目上,取消标记所有同状态的文件;若光标位于目录条目上,取消标记该目录树中的所有文件 (
vc-dir-unmark-all-files) 。带前缀参数时,取消标记所有文件和目录。 x- 隐藏状态为「up-to-date最新」或「ignored已忽略」的文件 (
vc-dir-hide-up-to-date) 。带前缀参数时,隐藏所有与光标所在条目状态相同的项。
在版本控制目录缓冲区中,通过 m (vc-dir-mark) 或 M (vc-dir-mark-all-files) 标记的所有文件,均会归入当前版本控制文件集。若使用 m 标记目录条目,则该目录树中所有列出的文件都会加入当前版本控制文件集。属于当前版本控制文件集的文件和目录,会在版本控制目录缓冲区中其状态旁标注 '*' 符号。通过这种方式,你可以创建多文件版本控制文件集,供 C-x v v (参见《版本控制下的基础编辑》)、 C-x v = (参见《查看并比较旧版本》)、 C-x v u (参见《撤销版本控制操作》)等版本控制命令操作。
版本控制目录缓冲区还为带 C-x v 前缀的版本控制命令定义了部分单键快捷方式:=、+、l、i、D、L、G、I、O 和 v。
例如,提交一组已编辑文件的操作流程为:打开版本控制目录缓冲区(其中已编辑文件会标注「edited」状态)→ 标记目标文件 → 键入 v 或 C-x v v (vc-next-action) 。若使用的是基于变更集的版本控制系统,Emacs 会将这些文件一次性提交为一个新版本。
在版本控制目录缓冲区中,还可对当前版本控制文件集执行查找和替换操作,相关命令如下:
S- 在文件集中执行查找 (
vc-dir-search) 。 Q- 在文件集中执行正则表达式查询替换 (
vc-dir-query-replace-regexp) 。 M-s a C-s- 在文件集中执行增量查找 (
vc-dir-isearch) 。 M-s a C-M-s- 在文件集中执行增量正则表达式查找 (
vc-dir-isearch-regexp) 。
除可作用于多个文件外,这些命令的行为与单缓冲区对应的查找替换命令基本一致(参见《查找与替换》章节)。
版本控制目录缓冲区还定义了一组以 b 为前缀的分支相关命令:
b c- 创建新分支 (
vc-create-branch) ,参见《创建新分支》章节。 b l- 提示输入分支名称,并展示该分支的修改历史 (
vc-print-branch-log) 。 b s- 切换至指定分支 (
vc-switch-branch) ,参见《分支间的切换》章节。 d- 删除已标记的文件;若无标记文件,则删除当前行文件 (
vc-dir-clean-delete) 。该命令不会在版本控制系统中将文件标记为已删除,因此主要适用于未注册的文件。
上述所有命令均可通过菜单栏调用,也可通过鼠标中键( mouse-2 )调出的上下文菜单调用。此外,部分版本控制后端会通过菜单提供专属的扩展命令,例如 Git 和 Bazaar 支持操作暂存区与搁置区(二者均用于临时保存未提交的修改,并可在后续恢复这些修改)。
30.1.11. 版本控制分支
版本控制的一项用途是支持多条独立的开发线,这些开发线被称为 branche分支 。分支可用于多种场景,例如维护程序相互独立的稳定版本与开发版本,以及在隔离环境中开发互不相关的功能模块。
目前版本控制(VC)对分支操作的支持尚较为有限。针对分布式版本控制系统,VC 提供了相关命令,可将一个分支的内容更新至另一分支,也可合并对两个不同分支所做的修改(参见《合并分支》章节)。针对集中式版本控制系统,VC 支持检出不同分支,并将修改提交至新分支或其他已有分支。
30.1.11.1. 分支切换
不同版本控制系统对分支的实现方式存在差异,而这些差异无法通过版本控制(VC)完全屏蔽。
在部分分布式版本控制系统中 —— 包括 Bazaar、以及默认模式下的 Mercurial,每个分支都拥有独立的工作目录树,因此分支间的切换仅需切换目录即可。在 Git 中,分支通常共存于同一目录下,通过 git checkout 命令完成分支切换,该命令会修改工作树的内容,使其与目标分支保持一致。Bazaar 也支持分支共存模式,此种情况下可通过 bzr switch 命令在当前目录中切换分支。在 Subversion 中,使用 svn switch 命令切换至其他分支;在 Mercurial 中,切换分支则通过 hg update 命令实现。
在当前目录中切换至其他分支的 VC 命令为: C-x v b s branch-name RET (vc-switch-branch) 。
在集中式版本控制系统中,也可在 状态为最新 的工作文件中键入 C-u C-x v v (参见《C-x v v 的高级用法》章节),并输入其他分支上某一版本的版本标识,完成分支切换。例如在 CVS 中,主干(主开发线)的版本标识格式通常为 1.1、1.2、1.3……;基于版本 1.2 创建的第一个分支,其版本标识为 1.2.1.1、1.2.1.2……;基于版本 1.2 创建的第二个分支,版本标识则为 1.2.2.1、1.2.2.2……,依此类推。你也可指定 branch ID分支标识 (即去掉最后一个组成部分的分支版本标识,如 1.2.1),直接切换至该分支的最新版本。
在基于锁定的版本控制系统中,切换至其他分支的同时,会对工作树执行解锁操作(设置为写保护状态)。
切换至某一分支后,所有 VC 命令将默认作用于该分支,直至你切换至其他分支;例如,你提交的所有版本控制文件集,都会被提交至该特定分支。
30.1.11.2. 分支间的变更拉取 / 推送
C-x v P- 在分布式版本控制系统中,将当前分支的本地提交修改同步至另一代码库(又称推送修改)。该概念在集中式版本控制系统中不存在。
C-x v +- 在分布式版本控制系统中,从另一代码库拉取修改,更新本地代码库的当前分支。在集中式版本控制系统中,从代码库更新当前版本控制文件集。
在分布式版本控制系统中, C-x v P (vc-push) 会将当前分支的本地提交修改,同步至另一远程位置(通常为上游代码库)。带前缀参数执行时,命令会提示输入要运行的具体版本控制命令,你可通过该方式指定修改的推送目标;不同系统的默认推送命令分别为:Bazaar 使用 bzr push 、Git 使用 git push 、Mercurial 使用 hg push 。默认命令会始终将修改推送至版本控制系统根据分支配置确定的默认位置的代码库。
执行推送操作前,可使用 C-x v O (vc-log-outgoing) 查看即将推送到上游的修改日志缓冲区,详见《版本控制变更日志》章节。
该命令目前仅支持 Bazaar、Git 和 Mercurial 系统。 推送 的概念在集中式版本控制系统中并不存在,此类系统中该操作属于变更集提交的一部分,因此在集中式版本控制系统中调用该命令会触发错误提示。若在 Bazaar 的绑定分支中执行该命令,同样会触发错误 —— 因为绑定分支中提交变更集时,会自动将修改推送至本地分支所绑定的远程代码库。
在分布式版本控制系统中, C-x v + (vc-pull) 会从上游代码库拉取修改,更新本地代码库的当前分支及其工作树。该命令通常用于更新远程分支的副本(又称克隆版)。带前缀参数执行时,命令会提示输入要使用的具体版本控制命令,你可通过该方式指定修改的拉取源;若未带前缀参数,会从版本控制系统根据分支配置确定的默认位置的代码库拉取修改。
在分布式版本控制系统中, C-x v + 命令目前仅支持 Bazaar、Git 和 Mercurial 系统。在 Bazaar 中,该命令对普通分支调用 bzr pull (将主分支的修改拉取至镜像分支),对绑定分支调用 bzr update (从中央代码库拉取修改);在 Git 中,调用 git pull 从远程代码库获取修改并合并至当前分支;在 Mercurial 中,调用 hg pull -u 从默认远程代码库获取变更集并更新工作目录。
执行拉取操作前,可使用 C-x v I (vc-log-incoming) 查看即将应用的修改日志缓冲区,详见《版本控制变更日志》章节。
在 CVS 等集中式版本控制系统中, C-x v + 命令的作用是从代码库更新当前版本控制文件集。
30.1.11.3. 分支合并
C-x v m在分布式版本控制系统中,将其他分支的修改合并至当前分支。
在集中式版本控制系统中,将其他分支的修改合并至当前版本控制文件集。
在分支开发过程中,你有时需要将其他分支中已完成的修改合并至当前分支。这并非简单的操作,因为两个分支可能存在重叠的修改,甚至出现修改冲突。
在分布式版本控制系统中,可通过 C-x v m (vc-merge) 执行合并操作。在 Bazaar 系统中,该命令会提示输入需传递给 bzr merge 命令的具体参数,并在可能的情况下提供合理的默认值;在 Git 系统中,会提示输入待合并的分支名称,且支持补全功能(基于当前代码库中已有的分支名);在 Mercurial 系统中,会提示输入需传递给 hg merge 命令的参数。合并命令的输出结果会在独立的缓冲区中展示。
在 CVS 等集中式版本控制系统中, C-x v m 会提示输入分支标识,或一对版本标识(参见《分支间的切换》章节);随后系统会提取该分支的修改,或你指定的两个版本之间的修改,并将这些修改合并至当前版本控制文件集。若你在提示时直接按下回车键,Emacs 会直接合并自你检出文件后,同一分支上产生的所有修改。
执行合并操作后,仅有工作树会被修改,你可通过 C-x v D 及相关命令查看合并产生的修改内容(参见《查看并比较旧版本》章节)。若两个分支存在重叠的修改,合并操作会产生 conflict冲突 ;合并命令的输出结果中会出现警告提示,且每个受影响的工作文件中会插入 conflict markers冲突标记 ,将两组冲突的修改内容包裹其中。此时你需要通过编辑文件解决冲突,Emacs 默认会将包含版本控制冲突的缓冲区置于专用的 Smerge 模式 ,该模式提供了用于解决合并冲突的专属命令。冲突解决完成并保存文件后,你必须按常规方式提交这些文件,合并操作才能正式生效(参见《版本控制下的基础编辑》章节)。
30.1.11.4. 新建分支
在 CVS 这类集中式版本控制系统中,Emacs 支持将创建新分支作为提交操作的一部分。提交已修改的版本控制文件集时,键入 C-u C-x v v (带前缀参数的 vc-next-action 命令,参见《C-x v v 的高级用法》章节),Emacs 会随即提示为新版本指定版本标识。你需要为基于当前版本创建的新分支,设定一个合适的分支标识。例如,若当前版本标识为 2.5,那么新分支的标识可设为 2.5.1、2.5.2 等,具体编号根据该节点已有的分支数量而定。
该操作流程不适用于 Git、Mercurial 这类分布式版本控制系统,此类系统中需使用 vc-create-branch 命令( C-x v b c branch-name RET )来创建新分支。
若要在旧版本(已非某分支最新版本的版本)上创建新分支,需先选中该版本(参见《分支间的切换》章节),后续操作步骤则根据所使用的是基于锁定还是基于合并的版本控制系统有所不同。
在基于锁定的版本控制系统中,你需要通过 C-x v v 为该旧版本分支加锁。锁定旧版本时,系统会让你确认是否确实要创建新分支 —— 若选择否,系统会提供锁定最新版本的选项。而在基于合并的版本控制系统中,可跳过此步骤。
完成上述操作后,对文件做出所需修改,再次键入 C-x v v 提交新版本,即可从选中的旧版本开始创建一条新分支。
新分支创建完成后,后续的所有提交操作都会在该分支上生成新版本。若要退出该分支,你必须通过 C-u C-x v v 显式选中其他版本。
30.1.12. 版本控制的杂项命令与功能
30.1.12.1. 变更日志与版本控制
若你在维护带有 ChangeLog 文件的程序时使用 RCS 或 CVS 版本控制系统(参见《变更日志》章节),可通过过往提交的版本控制日志条目,自动生成变更日志条目。
注意:该功能仅适用于 RCS 和 CVS 系统。在现代基于变更集的版本控制系统中,此操作方式并不适用 —— 这类系统中,对 ChangeLog 文件的修改通常会作为变更集的一部分一同提交。此种情况下,你应先编写好变更日志条目,再在提交时将其提取至 *vc-log* 缓冲区中(参见《日志条目缓冲区的功能》章节)。
C-x v a- 打开当前目录的 ChangeLog 文件,并为该目录下的已注册文件,针对自最新变更日志条目之后提交的版本创建新的变更日志条目 (
vc-update-change-log) 。 C-u C-x v a- 功能与上述命令一致,仅针对当前缓冲区对应的文件生成相关条目。
举例来说,假设 ChangeLog 文件的首行日期为 1999-04-10,此后仅有纳撒尼尔・鲍迪奇(Nathaniel Bowditch)在 1999-05-22 向 rcs2log 提交过一次版本,其日志条目为「Ignore log messages that start with '#'.」,执行 C-x v a 后,会在 ChangeLog 中插入如下条目:
1999-05-22 Nathaniel Bowditch <[email protected]> * rcs2log: Ignore log messages that start with '#'.
若版本控制日志条目中指定了函数名(位于某行开头的圆括号内),该信息也会体现在生成的变更日志条目中。例如, vc.el 文件的某条日志条目为「(vc-do-command): Check call-process status.」,则生成的变更日志条目为:
1999-05-06 Nathaniel Bowditch <[email protected]> * vc.el (vc-do-command): Check call-process status.
当 C-x v a 一次性添加多条变更日志条目时,若多条条目为同一作者在相近时间提交,命令会将这些相关条目归为一组;若多个文件对应的日志条目文本完全相同,会将其合并为单个条目。
30.1.12.2. 删除与重命名版本控制文件
M-x vc-delete-file- 提示输入文件名,将该文件从工作树中删除,并将此删除操作标记为待提交状态。
M-x vc-rename-file- 提示输入两个文件名(原文件名和新文件名),在工作树中完成文件重命名,并将此重命名操作标记为待提交状态。若当前缓冲区的文件受版本控制管理,原文件名会默认填充为该文件名。
若你想要删除受版本控制的文件,请使用 M-x vc-delete-file 命令。该命令会先提示输入待删除的文件名,再通过版本控制系统执行删除操作。文件会从工作树中移除,且在版本控制目录缓冲区中(参见《版本控制目录模式》章节),该文件会被标注为 'removed' (已移除)状态。当你执行提交操作后,此次删除才会在代码库中正式生效。
若要重命名受版本控制的文件,键入 M-x vc-rename-file 即可。该命令会依次提示输入两个参数:待重命名的原文件名和新文件名,随后通过版本控制系统完成重命名。重命名操作会立即在工作树中生效,待你提交该重命名后的文件后,此次修改才会在代码库中正式生效。
对于原生支持重命名操作的现代版本控制系统,重命名后的文件会保留原文件的完整修改历史。而在 CVS 及其他早期版本控制系统中, vc-rename-file 命令的实际执行逻辑为:以新文件名创建原文件的副本并完成注册,再删除原文件;此种情况下,文件的修改历史无法被保留。
30.1.12.3. 版本标签
大多数版本控制系统都支持为版本控制目录树的特定版本添加 revision tag版本标签 。在现代基于变更集的版本控制系统中,版本标签只是某一特定版本的符号名称;而在 CVS 这类早期的基于文件的系统中,标签会被添加至所有受版本控制的文件中,使这些文件可作为一个整体被管理。版本标签通常用于标识分发给用户的软件发布版本。
针对标签操作有两个基础命令:一个用于创建指定名称的标签,另一个用于调取已命名的标签。
C-x v s name RET- 将当前目录及其子目录下所有已注册文件的工作版本,定义为名为 标签名 的版本标签 (
vc-create-tag) 。 C-x v r name RET- 为当前目录及其子目录下的所有已注册文件,调取标有标签名的版本。若该名称为分支名,且所使用的版本控制系统会区分分支与标签,此命令会切换至对应分支 (
vc-retrieve-tag) 。
若当前目录及其子目录下存在任何被锁定的文件,该命令会直接抛出错误,且不执行任何修改操作;此设计是为了避免覆盖正在进行的工作内容。
你可将标签名或分支名作为参数,传入 C-x v = 或 C-x v ~ 命令中(参见《查看并比较旧版本》章节)。通过这种方式,你可以将带标签的版本与当前文件进行对比,也可对两个带标签的版本相互对比。
在 SCCS 系统中,版本标签功能由 VC 自行实现,这类标签仅能通过 VC 查看;而后续的大多数版本控制系统(包括 CVS、Subversion、Bazaar、Git 和 Mercurial)都拥有原生的标签功能,VC 会在可用时直接调用该功能,这类标签即便不通过 VC 操作也能被查看。
在基于文件的版本控制系统中,若你重命名了已注册的文件,需同时重命名其主文件, vc-rename-file 命令会自动完成这一操作(参见《删除和重命名受版本控制的文件》章节)。若使用的是 SCCS 系统,你还需更新标签记录,将文件的新名称录入其中(该操作 vc-rename-file 也会自动完成)。若某一旧标签所指向的主文件,已不再以记录中的名称存在,该标签将失效,VC 也无法再调取此标签。本手册不会详细讲解如何手动更新 RCS 和 SCCS 的标签,相关内容超出了手册的讲解范围。使用 vc-rename-file 命令虽能保证标签仍可被正常调取,但无法解决所有问题。例如,程序中的部分文件可能会通过文件名引用其他文件,至少编译配置文件( makefile )中大概率会提及你重命名的文件。若你调取某一旧标签,被重命名的文件会以新名称被调取,而这并非编译配置文件所期望的文件名,因此调取后的程序将无法正常运行。
30.1.12.4. 插入版本控制头信息
在 Subversion、CVS、RCS 和 SCCS 系统中,你可以在工作文件中加入一类名为 version headers版本头信息 的特殊字符串。当文件被提交时,版本控制系统会自动将版本号、提交者用户名以及其他相关信息填充到该版本头信息中。
版本控制(VC)通常不会调用版本头信息中的内容。仅有一个例外:使用 RCS 系统时,若文件中存在版本头信息,Emacs 会通过它来确定文件版本 —— 因为这种方式往往比读取 RCS 主文件更可靠。若要禁止该行为,可将变量 vc-consult-headers 设为 nil ,此后 VC 会始终通过文件权限(若开启了文件权限信任机制)来判断,或直接检查主文件。
要在当前缓冲区中插入合适的头信息字符串,可使用命令 M-x vc-insert-headers 。该命令仅适用于 Subversion、CVS、RCS 和 SCCS 系统。变量 vc-backend-header 中存储了需要插入版本头信息的关键字列表;例如 CVS 系统会使用变量 vc-cvs-header ,其默认值为 '("\$Id\$") (额外的反斜杠是为了防止:若定义该变量的 Emacs Lisp 文件本身受版本控制管理,这个字符串常量会被误解析为版本头信息)。 vc-insert-headers 命令会将列表中的每个关键字,在光标位置单独另起一行插入,两侧用制表符分隔;若有需要,还会将关键字包裹在对应语言的注释定界符中。
变量 vc-static-header-alist 可根据缓冲区名称,指定需要额外添加的头信息字符串。该变量的值为一个列表,每个元素的格式为 (正则表达式 . 格式字符串) 。当正则表达式匹配当前缓冲区名称时,对应的格式字符串也会作为版本头信息的一部分被插入。格式字符串中的%s会被替换为该文件所使用的版本控制类型。
30.1.12.5. 编辑版本控制命令
你可以使用前缀命令 C-x v ! (vc-edit-next-command) 编辑版本控制(VC)即将执行的 Shell 命令行。该功能主要用于为版本控制系统命令添加可选的命令行参数,同时避免让 VC 的命令集及其与后端的接口产生不必要的复杂改动。
例如,Git 可生成多个分支的日志,但 C-x v b l (vc-print-branch-log) 命令仅会提示输入单个分支的名称。若要获取多个分支的日志,你可以键入 C-x v ! C-x v b l ,随后在 VC 即将执行的 git log 命令末尾追加其他分支的名称即可。
30.1.12.6. 准备补丁文件
在项目协作过程中,通过电子邮件发送补丁文件来共享修改内容是一种常见做法。你可以使用 VC 的 vc-prepare-patch 命令完成此操作。该命令会先提示你输入想要共享的版本号,以及接收补丁的目标邮箱地址(多个地址按 crm-separator 的取值分隔,默认以逗号分隔)。随后命令会通过你的邮件用户代理(MUA)处理这些版本的补丁文件,生成后供你检查并发送。
若在日志查看缓冲区中,已标记部分版本并以交互方式调用该命令,系统将直接使用这些被标记的版本生成补丁。
根据用户配置项 vc-prepare-patches-separately 的取值, vc-prepare-patch 命令会生成一封或多封邮件:默认值 t 表示为每个版本单独生成一封邮件,依次展示;若设为 nil ,则会生成单封邮件,并将所有补丁文件附加在邮件正文中。
若你需要定期提交补丁文件,可将用户配置项 vc-default-patch-addressee 设为常用的接收邮箱地址,该地址会在调用 vc-prepare-patch 时作为默认值使用。项目维护者可将该配置项设为目录局部变量(参见《按目录设置局部变量》章节)。
30.1.13. 定制版本控制
变量 vc-handled-backends 用于指定版本控制(VC)需要支持的版本控制系统。其默认值为(RCS CVS SVN SCCS SRC Bzr Git Hg),包含当前所有受支持的版本控制系统。若希望 VC 忽略其中一个或多个系统,将对应系统名称从该列表中移除即可。若要完全禁用 VC,将该变量设为 nil 。
列表中各系统的排序具有实际意义:当你打开一个在多个系统中均完成注册的文件时,VC 默认会使用 vc-handled-backends 中排在靠前位置的系统;文件首次注册时(参见《将文件注册到版本控制系统》章节),系统的排序也会影响 VC 的选择逻辑。
30.1.13.1. 通用选项
Emacs 通常不会为受版本控制的源文件生成备份文件。若希望即便对版本控制文件也生成备份文件,可将变量 vc-make-backup-files 设为非空值。
若你未察觉符号链接指向的是受版本控制的文件,通过该符号链接编辑此文件可能会导致意外结果。变量 vc-follow-symlinks 用于控制 Emacs 打开指向版本控制文件的符号链接时的行为:值设为 ask (默认值)时,Emacs 会先请求确认;设为 nil 时,仅显示警告信息;设为 t 时,会自动跟随符号链接并打开实际的文件。
若将 vc-suppress-confirm 设为非空值,执行 C-x v v 和 C-x v i 时会直接保存当前缓冲区而不弹出确认提示,执行 C-x v u 时也会直接操作,无需确认。
VC 模式的大部分操作,都是通过执行对应版本控制系统的 Shell 命令完成的。若将 vc-command-messages 设为非空值,版本控制模块会实时显示正在执行的 Shell 命令,且在命令执行完成后展示额外的提示信息。
30.1.13.2. RCS 与 SCCS 相关选项
默认情况下,RCS 通过加锁机制协调多用户的操作,同时也支持非严格加锁模式,该模式下无需先锁定文件即可提交修改。可使用 rcs -U 命令为指定文件开启非严格加锁模式,详细说明参见 RCS 的手册页。
VC 推导 RCS 文件的版本控制状态时,会首先在文件中查找 RCS 版本头信息字符串(参见《插入版本控制头信息》章节)。若文件中无此头信息字符串,VC 通常会检查工作文件的文件权限,该方式效率较高。但部分场景下文件权限不可信,此时就必须读取主文件信息,该操作的开销相对较大。此外,主文件仅能告知文件是否被加锁,无法验证当前工作文件是否为该加锁版本。
将变量 vc-consult-headers 设为 nil ,可让 VC 不再通过版本头信息判断文件状态。此后 VC 会始终优先通过文件权限判断(若开启了文件权限信任机制),否则直接检查主文件。
VC 判断 SCCS 文件版本控制状态的逻辑与 RCS 基本一致,但不会读取 SCCS 的版本头信息,因此变量 vc-consult-headers 的设置对 SCCS 的使用无任何影响。
30.1.13.3. CVS 专用选项
可在变量 vc-cvs-global-switches 中指定需要传递给所有 CVS 操作的额外命令行参数,这些参数会直接插入在cvs命令后、待执行的操作名之前。
当使用位于远程服务器的 CVS 代码库时,VC 可尽量减少网络交互,该行为由变量 vc-cvs-stay-local 控制。若该变量值为 only-file (默认值),VC 仅会通过本地 CVS 子目录中的条目,以及此前 CVS 命令返回的信息,判断每个文件的版本控制状态。这会导致一种情况:若你修改了某文件后,其他用户又向代码库提交了该文件的修改,你在尝试提交自己的修改前,不会收到任何冲突提醒。
若将 vc-cvs-stay-local 设为 nil ,VC 在执行 vc-next-action 命令( C-x v v )并决定操作逻辑前,会先查询远程代码库,与处理本地代码库的逻辑保持一致。
你也可将 vc-cvs-stay-local 设为正则表达式,该表达式会与代码库的主机名进行匹配;此时 VC 仅会对主机名匹配该规则的代码库采用本地查询的方式。
使用远程代码库时,Emacs 通常会为每个被编辑文件的原始版本自动创建版本备份。当你首次保存对某文件的修改时,这些本地备份就会生成,而在你将修改提交至代码库后,备份文件会被自动删除。(注意:这类备份并非 Emacs 的普通备份文件,详见《备份文件》章节。) C-x v = 、 C-x v u 等命令会尽可能使用这些自动版本备份,以避免发起网络请求。
将 vc-cvs-stay-local 设为 nil 会禁用自动版本备份功能。
自动版本备份的文件名格式为文件 .~version.~ ,与 C-x v ~ 命令保存旧版本时的命名格式相近(详见《查看并比较旧版本》章节),区别仅在于版本号后多了一个英文句点('.')。相关 VC 命令可兼容这两种版本备份,核心差异是: C-x v ~ 创建的手动版本备份,不会在你执行提交操作后被自动删除。
CVS 默认不使用加锁机制,但可通过其 CVSREAD 或 watch 功能实现类加锁的行为,详细说明参见 CVS 官方文档。若开启了该功能,你可在 Emacs 中使用 C-x v v 命令切换文件的加锁状态,操作方式与基于加锁机制的版本控制系统一致(详见《基于加锁的基础版本控制操作》章节)。
30.2. 项目管理
project项目 是用于开发一个或多个程序的文件集合。属于某一项目的文件通常存储在层级目录结构中,该结构的顶层目录被称为 project root项目根目录 。
一个目录是否为某项目的根目录,由项目专属的底层架构(即 project back-end项目后端 )判定。Emacs 目前支持两种项目后端:一是支持版本控制(VC)的后端(参见《版本控制》章节),该后端将版本控制系统(VCS)的代码库视作一个项目;二是 EDE 后端(即 Emacs 开发环境,参见《Emacs 开发环境》章节)。未来该功能还会扩展,以支持更多类型的项目。
哪些文件属于或不属于某一项目,同样由项目后端判定。例如,支持版本控制的项目后端不会将「ignored被忽略的文件」(参见《忽略版本控制文件》章节)纳入项目范围;同时,该后端默认会将「untracked未被追踪的文件」视作项目的一部分,此行为可通过变量 project-vc-include-untracked 进行控制。
- 用户配置项:
project-mode-line - 若该用户配置项设为非空值,Emacs 会在模式行显示当前项目的名称(若存在);在项目名称上单击
mouse-1鼠标左键,会弹出包含所有项目相关命令的菜单。该配置项默认值为nil。
30.2.1. 作用于文件的项目命令
C-x p f- 打开当前项目中的文件 (
project-find-file) 。 C-x p g- 在当前项目的所有文件中查找正则表达式匹配内容 (
project-find-regexp) 。 M-x project-search- 以交互方式在当前项目的所有文件中查找正则表达式匹配内容。
C-x p r- 在当前项目的所有文件中对正则表达式执行查询替换 (
project-query-replace-regexp) 。 C-x p D- 在当前项目的根目录中启动 Dired 目录编辑器 (
project-dired) 。 C-x p v- 在当前项目的根目录中启动版本控制目录缓冲区 (
project-vc-dir) 。 C-x p s- 在当前项目的根目录中启动子 Shell (
project-shell) 。 C-x p e- 在当前项目的根目录中启动 Eshell (
project-eshell) 。 C-x p c- 在当前项目的根目录中执行编译操作 (
project-compile) 。 C-x p !- 在当前项目的根目录中执行 Shell 命令 (
project-shell-command) 。 C-x p &- 在当前项目的根目录中异步执行 Shell 命令 (
project-async-shell-command) 。 C-x p o- 在当前项目中执行后续输入的任意命令 (
project-any-command) 。
Emacs 提供了一系列便捷操作项目文件的命令,本节将对这些命令进行详细说明。
本节介绍的所有命令均基于 current project当前项目 的概念运行。当前项目由调用命令时的当前缓冲区的默认目录(参见《文件名》章节)判定;若该目录不属于可识别的项目,这些命令会提示你输入项目目录。
C-x p f (project-find-file) 命令可便捷打开当前项目中的文件(参见《打开文件》章节)。与 C-x C-f 不同,该命令无需输入待打开文件的完整路径,仅需输入文件的基名(即省略前置目录)即可。此外,该命令的补全候选列表仅包含当前项目的文件,无其他无关内容。若光标位置存在文件名,该命令会将此文件名作为「future history未来历史记录」的首个候选项;带前缀参数执行时,会包含项目根目录下的所有文件( vc-directory-exclusion-list 中列出的版本控制系统目录除外)。
C-x p g (project-find-regexp) 命令与rgrep功能类似(参见《在 Emacs 中使用 Grep 查找》章节),但仅在当前项目的文件中执行查找。该命令会提示输入待查找的正则表达式,随后在 Xref 模式缓冲区中展示查找结果,你可通过 Xref 模式的相关命令选择匹配项(参见《xref缓冲区中的可用命令》章节)。带前缀参数执行时,该命令会额外提示输入查找的起始基目录,例如可通过此方式将查找范围限定在项目根目录下的某个子目录内。该命令的匹配结果展示方式,受 xref-auto-jump-to-first-xref 变量值影响(参见《通过标识符查找与替换》章节)。
M-x project-search 是 project-find-regexp 的连续查找版。该命令会提示输入待在项目文件中查找的正则表达式,但不会一次性查找所有匹配项并展示,而是找到首个匹配项后即停止,同时打开该匹配文件并定位至匹配位置,方便你对文件进行编辑。若要继续查找后续匹配项,键入 M-x fileloop-continue RET 即可。
C-x p r (project-query-replace-regexp) 命令与 project-search 功能类似,但会像 query-replace 命令一样(参见《查询替换》章节),对每个找到的匹配项提示你是否执行替换,你做出响应后再继续查找下一个匹配项。若你的响应导致 Emacs 退出查询替换循环,后续可通过 M-x fileloop-continue RET 恢复执行。
C-x p d (project-find-dir) 命令会通过补全功能,提示你选择当前项目内的目录,随后打开 Dired 缓冲区列出该目录下的文件(参见《目录编辑器 Dired》章节)。
C-x p D (project-dired) 命令会打开 Dired 缓冲区,列出当前项目根目录下的所有文件(参见《目录编辑器 Dired》章节)。
C-x p v (project-vc-dir) 命令会打开版本控制目录缓冲区,列出当前项目根目录下整个目录树中文件的版本控制状态(参见《版本控制目录模式》章节)。
C-x p s (project-shell) 命令会在新缓冲区中启动 Shell 会话,并将当前项目根目录设为工作目录(参见《从 Emacs 中执行 Shell 命令》章节)。
C-x p e (project-eshell) 命令会在新缓冲区中启动 Eshell 会话,并将当前项目根目录设为工作目录(参见《Eshell:Emacs 内置 Shell》章节)。
C-x p c (project-compile) 命令会在当前项目的根目录中执行编译操作(参见《在 Emacs 中执行编译》章节)。
C-x p ! (project-shell-command) 命令会在当前项目的根目录中执行 Shell 命令。
C-x p & (project-async-shell-command) 命令会在当前项目的根目录中异步执行 Shell 命令。
最后, C-x p o (project-any-command) 命令可让你在当前项目环境中,执行后续输入的任意命令(无论是否与文件相关)。
30.2.2. 作用于缓冲区的项目命令
C-x p b- 切换至当前项目的其他缓冲区 (
project-switch-to-buffer) 。 C-x p C-b- 列出当前项目的所有缓冲区 (
project-list-buffers) 。 C-x p k- 关闭当前项目的所有活动缓冲区 (
project-kill-buffers) 。 C-x p o- 在当前项目中执行后续输入的任意命令 (
project-any-command) 。
进行项目开发时,Emacs 中可能会打开大量属于该项目的缓冲区 —— 既包括打开了项目文件的缓冲区,也包括属于项目但未关联任何文件的缓冲区(例如 project-compile 命令创建的 *compilation* 编译缓冲区)。 C-x p b (project-switch-to-buffer) 命令可便捷实现当前项目内的缓冲区切换,该命令会提示输入目标缓冲区名称,且补全候选列表仅包含当前项目的缓冲区。
与 list-buffers 命令功能类似(参见《列出已存在的缓冲区》章节), C-x p C-b (project-list-buffers) 命令会列出已存在的缓冲区,但仅展示属于当前项目的缓冲区内容。
当你完成某个项目的开发工作后,可关闭该项目的所有相关缓冲区,以精简 Emacs 的会话环境, C-x p k (project-kill-buffers) 命令可实现此功能:该命令会关闭当前项目中所有满足 project-kill-buffer-conditions 变量判定条件的活动缓冲区。若 project-kill-buffers-display-buffer-list 变量设为非空值,命令会先展示待关闭的缓冲区列表,再执行关闭操作。
最后, C-x p o (project-any-command) 命令可让你在当前项目环境中,执行后续输入的任意命令(无论是否与缓冲区相关)。
30.2.3. 项目切换
C-x p p- 为其他项目执行 Emacs 命令 (
project-switch-project) 。
对项目文件执行操作的相关命令(参见《对文件执行操作的项目命令》章节),在当前无可用项目时,会便捷地提示你输入项目目录。若你正处于某一项目中,却需要对另一项目执行操作,可使用 C-x p p (project-switch-project) 。该命令会提示你从已识别的项目根目录中选择一个目录,随后展示可对所选项目执行的命令菜单。变量 project-switch-commands 用于控制该菜单中的可用命令,以及每个命令对应的调用快捷键。
变量 project-list-file 指定了 Emacs 用于记录已识别项目列表的文件,其默认值为 user-emacs-directory 目录下的projects文件(参见《Emacs 如何查找你的初始化文件》章节)。
30.2.4. 项目列表文件管理
M-x project-forget-project- 将已识别的项目从项目列表文件中移除。
Emacs 通常会自动在项目列表文件中添加和移除项目,但有时你可能需要手动编辑可用项目列表。执行 M-x project-forget-project 后,命令会提示你从现有可用项目中选择一个,随后将该项目从项目列表文件中移除。
30.3. 变更日志
许多软件项目都会维护一份 change log变更日志 ,这是一个通常命名为 ChangeLog 的文件,按时间顺序记录程序的修改时间与修改内容。有时这类文件会从版本控制系统中存储的变更日志条目自动生成,也可能用于生成这些日志条目;部分项目会维护多个变更日志文件,分别记录某一目录或目录树中的修改内容。
30.3.1. 变更日志命令
Emacs 命令 C-x 4 a 会为当前编辑的文件在变更日志文件中添加一条新条目 (add-change-log-entry-other-window) 。若当前编辑的是备份文件,该命令会为原文件添加适配的条目 —— 此功能适用于为当前版本中已删除的函数编写日志条目。
执行 C-x 4 a 会打开变更日志文件并创建新条目, 除非 最新的条目已是你以本人名义在今日创建的。该命令还会为当前文件创建一个新的条目项,对于多数编程语言,它甚至能自动推测出被修改的函数或其他对象的名称。
Emacs 会从当前编辑文件所在目录开始,向上遍历目录树查找变更日志文件。默认情况下,若找到版本控制代码库的根目录,查找会停止;可通过自定义变量 change-log-directory-files 修改此规则。
当变量 add-log-keep-changes-together 设为非空值时, C-x 4 a 会在该文件已有的条目项中追加内容,而非新建条目项。
你可将多个同类型的修改合并记录。若首次执行 C-x 4 a 后未输入任何文本,后续再次执行 C-x 4 a 会为该变更日志条目添加另一个相关符号。
若变量 add-log-always-start-new-record 设为非空值,无论上一条目是否由你在当日创建, C-x 4 a 都会始终新建一条条目。
当变量 change-log-version-info-enabled 设为非空值时, C-x 4 a 会将文件的版本号添加至变更日志条目中。它会搜索文件的前 10% 内容,通过变量 change-log-version-number-regexp-list 中的正则表达式匹配获取版本号。
打开变更日志文件时,Emacs 会自动启用 变更日志模式 。在该主模式下,一组相关的条目项视为一个段落,每条日志条目视为一个页面,以此简化条目编辑操作。按下 C-j 或触发自动换行时,新行会继承上一行的缩进格式,方便录入条目内容。
启用变更日志模式后,可使用命令 change-log-goto-source (默认绑定快捷键 C-c C-c )跳至光标附近的变更日志条目对应的源码位置。此后,连续执行 next-error 命令(默认绑定快捷键 M-g M-n 和 C-x ` )可在变更日志的各条目间跳转,且会直接跳转到文件中实际被修改的位置,而非仅在日志条目间切换;也可使用 previous-error 命令向后跳转日志条目。
可使用命令 M-x change-log-merge 将其他日志文件合并至变更日志模式的缓冲区中,且会保留条目按日期的排序规则。
版本控制系统是另一种追踪程序修改、维护变更日志的方式。如今许多使用版本控制系统的项目,不会再维护单独的带版本控制的变更日志文件,因此你可能希望将此类文件排除在代码库外。若变量 add-log-dont-create-changelog-file 设为非空值,当变更日志文件尚未存在时, C-x 4 a (add-change-log-entry-other-window) 等命令会将修改记录在一个命名规范的临时缓冲区中,而非创建实际的文件。
无论你使用实体的变更日志文件,还是临时缓冲区记录变更日志,若存在相关的变更日志条目,均可在版本控制日志缓冲区中按下 C-c C-a (log-edit-insert-changelog) 插入这些条目。详见《日志条目缓冲区的功能》章节。
30.3.2. ChangeLog 文件的格式
一条变更日志条目以标题行开头,标题行包含当前日期、你的姓名(取自变量 add-log-full-name )以及电子邮箱地址(取自变量 add-log-mailing-address )。除标题行外,变更日志中的所有行均以空格或制表符开头。日志条目的主体由若干条目项组成,每个条目项的起始行均为「空白符 + 星号」的格式。以下是两个示例条目,均记录于 1993 年 5 月,分别包含两个条目项和一个条目项:
1993-05-25 Richard Stallman <[email protected]> * man.el: Rename symbols 'man-*' to 'Man-*'. (manual-entry): Make prompt string clearer. * simple.el (blink-matching-paren-distance): Change default to 12,000. 1993-05-24 Richard Stallman <[email protected]> * vc.el (minor-mode-map-alist): Don't use it if it's void. (vc-cancel-version): Doc fix.
单个日志条目可描述多项修改内容,每项修改应单独作为一个条目项,或作为同一条目项中的独立行。条目项之间通常需保留一个空行;若多个条目项彼此关联(如同一修改在不同位置的实现),则无需空行,将其归为一组即可。
你需要在变更日志文件的末尾添加版权声明和授权声明,示例如下:
Copyright 1997--1998, 2025 Free Software Foundation, Inc. Copying and distribution of this file, with or without modification, are permitted provided the copyright notice and this notice are preserved.
当然,你需要将示例中的年份和版权持有方替换为实际信息。
30.4. 查找标识符引用
identifier标识符 指程序语法子单元的名称,包括函数、子程序、方法、类、数据类型、宏等。在编程语言中,每个标识符都是该语言语法中的一个符号,标识符也被称作 tag标记 。
程序的开发与维护,需要具备快速查找各标识符的定义位置和引用位置、在整个项目中重命名标识符等能力。这些能力在非编程类的主模式中查找引用时同样适用,例如文本文档或 TeX 文档中的章节、小节、附录等也可被视作子单元,其名称可作为标识符使用。在本章中, identifiers标识符 一词为统称,既包括程序源码中的各类子单元名称,也涵盖其他文本中的子单元名称。
Emacs 为这些功能提供了一个统一的接口,即 'Xref' (交叉引用)。
Xref 需结合对应主模式的专属信息和方法实现功能,例如要检索哪些文件中的标识符、如何查找标识符的引用、如何对标识符进行补全等,这些均为与模式相关的专属逻辑。Xref 会将这类模式相关的功能委托给对应主模式提供的后端实现;对于未实现专属后端的主模式,Xref 也为其部分命令提供了默认实现。
Xref 后端可通过多种方式实现功能,以下为几类典型示例:
- 部分主模式内置了语言符号的查找方式。例如 Emacs Lisp 模式的后端,可通过检索 Emacs Lisp 解释器维护的包加载历史、查阅内置文档字符串来识别 Emacs Lisp 符号,进而实现符号定义的查找。(此类后端的一个缺点是,仅能识别已加载到解释器中的子单元。)
- 若当前缓冲区所属项目(参见《项目操作》章节)和主模式已启用 Eglot 插件,Eglot 会调用外部语言服务器程序,获取服务器提供的项目内标识符定义相关数据并对接至 Xref。详见《Eglot:Emacs 语言服务器协议客户端》中的《Eglot 功能》章节。
- 可通过外部程序扫描相关文件提取标识符引用信息,并构建引用数据库,后端在需要列出或查找引用时直接访问该数据库即可。Emacs 发行版中内置了
etags工具,该命令可提取程序中的标识符定义并生成标记表,支持多种编程语言及 HTML 等非编程主模式,相关说明参见《创建标记表》章节。支持 etags 的语言主模式,可将标记表作为其后端的实现基础。(此类后端的一个缺点是,标记表需要定期重新构建,才能保证内容的时效性。)
30.4.1. 查找标识符
本节介绍用于查找标识符引用并对标识符执行各类查询的命令。标识符的每一处引用,既可以是 define an identifier标识符的定义 (例如给出程序子单元的实现代码、文档章节的正文内容),也可以是 use th identifier标识符的使用 (例如调用某个函数或方法、为变量赋值、在交叉引用中提及某一章节等)。
30.4.1.1. 标识符查阅
xref 最核心的功能,是帮助你查找指定标识符的定义位置。
M-.- 查找标识符的定义 (
xref-find-definitions) C-M-. pattern RET- 查找名称匹配指定模式的所有标识符 (
xref-find-apropos) C-x 4 . RET- 查找标识符的定义,并在另一个窗口中展示结果 (
xref-find-definitions-other-window) C-x 5 . RET- 查找标识符的定义,并在新框架中展示结果 (
xref-find-definitions-other-frame) M-x xref-find-definitions-at-mouse- 查找鼠标点击位置的标识符定义
M-,- 返回上一次调用M-.及相关命令的位置 (
xref-go-back) C-M-,- 前进到上一次调用M-,后跳转离开的位置 (
xref-go-forward) M-x xref-etags-mode- 切换
xref至etags后端模式
M-. (xref-find-definitions) 会查找光标所在位置标识符的定义。若带前缀参数执行,或光标处无标识符,命令会提示你输入待查找的标识符(若希望该命令始终弹出输入提示,可将配置项 xref-prompt-for-identifier 设为 t )。
在为 M-. 输入标识符参数时,可使用迷你缓冲区的常规补全命令(参见《补全》章节),补全候选列表会显示所有已识别的标识符名称。
与多数可切换缓冲区的命令一致, xref-find-definitions 也提供了变体命令:一个在新窗口展示目标缓冲区,一个为目标缓冲区新建框架,分别对应 C-x 4 . (xref-find-definitions-other-window) 和 C-x 5 . (xref-find-definitions-other-frame)。
xref-find-definitions-at-mouse 命令的功能与 xref-find-definitions 一致,区别在于它会查找鼠标事件发生位置及附近的标识符名称。该命令通常绑定至鼠标事件,例如 C-M-mouxe-1 。
C-M-. (xref-find-apropos) 是适用于标记的类apropos命令(参见《Apropos 命令》章节),会在已选中的标记表中,列出名称匹配指定正则表达式的所有标识符。该命令与 M-. 的核心区别是:它通过正则表达式匹配标识符,而非将符号名作为固定字符串进行匹配。默认情况下,命令会像 M-. 一样弹出 *xref* 缓冲区,若需展示额外输出,可自定义配置项 tags-apropos-additional-actions ,具体说明参见该配置项的文档。
若上述任一命令找到多个匹配的定义,默认会弹出 *xref* 缓冲区展示所有匹配候选,并选中该缓冲区所在的窗口( C-M-. 只要找到至少一个匹配结果,就会弹出 *xref* 缓冲区)。缓冲区中会为每个候选结果显示文件名,以及该文件中匹配的标识符。在该缓冲区中,你可选择任意候选结果进行展示,同时还能使用多种扩展命令(详见《xref缓冲区中的可用命令》章节)。此外,若配置项 xref-auto-jump-to-first-definition 的值为 move ,Emacs 会自动将光标移至 *xref* 缓冲区中首个候选结果的位置,此时按下 RET 回车即可展示该候选的定义;若该配置项值为 t 或 show ,首个候选结果会自动在专属窗口中展示 —— t 会同时选中展示首个候选定义的窗口, show 则保持 *xref* 缓冲区的窗口为选中状态。该配置项默认值为 nil ,即仅在 *xref* 缓冲区展示候选结果,不会自动选中或展示任何候选的定义,直至你在缓冲区中手动选择。
若你切换离开展示多个候选结果的 *xref* 缓冲区窗口,可通过 M-g M-n (next-error) 和 M-g M-p (previous-error) 命令在候选结果间切换(参见《编译模式》章节)。
如需返回此前展示标识符定义的位置,可使用 M-, (xref-go-back) ,该命令会跳回上一次调用 M-. 的位置。因此,你可通过 M-. 查找并查看某标识符的定义,再通过 M-, 返回操作前的位置。 M-, 支持回溯你在位置历史中向前跳转的所有步骤,直至首次调用 M-. 的初始位置,也可停留在历史中的任意中间位置。
若你此前通过 M-, 回溯了过多步骤,或希望重新查看某次回溯前的位置,可使用 C-M-, (xref-go-forward) 再次向前跳转。该命令与 M-. 的区别是:每一步跳转无需将光标移至待查找定义的标识符处。 C-M-, 支持回溯你在位置历史中向后跳转的所有步骤,直至最后一次调用 M-, 的位置,也可停留在历史中的任意中间位置。
部分主模式提供的 xref 支持功能,可能无法找到某些标识符。例如在 Emacs Lisp 模式中(参见《执行 Emacs Lisp 表达式》章节), M-. 默认仅能查找已加载至当前 Emacs 会话,或支持自动加载的 Lisp 包中的函数和变量(参见《Emacs Lisp 参考手册》中的《自动加载》章节)。若 M-. 无法找到目标标识符,可尝试强制 xref 使用 etags 后端(参见《查找标识符引用》章节):通过 M-x xref-etags-mode 开启 Xref Etags 次要模式,再重新调用 M-. 即可(需提前在源码文件的目录树中运行 etags 命令创建标记表,详见《创建标记表》章节)。
30.4.1.2. xref 缓冲区中的可用命令
专用的 XREF 模式为 *xref* 缓冲区提供了以下命令:
RETmouse-1- 跳转到当前行对应的引用位置 (
xref-goto-xref) 。带前缀参数执行时,会同时隐藏*xref*缓冲区。 mouse-2- 功能与
mouse-1一致,且会将展示*xref*缓冲区的窗口设为当前选中窗口 (xref-select-and-show-xref) 。 n.- 跳转到下一个引用位置,并在另一个窗口中展示 (
xref-next-line) 。 N- 跳转到下一个引用组的首个引用位置,并在另一个窗口中展示 (
xref-next-group) 。 p,- 跳转到上一个引用位置,并在另一个窗口中展示 (
xref-prev-line) 。 P- 跳转到上一个引用组的首个引用位置,并在另一个窗口中展示 (
xref-prev-group) 。 C-o- 在另一个窗口中展示当前行对应的引用位置 (
xref-show-location-at-point) 。 r pattern RET replacement RET- 对匹配指定模式的引用执行交互式查询替换 (
xref-query-replace-in-results) ,将匹配内容替换为指定的替换内容。该命令仅可在展示了所有相关文件中某一标识符所有匹配结果的*xref*缓冲区中使用。详见《通过标识符进行查找与替换》章节。 g- 刷新
*xref*缓冲区的内容 (revert-buffer) 。详见《恢复缓冲区》章节。 M-,- 关闭展示
*xref*缓冲区的窗口,随后跳转到 Xref 栈的上一个位置 (xref-quit-and-pop-marker-stack) 。 q- 关闭展示
*xref*缓冲区的窗口 (xref-quit) 。
此外,方向键、 C-n 、 C-p 等常规导航命令也可在该缓冲区中使用,仅用于移动光标位置,不会跳转展示对应的引用。
30.4.1.3. 基于标识符的搜索与替换
本节介绍的命令可对标识符本身,或引用了标识符的文件执行各类查找与替换操作。
M-?- 查找光标所在位置标识符的所有引用。
rM-x xref-query-replace-in-results RET replacement RETC-u M-x xref-query-replace-in-results RET regexp RET replacement RET- 在
*xref*缓冲区显示的所有标识符名称中,以交互方式将匹配 regexp正则表达式 的内容替换为指定替换内容。 M-x xref-find-references-and-replace RET from RET to RET- 以交互方式将标识符原标识符的所有引用重命名为新名称新标识符。
M-x tags-search RET regexp RET- 在已选中的标记表对应的所有文件中查找指定正则表达式。
M-x tags-query-replace RET regexp RET replacement RET- 在已选中的标记表对应的每个文件中执行正则表达式查询替换。
M-x fileloop-continue- 从当前光标位置重新执行上述最后两条命令中的任意一条。
M-? 会查找光标所在位置标识符的所有引用,必要时会提示输入标识符并提供补全功能。根据当前使用的后端(参见《查找标识符引用》章节),即便光标处能识别出有效标识符,该命令也可能弹出输入提示;带前缀参数执行时,始终会提示输入标识符。(若希望该命令始终弹出提示,可将变量 xref-prompt-for-identifier 自定义为 t ;若设为 nil ,则仅在光标处无可用标识符时才弹出提示。)执行后会在 *xref* 缓冲区展示该标识符的所有引用,包含引用所在的文件名和行号,该缓冲区可使用 XREF 模式的所有命令(参见《xref缓冲区中的可用命令》章节)。
若变量 xref-auto-jump-to-first-xref 的值为 t , xref-find-references 会自动跳转到 *xref* 缓冲区中的首个结果,并选中展示该引用的窗口;可通过 M-g M-n (next-error) 和 M-g M-p (previous-error) 选择其他结果(参见《编译模式》章节)。若值为 show ,首个结果会被展示,但仍保持 *xref* 缓冲区的窗口为选中状态。若值为 move ,首个结果会在 *xref* 缓冲区中被选中,但不会展示;此时按下回车即可实际跳转到该引用位置。该变量默认值为 nil ,即仅在 *xref* 缓冲区展示结果,不会选中任何结果,也不会跳转到引用本身,直至手动在缓冲区中选择结果。
r (xref-query-replace-in-results) 与常规的 M-x query-replace-regexp 类似,会先读取替换内容,随后将 *xref* 缓冲区中显示的标识符,在其所有被引用的文件和位置中统一重命名为该替换内容,该功能在重构时代理标识符重命名非常实用。此命令需在 M-? 生成的 *xref* 缓冲区中调用,默认会将每个标识符的完整名称替换为指定内容;若带前缀参数执行,会先提示输入匹配标识符名称的正则表达式,仅将标识符名称中匹配该正则的部分替换为指定内容。
M-x xref-find-references-and-replace 与 xref-query-replace-in-results 功能类似,在需要将指定名称原标识符的单个标识符重命名时,使用该命令会更便捷。
M-x tags-search 会通过迷你缓冲区读取正则表达式,随后在已选中的标记表对应的所有文件中逐个查找匹配内容,过程中会显示当前正在搜索的文件名,方便查看进度,找到首个匹配项后即停止执行。该命令需要存在可用的标记表(参见《标记表》章节)。
使用 tags-search 找到首个匹配项后,通常需要继续查找其余匹配项,可通过 M-x fileloop-continue 恢复执行 tags-search ,查找下一个匹配项,该命令会先搜索当前缓冲区的剩余内容,再继续搜索标记表中的其他文件。
M-x tags-query-replace 会在标记表对应的所有文件中执行一次正则表达式查询替换,与常规的 M-x query-replace-regexp 类似,会先读取待搜索的正则表达式和替换内容,查找逻辑与 M-x tags-search 基本一致,但会反复执行并根据用户的操作处理匹配项,关于查询替换的更多信息,参见《查询替换》章节。
可通过自定义变量 tags-case-fold-search 的值,控制标记表查找命令的大小写敏感性,默认与 case-fold-search 的设置保持一致(参见《查找时的宽松匹配》章节)。
单次调用 M-x tags-query-replace 即可遍历标记表中的所有文件,但实际使用中常需要临时退出,可通过任意无查询替换特殊含义的输入事件实现;后续可键入 M-x fileloop-continue 恢复执行查询替换,该命令会恢复最近一次执行的标记表查找或替换命令。例如,若要跳过当前文件的剩余内容,可键入 M-> M-x fileloop-continue 。
请注意,本节介绍的命令比 xref-find-definitions 系列命令的查找范围更广: xref-find-definitions 系列仅查找匹配指定字符串或正则表达式的标识符定义,而 xref-find-references 、 tags-search 和 tags-query-replace 会像常规的查找和替换命令在当前缓冲区中执行的逻辑一样,查找标识符或正则表达式的 所有出现位置 。
除了 xref-find-references 和 tags-search ,也可将 grep 作为子进程运行,让 Emacs 逐个展示匹配的行,参见《在 Emacs 中使用 Grep 查找》章节。
30.4.1.4. 标识符查询
C-M-iM-TAB- 对光标附近的文本执行补全,若已加载标记表则可调用其完成补全 (
completion-at-point) 。 M-x list-tags RET file RET- 展示程序文件文件名中定义的所有标识符列表。
C-M-. regexp RET- 展示所有匹配该正则表达式的标识符列表 (
xref-find-apropos) ,详见《标识符查找》章节。 M-x tags-next-file- 访问已选中标记表中记录的文件。
在大多数编程语言模式下,键入 C-M-i 或 M-TAB (completion-at-point) 可对光标处的符号执行补全。部分模式会为该命令提供适配自身的专属补全逻辑;对于未提供专属补全的模式,若当前已加载标记表,该命令会调用标记表生成补全候选项,详见《符号名称的补全》章节。
M-x list-tags 会通过补全功能读取已选中标记表所覆盖的某个文件名称,随后展示该文件中定义的所有标记;命令会将当前缓冲区对应的文件名,作为待列出标记的默认文件。注意:请勿在文件名中包含目录路径,除非标记表中记录的该文件名本身带有目录。该命令仅适用于 etags 后端,且要求项目存在可用的标记表,详见《标记表》章节。
M-x tags-next-file 用于访问已选中标记表中覆盖的文件。首次调用时,会打开标记表中的第一个文件;后续每次调用会依次打开下一个文件,若带前缀参数执行,则会返回并打开第一个文件。该命令要求当前已选中某一标记表。
30.4.2. 标签表
tags table标记表 会记录对某一程序或文档的源码进行扫描后提取出的标记18。从生成文件中提取的标记,指向的是原始文件,而非标记提取过程中被扫描的生成文件。生成文件包括从 Cweb 源文件、Yacc 解析器定义、Lex 扫描器定义生成的 C 文件,经过预处理的 C.i 文件,以及对 .fpp 源文件预处理后生成的 Fortran 文件。
生成标记表时,需在文档或源码文件上执行 shell 命令 etags 。etags程序会将标记写入标记表文件(简称 tags file标记文件 ),标记文件的惯用名称为 TAGS ,详见《创建标记表》章节。(也可通过其他软件包的相关命令创建标记表,只需保证生成的文件格式一致即可。)
Emacs 通过 etags 软件包将标记表用作 xref 的受支持后端之一。由于标记表由 Emacs 发行版自带的 etags 命令生成,本节将对其进行详细介绍。
Ebrowse 工具与 etags 功能类似,专为 C++ 语言定制,详见《Ebrowse 用户手册》中的 Ebrowse 章节。Semantic 软件包则提供了另一套独立于 etags 的标记生成与使用方案,详见 Semantic 相关章节。
30.4.2.1. 源文件标签语法
以下为最常用编程语言的标记语法定义规则:
在 C 语言代码中,所有 C 函数、类型定义(typedef),以及结构体(struct)、共用体(union)、枚举(enum)的定义均为标记。宏定义#define、宏取消#undef和枚举常量也会被视作标记,除非生成标记表时指定 '
--no-defines' 参数;全局变量同理,除非指定 '--no-globals' ,结构体成员亦是如此,除非指定 '--no-members' 。使用 '--no-globals'、'--no-defines'和'--no-members'参数可大幅减小标记表文件的体积。为 etags 命令添加'
--declarations'参数,除函数定义外,函数声明和外部变量也会被标记。- 在 C++ 语言代码中,除支持所有 C 语言的标记结构外,成员函数也会被识别;成员变量同样会被识别,除非使用 '
--no-members' 参数。运算符定义的标记名格式为 'operator+' 这类形式。若指定 '--class-qualify' 参数,类中的变量和函数标记名将以 'class::variable'、'class::function'的格式命名。默认情况下,类的方法和成员不会添加类限定符,此举能更精准地在源码中匹配其名称。 - 在 Java 语言代码中,标记包含所有 C++ 支持的结构,同时新增接口(interface)、继承(extends)和实现(implements)相关结构。类中的变量和函数标记名格式为 '
class.variable'、'class.function'。 在 LaTeX 文档中,
\chapter、\section、\subsection、\subsubsection、\eqno、\label、\ref、\cite、\bibitem、\part、\appendix、\entry、\index、\def、\newcommand、\renewcommand、\newenvironment、\renewenvironment这些命令的参数均为标记。若在调用
etags前,于环境变量TEXTAGS中指定其他命令,这些命令的参数也可成为标记。该环境变量的值为以冒号分隔的命令名列表,示例如下:TEXTAGS="mycommand:myothercommand" export TEXTAGS
以上代码(使用 Bourne Shell 语法)指定 '
\mycommand' 和 '\myothercommand' 命令的参数也会被定义为标记。- 在 Lisp 语言代码中,所有通过
defun定义的函数、defvar或defconst定义的变量,以及所有行首以(def开头的表达式的第一个参数,均为标记。例外情况为:(defvar foo)形式的表达式会被视作声明,仅当指定 '--declarations' 参数时才会被标记。 - 在 Scheme 语言代码中,标记包括所有通过
def定义的内容、名称以 'def' 开头的结构定义的内容,同时还包括文件顶层通过set!赋值的变量。
etags 同时支持多种其他编程语言,各语言标记规则如下:
Ada 语言:函数、过程、包、任务和类型为标记;指定 '
--packages-only' 参数,可仅为包创建标记。Ada 中同一名称可用于不同类型的实体(如同时作为过程和函数名);包、过程、函数等实体还分为规约(spec,即接口)和体(body,即实现)。为方便精准选择所需定义,Ada 的标记名会添加后缀标识实体类型:
- /b:包体
- /f:函数
- /k:任务
- /p:过程
- /s:包规约
- /t:类型
例如,执行
M-x find-tag RET bidule/b RET会直接跳转到包 bidule 的包体,而执行M-x find-tag RET bidule RET会搜索所有名为 bidule 的标记。- 汇编语言:行首出现且后接冒号的标签为标记。
- Bison/Yacc 输入文件:每条规则会将其定义的非终结符作为标记;文件中包含的 C 代码部分会按 C 语言规则解析标记。
- Cobol 语言:段落名为标记,即第 8 列开始且后接句点的单词。
- Erlang 语言:文件中定义的函数、记录和宏为标记。
- Fortran 语言:函数、子程序和块数据为标记。
- Go 语言:包、函数和类型为标记。
- HTML 输入文件:标题(title)、一级至三级标题(h1、h2、h3)为标记;锚点中的name=属性、所有id=属性也为标记。
- Lua 语言:所有函数为标记。
- Makefile 文件:目标为标记;变量同样为标记,除非指定 '
--no-globals' 参数。 - Objective C 语言:类、类分类、方法和协议的定义为标记;类中的变量和函数标记名格式为 '
class::variable'、'class::function'。 - Pascal 语言:文件中定义的函数和过程为标记。
- Perl 语言:通过package、sub、use constant、my、local关键字定义的包、子程序和变量为标记;若需为全局变量创建标记,可使用 '
--globals' 参数。子程序标记名格式为 'package::sub' ,默认包中定义的子程序标记名格式为 'main::sub' 。 - PHP 语言:函数、类和宏定义(defines)为标记;变量同样为标记,除非使用 '
--no-members' 参数。 - PostScript 语言:函数为标记。
- Prolog 语言:行首的谓词和规则为标记。
- Python 语言:行首的def或class定义会生成标记。
- Ruby 语言:行首的def、class或module定义会生成标记;常量也会生成标记。
- Rust 语言:所有通过fn、enum、struct或
macro_rules!定义的内容为标记。
你也可基于正则表达式匹配生成标记(参见《Etags 正则表达式》章节),以适配其他文件格式和编程语言。
30.4.2.2. 创建标签表
etags 程序用于创建标记表文件,它支持多种编程语言的语法解析,具体规则详见《源文件标记语法》章节。etags 的运行方式如下:
etags inputfiles…
etags 程序会读取指定的文件,并在当前工作目录下生成一个名为 TAGS 的标记表文件。你可通过 '--output=file' 选项自定义标记表的文件名;若将文件名指定为 '-',则会将标记表内容输出至标准输出。也可使用 '--append' 选项,将新生成的标记表内容追加至已存在的文件中。
若指定的文件不存在,etags 会自动查找其压缩版本,解压缩后再进行读取。在 MS-DOS 系统中,若命令行指定了 mycode.c 但该文件不存在,etags 还会查找 mycode.cgz 这类命名的文件。
当标记表中记录的文件发生修改,导致标记表失效时,可重新运行 etags 程序更新。若标记表未记录某一标记,或标记关联到了错误的文件,在更新标记表前,Emacs 将无法找到该标记的定义;但如果仅因文件编辑导致标记表中记录的位置略有偏差,Emacs 仍能找到正确位置,只是会有轻微的延迟。
因此,无需在每次编辑文件后都更新标记表。仅当新增了需要纳入列表的标记、将标记定义从一个文件移至另一个文件,或文件发生大量修改时,才需要更新标记表。
通过向 etags 传递 '--include=file' 选项,可让一个标记表包含另一个标记表的内容,此时该标记表的覆盖范围将同时包含自身处理的文件和被包含标记表所覆盖的所有文件。
若运行 etags 时使用相对路径指定源文件,生成的标记表中将记录相对于标记表初始生成目录的文件路径。这种方式下,若将包含标记表和所有源文件的整个目录树移动,标记表仍能正确指向源文件。但如果标记表的输出目标为 '-' (标准输出)或位于 /dev 目录下,文件名将以当前工作目录为基准生成相对路径,这在将标记表内容写入标准输出时会非常实用。
使用相对路径时,标记表文件不应是指向其他目录中标记表的符号链接,否则会导致表中的文件路径全部失效。
若运行 etags 时使用绝对路径指定源文件,生成的标记表中将记录绝对文件路径。这种方式下,只要源文件的位置不变,即便移动标记表文件,它仍能正确指向对应的源文件。在 MS-DOS 和 MS-Windows 系统中,绝对路径以设备名 ':/' 开头,其他系统则以 '/' 开头。
当需要为大量文件生成标记表时,直接在命令行列出所有文件名可能会因系统命令行长度限制而失败。此时可在文件名的位置输入 '-' ,让 etags 从标准输入读取文件名,以此规避该限制,示例如下:
find . -name "*.[chCH]" -print | etags -
etags 会根据输入文件的文件名和内容自动识别其使用的编程语言:首先通过文件名和扩展名匹配常用编程语言的命名规则;对于部分有专属解释器的语言(如 Perl 的 perl、Prolog 的 pl),etags 会接着检查文件首行是否有 '#!解释器名' 形式的解释器指定语句,并与已知的解释器进行匹配。
若上述自动识别方式失效,或你需要手动指定编程语言,可通过 '--language=name' 选项显式声明。该选项可与文件名交替使用,后续的所有文件都将使用该选项指定的语言解析。指定 '--language=auto' 可让 etags 恢复通过文件名和内容自动推测编程语言;指定 '--language=none' 则会完全关闭针对特定语言的解析逻辑,此时 etags 将仅通过正则表达式匹配识别标记(详见《Etags 正则表达式》章节)。当处理的文件使用 etags 暂不支持的编程语言时,该选项非常实用,可避免 etags 默认以 Fortran 和 C 语言的规则解析文件。
'--parse-stdin=file' 选项主要用于从其他程序中调用 etags 的场景,该选项在命令行中只能使用一次,替代文件名的位置。使用该选项时,etags 会从标准输入读取内容,并将生成的所有标记关联至指定的文件。
对于 C 和 C++ 语言,若源文件未遵循 GNU 编码规范 —— 即仅让函数、结构体定义等顶层定义的大括号( '{' 和 '}' )出现在行首,建议使用 '--ignore-indentation' 选项,防止 etags 将行首的闭合大括号错误解析,导致标记识别异常。
执行 etags --help 会输出 etags 支持的所有编程语言列表、通过文件名推测语言的规则,以及所有可用选项的简短说明。若在该命令后追加一个或多个 '--language=name' 选项,还会输出该语言的标记生成详细规则。
除了手动调用 etags 创建和更新标记表,也可让 Emacs 自动完成该操作。启用全局次要模式 etags-regen-mode 后,Emacs 会根据需要自动生成标记表,并在编辑任何参与标记生成的源文件时,自动更新标记表。该模式会通过当前的项目配置(详见《项目操作》章节)确定需要传递给 etags 的文件,以重新生成项目的标记表。可通过以下用户配置项自定义该次要模式的行为:
etags-regen-program- 用于重新生成标记表的程序,默认值为
etags。 etags-regen-program-options- 传递给标记表重新生成程序的命令行选项。
etags-regen-ignores- 重新生成标记表时需要忽略的文件匹配模式列表(通配符形式)。
若通过 M-x visit-tags-table 手动选择了标记表(详见《选择标记表》章节), etags-regen-mode 会自动失效,不再创建和更新标记表 —— 该行为默认你希望手动管理标记表。若要取消该效果,可执行 tags-reset-tags-tables 命令。
30.4.3. 选择标签表
Emacs 在任一时刻最多仅有一个 selected tags table已选标签表 ,所有操作标签表的命令都会优先使用该标签表。要选择标签表,可执行命令 M-x visit-tags-table ,该命令会以参数形式读取标签表文件名,默认值为 TAGS ;程序会从默认目录向上递归搜索,将首个包含 TAGS 文件的目录作为该命令的默认目录。
Emacs 并不会在执行选择操作时立即读取标签表内容, visit-tags-table 命令的核心作用仅为将文件名存入变量 tags-file-name ,无其他额外操作。该变量的初始值为 nil ,此值会触发所有标签表操作命令,使其向用户请求待使用的标签表文件名。
除已选标签表外,Emacs 还会维护一个 标签表列表 ,用于管理你需要协同使用的多个标签表。例如,当你开发的程序依赖某一函数库时,可同时加载程序自身和该函数库的标签表,让 Emacs 能快速检索到两者中的所有标识符。若已选标签表中无目标标识符,或未包含标签命令所需的源文件,该命令会自动尝试使用标签表列表中的其他所有标签表。
当已有标签表处于加载状态时,再次执行 visit-tags-table 加载新标签表,Emacs 会提供两种选择:将新标签表 添加至当前标签表列表 ,或 清空当前列表并新建列表 (仅保留新标签表)。若选择添加,新标签表会与原有标签表协同生效;若选择新建,新标签表将替代所有原有标签表。
你也可通过设置变量 tags-table-list 为 目录名列表 ,来指定精准的标签表集合,示例如下:
(setq tags-table-list '("~/.emacs.d" "/usr/local/lib/emacs/src"))
上述配置会让标签命令检索 ~/.emacs.d 目录和 /usr/local/lib/emacs/src 目录下的 TAGS 文件。检索的优先级由当前编辑的文件所属目录、以及哪个标签表包含该文件共同决定。
注意:请勿同时设置 tags-file-name 和 tags-table-list 两个变量。
30.5. Emacs 开发环境
EDE(Emacs Development Environment)是一款扩展包,可简化在 Emacs 中创建、构建和调试大型程序的操作,为 Emacs 提供了集成开发环境(IDE)的部分核心功能。
本节仅对 EDE 的使用方法作简要说明,完整详情请参阅《Emacs 开发环境》中的 EDE 相关章节。
EDE 以 全局次要模式 实现(参见 “次要模式” 相关内容)。启用该模式可执行命令 M-x global-ede-mode ,或点击菜单栏中 工具 选项下的「项目支持(EDE)」项;也可在 Emacs 初始化文件中添加以下代码,实现启动时自动启用 EDE:
(global-ede-mode t)
激活 EDE 后,菜单栏会新增 Development开发 选项,本节介绍的各类 EDE 命令及其他多数 EDE 命令,均可通过该菜单调用。
EDE 将文件按 projects项目 组织,每个项目对应一个目录树, project root项目根目录 为该目录树的最顶层目录。定义新项目的操作步骤:打开目标项目根目录下的任意文件,执行命令 M-x ede-new ,该命令会提示选择 project type项目类型 —— 项目类型决定了 EDE 管理该项目的底层方式(参见《Emacs 开发环境》中的 EDE 相关章节)。最常用的项目类型为「Make」(基于 Makefile 管理)和「Automake」(基于 GNU Automake 管理,参见《Automake》相关内容)。无论选择哪种类型,EDE 都会在项目根目录生成名为 Project.ede 的文件,用于存储该项目的相关配置信息。
一个项目可包含 一个或多个 targets目标 ,目标可以是目标文件、可执行程序,或由项目中一个 / 多个文件构建生成的其他类型文件。
向项目中添加新目标:执行快捷键 C-c . t (M-x ede-new-target) ,该命令会同时询问是否将 当前文件 添加至该目标(即该目标会基于此文件构建)。目标创建完成后,可通过快捷键 C-c . a (ede-add-file) 为其添加更多文件。
构建单个目标:执行快捷键 C-c . c (ede-compile-target) ;构建项目中所有目标:执行快捷键 C-c . C (ede-compile-project) 。EDE 会根据 文件类型 自动推断目标的构建方式。
30.6. 使用 Emerge 合并文件
程序员在协作中出现思路偏差,对同一程序做出不同方向的修改,这种情况十分常见。要解决这一问题,就需要对两个版本的程序进行合并,而 Emerge 工具能让这项工作变得更简便。关于文件对比的其他方法,可参阅文件对比章节,以及《Ediff 使用手册》中的 Ediff 相关内容。
30.6.1. Emerge 概述
启动 Emerge 可执行以下四条命令之一:
M-x emerge-files- 合并两个指定的文件。
M-x emerge-files-with-ancestor- 参照共同的原始版本,合并两个指定的文件。
M-x emerge-buffers- 合并两个缓冲区。
M-x emerge-buffers-with-ancestor- 以第三个缓冲区作为两个待合并缓冲区的共同原始版本,完成合并操作。
Emerge 相关命令会对比两个文件或缓冲区,并在三个缓冲区中展示对比结果:两个缓冲区分别对应两份待合并的源文本(/A 缓冲区/ 和 B 缓冲区 ),第三个为合并缓冲区,所有合并操作均在此完成。合并缓冲区中会显示完整的合并后文本,而非仅展示差异内容。在两份源文本存在差异的任意位置,你可选择将其中一份的内容纳入合并缓冲区。
若待合并的源文本来自已做 区域窄化 的缓冲区,那么从现有缓冲区读取内容的 Emerge 命令,仅会使用这些缓冲区中可访问的文本区域(参见「区域窄化」相关内容)。
若两份待合并文本均衍生自同一个 共同原始版本 ,Emerge 可借助该版本自动判断哪一方的修改更合理。当其中一个当前版本与原始版本内容一致时,Emerge 会认定另一个当前版本的内容为主动修改,并将其保留至合并后的文本中。如需指定共同原始文本,可使用上述带 'with-ancestor' 后缀的命令,这类命令会依次读取三个文件或缓冲区名称 ——A 变体、B 变体以及共同原始版本。
完成文本对比并准备好相关缓冲区后,即可开始交互式合并。你可在合并缓冲区中输入专属的合并命令,操控整个合并过程(参见「合并命令」相关内容)。对于源文本中每一处连续的差异区域,你可选择保留其中一方的内容,也可对双方内容进行协同编辑。
合并缓冲区采用专属的主模式 ——Emerge 模式,该模式内置了用于选择差异内容的各类命令,同时你也可使用常规的 Emacs 命令编辑该缓冲区。
在任意时刻,Emerge 都会将焦点聚焦于某一处特定的差异,该差异被称为 selected difference选中的差异 。这一处差异会在三个缓冲区中以如下标记标注出来:
vvvvvvvvvvvvvvvvvvvv text that differs ^^^^^^^^^^^^^^^^^^^^
Emerge 会为所有差异按顺序编号,且模式行中会始终显示当前选中差异的编号。
默认情况下,合并缓冲区初始会载入源文本的 A 版本内容;但如果某一处差异的 A 版本与共同原始版本内容一致,那么该差异位置会优先载入 B 版本内容作为初始值。
退出 Emerge 时,合并后的文本会保留在合并缓冲区中,此时你可使用 C-x C-w 将其保存至文件。若为 emerge-files 或 emerge-files-with-ancestor 命令传入数字参数,命令会通过迷你缓冲区读取输出文件的名称(该名称为这类命令最后读取的文件名),退出 Emerge 时,合并后的文本会自动保存至该输出文件。
默认情况下,退出 Emerge 时,相关命令会将输出缓冲区的内容保存至对应文件;若使用 C-] 中止 Emerge 操作,命令则不会自动保存输出缓冲区,但你可根据需要手动保存。
30.6.2. Emerge 的子模式
执行合并命令时可选择两种模式: Fast mode快速模式 和 Edit mode编辑模式 。快速模式下,基础合并命令仅需输入单个字符即可执行,但常规的 Emacs 命令会被禁用;若你仅需使用合并命令,该模式会更为便捷。编辑模式下,所有合并命令均需以前缀键 C-c C-c 开头,同时保留所有常规 Emacs 命令的使用权限;该模式支持对合并缓冲区进行编辑操作,但会降低 Emerge 的操作效率。
输入 e 可切换至编辑模式,输入 C-c C-c f 可切换至快速模式。模式行会分别以字符 'E' 和 'F' 标识编辑模式与快速模式。
Emerge 另有两个附加子模式,会影响特定合并命令的执行方式: Auto Advance mode自动前进模式 和 Skipp Prefers mode跳过优选模式 。
开启自动前进模式后,执行 a 和 b 命令选择差异内容的同时,会自动跳转到下一处差异。若你仅需从两份源文本中选择其一作为合并内容,该模式能让你更快完成整个合并流程。模式行会以字符A标识自动前进模式。
开启跳过优选模式后,执行 n 和 p 命令跳转差异时,会跳过处于「prefer-A」和「prefer-B」状态的差异(参见「差异的状态」),仅展示无默认正确版本的差异内容。模式行会以字符 'S' 标识跳过优选模式,该模式仅在指定了共同原始版本时生效。
输入命令 s a (emerge-auto-advance) 可开启或关闭自动前进模式,输入命令 s s (emerge-skip-prefers) 可开启或关闭跳过优选模式。为这两个命令传入正数值参数会开启对应模式,传入负数或 0 参数会关闭对应模式,无参数时则会切换模式的开启 / 关闭状态。
30.6.3. 差异状态
在合并缓冲区中,差异区域会以由 'v' 和 '^' 字符组成的行进行标记。每一处差异都会处于以下七种状态之一:
A- 差异区域显示 A 版本内容。执行
a命令会将差异强制设为该状态,模式行会以字符 'A' 标识此状态。 B- 差异区域显示 B 版本内容。执行
b命令会将差异强制设为该状态,模式行会以字符 'B' 标识此状态。 default-Adefault-B差异区域默认显示 A 或 B 版本内容,该状态表示你尚未对该差异做出选择。所有差异的初始状态均为
default-A(因此合并缓冲区初始为 A 缓冲区的副本), 被标记为优选 的差异除外(详见下文)。当你选中某一处差异时,其状态会从
default-A或default-B切换为普通的 A 或 B 状态。因此,被选中的差异绝不会处于default-A或default-B状态,且这两种状态也不会在模式行中显示。执行命令
d a可将default-A设为全局默认状态,d b则可将default-B设为全局默认状态。该默认设置会作用于 所有你从未选中、且无优选版本的差异。若你按顺序逐个处理合并中的差异 ,未选中的差异即为当前选中差异之后的所有差异。因此,在按顺序处理时,你可在不同差异段之间使用d a和d b,灵活将合并缓冲区的某段区域默认设为 A 版本、另一段设为 B 版本。prefer-Aprefer-B差异区域显示 A 或 B 版本内容,该状态表示此版本为 优选版本 。优选状态意味着你未对此差异做出显式选择,但 Emerge 根据 共同原始版本 判断,其中一个版本更可能是合理修改 —— 当其中一个版本与共同原始版本内容一致时,另一个版本会被判定为主动修改,进而标记为优选。例如,当 A 缓冲区内容与共同原始版本一致时,B 版本会被设为
prefer-B,因为其更可能是实际需要保留的修改。这两种状态在模式行中分别以
A*和B*标识。combined差异区域显示 A、B 两个版本的 合并内容 ,该状态由执行
x c或x C命令触发。一旦差异进入此状态,直接执行
a和b命令将不再对其生效,除非为这两个命令传入 数字参数 。该状态在模式行中以 'comb' 标识。
30.6.4. 合并命令
以下为快速模式下的合并命令;若在编辑模式中使用,需在所有命令前添加前缀键 C-c C-c :
p- 选中上一处差异。
n- 选中下一处差异。
a- 选用当前差异的 A 版本内容。
b- 选用当前差异的 B 版本内容。
C-u n j- 选中第 n 处差异。
.- 选中光标所在位置的差异。
q- 退出 —— 完成合并操作。
C-]- 中止 —— 退出合并流程,且不保存输出内容。
f- 切换至快速模式(编辑模式下实际为
C-c C-c f)。 e- 切换至编辑模式。
l- 重绘所有三个窗口的内容(功能同
C-l);带参数执行时,恢复三个窗口的默认显示布局。 -- 输入前缀数字参数的一部分。
数字键- 同样用于输入前缀数字参数的一部分。
d a- 将当前位置往后的合并缓冲区内容,默认设为 A 版本。
d b- 将当前位置往后的合并缓冲区内容,默认设为 B 版本。
c a- 将当前差异的 A 版本内容复制至杀环。
c b- 将当前差异的 B 版本内容复制至杀环。
i a- 在光标位置插入当前差异的 A 版本内容。
i b- 在光标位置插入当前差异的 B 版本内容。
m- 在当前差异的首尾位置标记光标和标记位。
^- 将三个窗口同步向下滚动(功能同
M-v)。 v- 将三个窗口同步向上滚动(功能同
C-v)。 <- 将三个窗口同步向左滚动(功能同
C-x <)。 >- 将三个窗口同步向右滚动(功能同
C-x >)。 |- 重置所有三个窗口的水平滚动位置。
x 1- 将合并窗口收缩为单行显示(可使用
C-u l恢复全屏显示)。 x c- 合并当前差异的两个版本内容(参见「合并两个版本的内容」章节)。
x f- 在帮助窗口中显示 Emerge 当前操作的文件 / 缓冲区名称(可使用
C-u l恢复窗口布局)。 x j- 将当前差异与下一处差异合并为一个差异(带参数执行
C-u x j时,将当前差异与上一处差异合并)。 x s- 将当前差异拆分为两个差异;使用该命令前,需在三个缓冲区的光标位置,分别定位到想要拆分差异的位置。
x t- 剔除当前差异首尾处的相同行;此类行出现的场景为:A、B 版本内容一致,但均与原始版本存在差异
30.6.5. 退出 Emerge
执行 q 命令 (emerge-quit) 会完成合并操作;若你已指定输出文件,该命令会将合并结果写入此文件。命令会将 A、B 缓冲区恢复至原有内容;若这两个缓冲区由 Emerge 创建且你未对其做任何修改,命令会直接删除这两个缓冲区。同时,该命令会禁用合并缓冲区中的所有 Emerge 命令,避免后续执行此类命令破坏各缓冲区的内容。
按下 C-] 会 中止 合并操作,即退出 Emerge 且不会将内容写入输出文件。若你未指定输出文件,中止合并与完成合并这两种操作并无实际区别。
若 Emerge 命令由其他 Lisp 程序调用,该命令会返回布尔值t表示合并成功完成,返回 nil 则表示你中止了合并操作。
30.6.6. 合并两个版本的内容
有时你希望为某一处特定差异保留两个版本的内容,可执行 x c 命令实现此需求,该命令会按如下形式编辑合并缓冲区:
#ifdef NEW B缓冲区中的版本内容 #else /* not NEW */ A缓冲区中的版本内容 #endif /* not NEW */
上述示例使用 C 语言预处理器条件编译指令分隔两个版本的内容,你也可通过设置变量 emerge-combine-versions-template 为自定义字符串,指定分隔所用的标识格式。在该自定义字符串中, '%a' 表示 A 版本内容的插入位置, '%b' 表示 B 版本内容的插入位置。生成上述示例效果的默认配置如下:
"#ifdef NEW\n%b#else /* not NEW */\n%a#endif /* not NEW */\n"
30.6.7. Emerge 的细节要点
合并过程中, 切勿手动编辑 A、B 缓冲区 。Emerge 会对这两个缓冲区做临时修改,最终会将其恢复至原始状态。
你可以同时执行多个合并操作,但 不可将同一个缓冲区作为多个合并操作的输入源 —— 因为这类缓冲区中的临时修改会互相干扰,导致操作异常。
启动 Emerge 的耗时可能较长,原因是程序需要对文件进行完整的对比;在 diff 对比操作完成前,Emacs 无法执行其他任何操作。未来或许会有开发者对 Emerge 做优化,让程序在处理大文件时,于后台执行对比操作 —— 如此一来,在 Emerge 准备好接收命令前,你仍可使用 Emacs 进行其他操作。
完成合并环境的初始化后,Emerge 会执行钩子函数 emerge-startup-hook (详见「钩子函数」相关内容)
30.7. 漏洞引用
多数拥有一定用户量的项目,会通过 缺陷跟踪软件 记录缺陷报告,并为每份报告分配一个唯一且简短的编号或标识符。这类标识可用于引用特定缺陷,例如在修复某一缺陷的代码上方添加源码注释、在文档文件中提及,或是在邮件列表、IRC 频道的讨论中引用。
次要模式 bug-reference-mode (缺陷引用模式)和 bug-reference-prog-mode (编程缺陷引用模式)可对这类缺陷引用进行高亮显示,还能实现点击引用直接跳转到项目缺陷跟踪系统中对应的缺陷报告页面。其中 bug-reference-prog-mode 是 bug-reference-mode 的变体,仅会对 源码注释和字符串内 的缺陷引用进行高亮。
该模式的正常运行,需要提前配置两个核心参数:缺陷引用的语法规则(变量 bug-reference-bug-regexp ),以及用于查阅缺陷报告的跟踪系统 URL 格式(变量 bug-reference-url-format )。由于不同项目的配置规则通常不同,建议在 按目录本地变量 或 文件本地变量 中单独指定这些参数。
举个例子,若项目中通常以 'bug#1234' 或 'Bug-1234' 的形式引用缺陷报告,且该缺陷在跟踪系统中的访问地址为 https://project.org/issues/1234,只需在文件中添加以下本地变量配置即可:
;; Local Variables: ;; bug-reference-bug-regexp: "\\([Bb]ug[#-]\\([0-9]+\\)\\)" ;; bug-reference-url-format: "https://project.org/issues/%s" ;; End:
正则表达式的 第一个捕获组 匹配的字符串,会作为 bug-reference 创建高亮覆盖层的范围,即这部分内容会被高亮并设置为可点击状态。
bug-reference-bug-regexp 中 第二个捕获组 匹配的字符串,会用于替换 bug-reference-url-format 中的 %s 占位符。
需要注意的是, bug-reference-url-format 也可设为 函数 ,以适配更复杂的使用场景。例如,当需要通过缺陷引用中的不同部分区分普通缺陷和合并请求,进而跳转至不同 URL 时,即可使用函数配置。
自动配置
若激活 bug-reference-mode 并执行了钩子函数 bug-reference-mode-hook 后, bug-reference-bug-regexp 或 bug-reference-url-format 仍为 nil ,该模式会自动调用 bug-reference-auto-setup-functions 中的函数,依次尝试为这两个变量配置合适的值,直至某个函数执行成功。
目前该变量中包含三类自动配置函数:
- 版本控制文件配置:由变量
bug-reference-forge-alist和bug-reference-setup-from-vc-alist配置,默认支持 GNU 项目(使用 https://debbugs.gnu.org作为缺陷跟踪系统,缺陷通常以 'bug#13' 形式引用,同时兼容多种其他写法),也支持 GitLab、Gitea、SourceHut、GitHub 等主流代码托管平台。若部署了这类平台的私有实例,可通过bug-reference-forge-alist快速配置 bug-reference。 - 邮件信息推导配置:由变量
bug-reference-setup-from-mail-alist配置,可从邮件文件夹 / 邮箱文件名、邮件头信息中自动推导配置,支持 Emacs 内置的新闻和邮件阅读器 Gnus(网络新闻)与 Rmail(邮件阅读)。 - IRC 频道配置:由变量
bug-reference-setup-from-irc-alist配置,支持 Emacs 内置的 IRC 客户端 Rcirc(详见《Rcirc 使用手册》)和 ERC(详见《ERC 使用手册》)。
对于上述几乎所有模式,只需直接启用 bug-reference-mode 即可完成配置,仅 Rmail 需要稍作特殊设置,配置示例如下:
;; 若文件受版本控制,启用基于版本控制的配置 (add-hook 'prog-mode-hook #'bug-reference-prog-mode) ;; Gnus(摘要和文章缓冲区) (add-hook 'gnus-mode-hook #'bug-reference-mode) ;; Rmail 特殊配置 (add-hook 'rmail-show-message-hook #'bug-reference-mode-force-auto-setup) ;; Rcirc (add-hook 'rcirc-mode-hook #'bug-reference-mode) ;; ERC (add-hook 'erc-mode-hook #'bug-reference-mode)
Rmail 的配置中,不能直接使用模式钩子,需将 rmail-show-message-hook 与函数 bug-reference-mode-force-auto-setup 配合使用 —— 该函数会激活 bug-reference-mode 并强制触发自动配置。原因是 Rmail 中所有邮件都存放在同一个缓冲区,而每次显示新邮件时,都需要重新执行配置。
为第三方包添加支持
为第三方包添加bug-reference自动配置的流程通常十分简单,只需完成以下步骤:
- 编写一个 无参配置函数 ,用于收集所需的配置信息(例如邮件客户端需获取邮件头的 List-Id/To/From/Cc 等字段值);
在函数中调用以下辅助函数之一完成配置:
bug-reference-maybe-setup-from-vc:根据bug-reference-setup-from-vc-alist完成版本控制相关配置;bug-reference-maybe-setup-from-mail:根据bug-reference-setup-from-mail-alist完成邮件相关配置;bug-reference-maybe-setup-from-irc:根据bug-reference-setup-from-irc-alist完成 IRC 相关配置。
若配置函数能成功为
bug-reference-mode完成配置,需返回非 nil 值(通常将上述辅助函数作为函数最后一步执行,即可满足该要求)。- 最后将该配置函数添加至
bug-reference-auto-setup-functions中即可。
注意:所有自动配置函数的 第一步 ,都应先检查自身是否适用当前场景(例如通过判断 major-mode 的值)。
与 debbugs 包的集成
若项目的缺陷跟踪系统部署在 https://debbugs.gnu.org 服务器,可通过 Emacs 的debbugs包在 Emacs 内直接浏览和回复缺陷报告,该包可通过 包菜单 下载安装(详见《Emacs Lisp 包》章节)。debbugs包提供了次要模式 debbugs-browse-mode ,可在 bug-reference-mode 和 bug-reference-prog-mode 基础上叠加激活,配置如下:
(add-hook 'bug-reference-mode-hook 'debbugs-browse-mode) (add-hook 'bug-reference-prog-mode-hook 'debbugs-browse-mode)
31. 缩写
已定义 abbrev缩写 指输入后会自动展开为其他文本的词汇,由用户自定义展开规则。例如,你可将 'foo' 定义为缩写,使其展开为 'find outer otter' ;此后只需输入 'foo' 并按下 SPC 空格键,即可在缓冲区中插入 'find outer otter' 。
Emacs 还提供第二种缩写功能 —— dynamic abbrev expansion动态缩写展开 ,需通过显式命令触发:以光标前的字符为前缀,在缓冲区中查找以该前缀开头的其他词汇,实现对应字符的展开。详见「动态缩写展开」章节。
第三种是 hippie expansion嬉皮式展开 ,是对缩写展开功能的通用化扩展。详见《自动输入相关功能》中的「嬉皮式展开」章节。
31.1. 缩写概念
abbrev缩写 指被定义为可 expand展开 为指定文本的词汇。当你在缩写后输入 单词分隔符 时,该缩写会自动展开 —— 即用其对应的展开文本替换缩写本身。例如,若将 'foo' 定义为展开为 'find outer otter' 的缩写,那么输入 f o o . 后,缓冲区中会插入 'find outer otter.' 。
缩写仅在 缩写模式(Abbrev mode) 开启时生效,该模式是一个缓冲区局部的次要模式。关闭缩写模式并不会让已定义的缩写规则丢失,只是在重新开启该模式前,缩写不会自动展开。执行命令 M-x abbrev-mode 可切换缩写模式的开关;若为该命令传入数字参数,正参数会开启缩写模式,非正参数则会关闭。详见「次要模式」相关内容。
缩写可设置 mode-specific definitions模式专属定义 ,仅在某一种主模式下生效;也可设置 global definitions全局定义 ,在所有主模式中均生效。同一个缩写可同时拥有全局定义,以及针对不同主模式的多个模式专属定义。当前主模式对应的模式专属定义,会 覆盖 其全局定义。
无论缩写模式是否开启,你都可在编辑会话中 交互式定义缩写 。同时,你也能将缩写定义列表保存至文件中,后续编辑会话可重新加载该文件,复用已定义的缩写规则。
31.2. 定义缩写
C-x a g- 定义全局缩写,将光标前的一个或多个单词设为该缩写的展开文本 (
add-global-abbrev) 。 C-x a l- 功能与上一命令类似,定义 当前主模式专属 的缩写 (
add-mode-abbrev) 。 C-x a i g- 将缓冲区中的指定单词定义为全局缩写 (
inverse-add-global-abbrev,反向定义) 。 C-x a i l- 将缓冲区中的指定单词定义为当前主模式专属的缩写 (
inverse-add-mode-abbrev,反向定义) 。 M-x define-global-abbrev RET 缩写名 RET 展开文本 RET- 自定义全局缩写,将指定的 缩写名 定义为展开为指定 展开文本 的全局缩写。
M-x define-mode-abbrev RET abbrev RET exp RET- 自定义模式专属缩写,将指定的 缩写名 定义为当前主模式下展开为指定 展开文本 的缩写。
M-x kill-all-abbrevs- 清除所有缩写定义,恢复为无缩写的初始状态。
常规定义方法
定义缩写的常用方式为:先输入该缩写想要展开成的目标文本,将光标移至文本末尾,执行 C-x a g (add-global-abbre) 。该命令会通过迷你缓冲区读取你要定义的 缩写名 ,并将光标前的内容设为该缩写的展开文本。可通过数字参数指定取光标前多少个单词作为展开文本。例如,要将 'foo' 定义为展开为 'find outer otter' 的缩写,需先在缓冲区中输入 'find outer otter' ,再执行 C-u 3 C-x a g foo RET 即可。
若开启了临时标记模式( transient-mark-mode ,默认开启),会直接将 激活的选区内容 设为待定义缩写的展开文本;若未开启该模式,为 C-x a g 传入0 参数( C-u 0 C-x a g ),同样可将选区内容设为展开文本。
命令 C-x a l (add-mode-abbrev) 的操作方式与 C-x a g 完全一致,唯一区别是该命令定义的缩写仅在当前主模式下生效。
反向定义方法
C-x a i g (inverse-add-global-abbrev) 和 C-x a i l (inverse-add-mode-abbrev) 为 反向定义命令 ,适用于 缩写名已在缓冲区中 的场景:只需将光标定位到该缩写名处,执行对应命令,通过迷你缓冲区输入该缩写的 展开文本 ,即可完成定义。定义完成后,该缩写名会立即展开为你指定的文本。
无缓冲区内容定义
若不想在缓冲区中输入任何内容,也可通过 define-global-abbrev 命令定义全局缩写,该命令会直接依次读取缩写名和展开文本两个参数,无需依赖缓冲区内容; define-mode-abbrev 命令则以同样方式定义模式专属缩写。
修改与删除缩写
修改缩写定义的方式为:直接对该缩写名重新执行定义命令即可。若该缩写已有定义,重新定义时,Emacs 会先弹出确认提示,确认后才会覆盖原有定义。
删除缩写定义的方式为:为缩写定义命令传入负参数。其中 C-u - C-x a g 用于删除全局缩写的定义, C-u - C-x a l 用于删除当前模式专属缩写的定义。执行 M-x kill-all-abbrevs 可一次性删除所有缩写定义(包括全局和模式专属)。
31.3. 控制缩写展开
启用缩写模式后,若光标前的缓冲区内容为已定义的缩写,此时输入自插入的空白字符或标点符号(空格、逗号等),该缩写会自动展开。更准确地说,非单词构成字符均可触发缩写展开,而单词构成字符可作为缩写的组成部分。使用缩写最常用的方式是:输入缩写后,输入一个标点或空白字符触发其展开。
缩写展开会保留大小写格式:'foo' 展开为 'find outer otter' , 'Foo' 展开为 'Find outer otter' 。全大写的 'FOO' 默认展开为 'Find Outer Otter' ;若将变量 abbrev-all-caps 设为非 nil 值,该缩写会展开为全大写的 'FIND OUTER OTTER' 。
以下为控制缩写展开的相关命令:
M-'- 为后续待展开的缩写分隔出前缀 (
abbrev-prefix-mark) 。 C-x a e- 展开光标前的缩写 (
expand-abbrev) ,该命令在缩写模式未启用时也可生效。 M-x unexpand-abbrev- 撤销上一次的缩写展开操作。
M-x expand-region-abbrevs- 展开选区中部分或全部的已定义缩写。
你可能需要在缩写展开后的文本前添加前缀,例如:若 'cnst' 展开为 'construction' ,希望通过该缩写输入 'reconstruction' 。直接输入 'recnst' 无法实现,因为该字符串并非已定义的缩写。正确操作是在前缀 're' 和缩写 'cnst' 之间使用命令 M-' (缩写前缀标记),步骤如下:
- 输入前缀 're' ;
- 按下
M-',缓冲区中会插入一个连字符 '-' ,表示该命令已生效; - 输入缩写 'cnst',此时缓冲区内容为 're-cnst' ;
- 输入一个非单词字符触发缩写展开,'cnst' 会展开为 'construction' ,且步骤 2 中插入的连字符会被自动删除,最终得到目标文本 'reconstruction' 。
若希望在缓冲区中保留缩写本身而非展开后的文本,可通过 C-q 搭配标点的方式输入终止字符。例如,输入 'foo' 后按下 C-q , ,缓冲区中会保留 'foo,' ,缩写不会被展开。
若误触发了缩写展开,可按下 C-/ (undo)撤销操作(详见「撤销」章节),该操作会删除展开后的文本,恢复缩写本身。若你希望保留终止的非单词字符,同时恢复未展开的缩写,需通过 C-q 重新输入该终止字符;也可使用命令 M-x unexpand-abbrev ,仅撤销上一次的展开操作,而保留终止字符。
M-x expand-region-abbrevs 会在选区内检索所有已定义的缩写,并逐个询问是否将其展开为对应文本。该命令适用于以下场景:输入文本时使用了缩写,但忘记提前开启缩写模式;也可配合一组专用的缩写定义,实现多处文本的批量替换。该命令在缩写模式未启用时也可生效。
expand-abbrev 函数会通过调用 abbrev-expand-function 变量指定的函数执行缩写展开操作。修改该变量对应的函数,即可对缩写展开的逻辑进行任意自定义(详见《Emacs Lisp 参考手册》中的「缩写展开」章节)。
31.4. 缩写建议
当你手动输入的文本,恰好是当前已启用的缩写所对应的展开文本时,Emacs 会为你给出 缩写建议 。例如,若已定义缩写 'foo' ,其展开文本为 'find outer otter' ,当你手动完整输入 'find outer otter' 后,停止输入时 Emacs 会识别到这一情况,并在回显区显示相关的缩写提示。
要启用缩写建议功能,需将配置项 abbrev-suggest 自定义设为非nil值。
变量 abbrev-suggest-hint-threshold 用于控制向用户推送缩写建议的触发条件,该变量定义了 Emacs 给出缩写建议所需的 最小字符节省量 (即使用缩写相比手动输入能少打的字符数)。例如,若你手动输入了 'foo bar' (共 7 个字符),而当前已定义缩写 'fubar' (5 个字符)可展开为该文本,那么只有当该阈值设为 2 或更小值时,Emacs 才会给出建议;该变量默认值为 3,此情况下上述示例不会触发建议,因为使用该缩写的字符节省量未达到阈值要求。若希望始终收到缩写建议,可将该变量值设为 0。
执行命令 abbrev-suggest-show-report ,会打开一个缓冲区,展示当前编辑会话中出现过的所有缩写建议。如果你收到了多条缩写建议但无法全部记住,该命令会非常实用。
31.5. 查看与编辑缩写
M-x list-abbrevs- 展示所有缩写的定义列表;若传入数字参数,仅列出 模式专属缩写 。
M-x edit-abbrevs- 编辑缩写列表,可对缩写定义进行添加、修改或删除操作。
M-x list-abbrevs 命令的输出格式示例如下:
各类其他缩写表… (python-mode-skeleton-abbrev-table) "class" (sys) 0 "" python-skeleton-class (lisp-mode-abbrev-table) "ks" 0 "keymap-set" (global-abbrev-table) "dfn" 0 "definition"
(示例中省略了无实际语义的空行及其他部分缩写表。)
以括号包裹名称的行,是对应 专属缩写表 的标题: global-abbrev-table 包含所有全局缩写,其余以主模式命名的缩写表,则存放对应模式的专属缩写。
在每个缩写表内,非空行各定义一个缩写:行首的单词为 缩写名 ,其后的数字是该缩写的 展开次数 。Emacs 会统计该数值,帮助你了解实际常用的缩写,从而清理不常使用的缩写;行尾的字符串则是该缩写的 展开文本 。
部分缩写会标注 '(sys)' 标识,这类 system abbrevs系统缩写 (详见《Emacs Lisp 参考手册》中的「缩写」章节)由各类模式预先定义, 不会被保存至你的缩写文件 。若要禁用某个系统缩写,可定义一个同名缩写并将其展开文本设为自身,再将该定义保存至缩写文件即可。系统缩写会关联一个钩子函数,用于执行缩写展开操作;在 list-abbrevs 命令展示的缓冲区中,该函数名会紧跟在系统缩写的展开文本之后。
M-x edit-abbrevs 命令允许你在 Emacs 缓冲区中编辑缩写列表,完成缩写定义的添加、修改和删除,该列表的格式与上述 list-abbrevs 的输出格式一致。存放缩写的缓冲区名为 *Abbrevs* ,并启用 编辑缩写模式 (Edit-Abbrevs mode)。在该缓冲区中按下 C-c C-c ,会按照缓冲区中的内容加载缩写定义,同时 删除所有未在列表中列出的缩写定义 。
实际上 edit-abbrevs 与 list-abbrevs 是同一命令,唯一区别在于: edit-abbrevs 会直接选中 *Abbrevs* 缓冲区,而 list-abbrevs 仅在其他窗口中展示该缓冲区。
31.6. 保存缩写
下述命令可让你在不同的编辑会话中保留缩写定义。
M-x write-abbrev-file RET file RET- 将所有已定义的缩写信息写入指定文件。
M-x read-abbrev-file RET file RET- 读取指定文件,并根据文件内容定义对应的缩写。
M-x define-abbrevs- 根据当前缓冲区中的定义内容创建缩写。
M-x insert-abbrevs- 将所有缩写及其对应的展开文本插入当前缓冲区。
M-x write-abbrev-file 会通过迷你缓冲区读取文件名,随后将当前所有的缩写定义信息写入该文件,用于将缩写定义保存下来,供后续编辑会话使用。文件中存储的是一系列 Lisp 表达式,这些表达式执行后,就能还原出你当前所有的缩写定义。
M-x read-abbrev-file 会通过迷你缓冲区读取文件名,再读取该文件的内容,并依照文件中的配置定义缩写。函数 quietly-read-abbrev-file 的功能与其类似,区别在于该函数不会在回显区显示提示信息;此函数无法以交互方式调用,主要用于你的初始化文件中(参见《Emacs 初始化文件》章节)。若为这两个函数传入 nil 作为参数,它们会使用变量 abbrev-file-name 指定的文件,该变量的默认值为 ~/.emacs.d/abbrev_defs ,这是你的标准缩写定义文件,Emacs 启动时会自动从中加载缩写定义。(例外情况:当 Emacs 以批处理模式启动时,不会加载该缩写文件,批处理模式的说明参见《初始化选项》章节。)
当 Emacs 提示你保存所有文件时(如执行 C-x s 或 C-x C-c 命令时),若你对缩写定义做过任何修改,Emacs 会自动提示你保存缩写,并将其保存至 abbrev-file-name 指定的文件中。将变量 save-abbrevs 设为 nil ,可禁用该自动保存功能;若将其设为 silently ,Emacs 会自动保存缩写,且不会弹出确认提示。
M-x insert-abbrevs 和 M-x define-abbrevs 与前述命令功能相近,区别在于二者针对的是 Emacs 缓冲区中的文本。 M-x insert-abbrevs 会在光标后方,将当前所有的缩写定义信息插入到当前缓冲区; M-x define-abbrevs 会解析整个当前缓冲区的内容,并据此定义对应的缩写。
31.7. 动态缩写展开
前文介绍的缩写功能会在你输入文本时自动生效,但所有缩写都必须经过显式定义。与之不同, dynamic abbrevs动态缩写 的含义可由缓冲区内容自动推导得出,而动态缩写的展开仅在你显式触发时执行。
M-/- 搜索缓冲区中以光标前字符为 /dynamic abbrev/前缀的词汇 ,将其作为动态缩写展开光标前的内容 (
dabbrev-expand) 。 C-M-/- 将光标前的内容作为动态缩写完成补全 (
dabbrev-completion) 。
例如,若缓冲区中已有内容 'does this follow ' ,此时输入 f o M-/ ,会自动插入 'follow' —— 因为这是缓冲区中最后一个以fo开头的词汇。为 M-/ 传入 数字参数 ,表示选取从光标位置向前搜索时,找到的第 2 个、第 3 个…… 不同的展开结果。重复按下 M-/ 会继续向前搜索,寻找其他可选的展开结果;当扫描完光标前的所有文本后,会继续搜索光标后的文本。变量 dabbrev-limit 若设为非nil值,可指定在缓冲区中搜索展开结果的最大范围。
扫描完当前缓冲区后, M-/ 通常会继续搜索其他缓冲区。可通过变量 dabbrev-check-all-buffers 和 dabbrev-check-other-buffers 设置是否搜索、以及搜索哪些其他缓冲区。所有主模式派生自 dabbrev-ignored-buffer-modes 中任一模式的缓冲区,都会被跳过不做搜索。
若需更精细地控制待扫描的缓冲区,可自定义变量 dabbrev-ignored-buffer-names 和 dabbrev-ignored-buffer-regexps 。前者的值为需跳过的缓冲区名称列表,后者的值为正则表达式列表;若某个缓冲区的名称匹配其中任意一个正则表达式,动态缩写展开时会跳过该缓冲区。
为 M-/ 传入负参数(如 C-u - M-/ ),表示优先搜索光标后的文本以获取展开结果,再搜索其他缓冲区,仅将光标前的文本作为最后选择。若需重复按下 M-/ 查找其他展开结果,无需再指定参数;重复按下时会先遍历光标后的所有展开结果,再遍历光标前的结果。
展开动态缩写后,你可复制该缩写在原始语境中后续的多个词汇:只需为每个需要复制的后续词汇,按下一次 SPC M-/ 即可,词汇之间的空格和标点会随词汇一同被复制。
你可自定义 M-/ 识别待展开词汇的规则及展开方式,详见「自定义动态缩写」章节。
命令 C-M-/ (dabbrev-completion) 用于完成动态缩写的补全操作。该命令不会逐个尝试可能的展开结果,而是先找出所有匹配的结果,再插入这些结果的 公共前缀文本 ;若结果无公共前缀, C-M-/ 会展示所有补全候选列表,你可按常规方式选择所需结果(详见「补全」章节)。
动态缩写展开与 Abbrev mode缩写模式 完全独立:使用 M-/ 展开词汇的操作,与该词汇是否被定义为普通缩写毫无关联。
31.8. 定制动态缩写
默认情况下,动态缩写展开在搜索匹配的展开文本时 忽略大小写 ,即展开文本的大小写无需与待展开的缩写保持一致。
该特性由变量 dabbrev-case-fold-search 控制:若值为t,搜索时完全忽略大小写;若值为 nil ,待展开缩写与展开文本必须大小写完全匹配;若值为 case-fold-search (默认值),则由变量 case-fold-search 决定搜索展开文本时是否忽略大小写(参见「搜索中的宽松匹配」章节)。
默认情况下,动态缩写展开会 dynamic abbrev you are expanding保留待展开缩写的大小写格式 ,并将匹配到的展开文本转换为对应的大小写形式。
变量 dabbrev-case-replace 控制是否保留动态缩写的大小写格式:若值为t,绝大多数情况下会保留待展开缩写的大小写格式;若值为 nil ,展开文本会始终按原格式直接复制,不做大小写转换;若值为 case-replace (默认值),则由变量 case-replace 决定是否直接原格式复制展开文本(参见「替换命令与宽松匹配」章节)。
但如果匹配到的展开文本本身包含 复杂的混合大小写格式 ,且待展开的动态缩写在已输入范围内与该大小写格式完全匹配,那么无论上述变量如何设置,展开文本都会 始终按原格式直接复制 。例如,若缓冲区中存在文本 variableWithSillyCasePattern ,当你输入 v a M-/ 时,展开后的文本会完整保留原有的大小写格式。
变量 dabbrev-abbrev-char-regexp (若非nil)用于定义动态缩写展开中 被视为单词组成部分的字符 ,该正则表达式必须仅匹配单个字符,不可匹配多个字符;同时,该正则表达式也会决定哪些字符可作为展开文本的组成部分。该变量的默认值为 nil ,具有特殊含义:动态缩写(即光标处的待展开词汇)仅由 单词字符 构成,但搜索展开文本时,会匹配由 单词字符和符号字符 组成的序列。该默认规则通常适用于程序源码中的符号展开,也适用于多种语言的自然可读文本,但对于包含特殊标点字符的文本缓冲区可能并不适用;此种情况下,将该变量值设为 "\\sw" 可能会得到更理想的效果。
在 Shell 脚本和 Makefile 文件中,变量名有时会带 '$' 前缀,有时则不带。针对这类文本的主模式,可通过设置变量 dabbrev-abbrev-skip-leading-regexp ,让动态缩写展开支持 忽略可选的前缀字符 。该变量的值应为一个正则表达式,用于匹配动态缩写展开时需要忽略的可选前缀;默认值为 nil ,表示不忽略任何字符。
32. 目录编辑器(Dired)
Dired 功能会在 Emacs 中创建一个缓冲区,显示某个目录的文件列表,也可按需显示其部分子目录的内容。你可以使用 Emacs 常规命令在该缓冲区中移动,还能通过 Dired 专属命令对列表中的文件执行操作。Dired 同时支持本地目录和远程目录的操作。
Dired 缓冲区默认处于只读状态,不允许插入文本(不过 Wdired mode 支持编辑该缓冲区,详见「编辑 Dired 缓冲区」)。 d 、 x 这类普通的可打印字符,在 Dired 中会被重新定义为专属命令。部分 Dired 命令会为当前行对应的文件(即 current file当前文件 )添加 mark标记 或 flag标记标识 ;另有一些命令则会对带标记的文件或带标识的文件执行批量操作。你可先为目标文件添加标记,再通过单个命令对所有标记文件执行统一操作。
Dired-X 扩展包为 Dired 模式提供了各类额外功能,详见《Dired 扩展用户手册》中的 Dired-X 相关章节。
你也可以使用 C-x C-d (list-directory) 查看目录的文件列表,该命令与 Dired 不同,无法对列出的文件执行后续操作,详见「文件目录」章节。
32.1. 进入 Dired
启动 Dired,请按 C-x d (dired) 。该命令会通过迷你缓冲区读取一个目录名,并打开一个 Dired 缓冲区 ,列出该目录下的文件。你也可以在迷你缓冲区中输入通配符文件名模式,此时 Dired 缓冲区会列出所有匹配该模式的文件。通配符也可以出现在目录部分,例如:
C-x d ~/foo/*.el RET C-x d ~/foo/*/*.el RET
前者列出目录 'foo' 下所有后缀为 '.el' 的文件。后者列出 'foo' 下所有一级子目录中后缀为 '.el' 的文件。
在 POSIX 系统上,如果系统 Shell 支持 globstar (递归通配功能)且已启用,你可以在 Dired 中使用递归通配:
C-x d ~/foo/**/*.el RET
该命令会递归遍历 'foo' 下所有子目录,列出所有 '.el' 文件。注意:不同 Shell 对通配功能的实现略有差异,请查阅你的 Shell 手册确认具体行为。
如果 Shell 支持 globstar 但默认未启用,你仍可以让 Dired 使用该功能:
- 将变量
dired-maybe-use-globstar设为非 nil,Dired 会为它已知的 Shell 自动开启 globstar(支持的 Shell 列表见dired-enable-globstar-in-shell)。
你可以在迷你缓冲区中使用常规的历史记录与补全命令;特别地, M-n 会将当前已访问文件的文件名(如果有)填入迷你缓冲区(见 “迷你缓冲区历史”)。
你也可以直接给 C-x C-f (find-file) 传入一个目录名,来启动 Dired。
按 C-x C-j (dired-jump) ,可以让 Emacs 对任意缓冲区的 default-directory (当前目录)打开 Dired。
- 如果该缓冲区关联着一个文件,此命令会在打开的 Dired 缓冲区中把光标定位到该文件所在行。
- 否则光标会停在目录列表的第一个文件上。
- 一个例外:如果你在 Dired 缓冲区中按
C-x C-j,Emacs 会显示上级目录的列表,并将光标定位到你刚才所在的目录那一行。 - 按
C-x 4 C-j(dired-jump-other-window) 效果相同,但会在新窗口中显示 Dired 缓冲区。
变量 dired-listing-switches 用于指定传给 ls 命令的目录列表参数;这个字符串必须包含 '-l' 。如果你给 dired 命令加前缀参数,可以在输入目录前,通过迷你缓冲区另行指定 ls 参数。无论以哪种方式指定,ls 参数都可以包含:
- 无需参数的短选项(单个字符)
- 以 '
--' 开头、用 '=' 指定参数的长选项。
Dired 对文件名中包含换行符的文件支持不佳。如果你有很多这类文件,可以考虑在 dired-listing-switches 中加入 '-b',它会对所有特殊字符转义,让 Dired 能更好地处理这些文件名。(你也可以临时用 C-u C-x d 加上 '-b'。)
dired-listing-switches 可以声明为连接局部变量,以便适配远程系统的要求(见 “按连接局部变量”)。
Dired 会在模式行中显示调用 ls 时使用的参数。默认情况下,Dired 会尝试判断参数是按文件名还是按日期排序,并在模式行中显示。
- 如果变量
dired-switches-in-mode-line设为as-is,会原样显示参数。 - 如果值是一个整数,参数显示会被截断到该长度。
- 该变量也可以是一个函数:会以
dired-actual-switches为唯一参数调用,并返回要在模式行显示的字符串。
如果你的 ls 程序支持 '--dired' 选项,Dired 会自动传入该选项。它让 ls 对某些特殊文件名输出特殊转义序列,否则 Dired 无法正确解析这些文件名。
在一个 Emacs 会话中第一次运行 Dired 时,它会带 '--dired' 选项调用一次 ls 以检测支持:
- 退出码为 0,则后续都使用 '
--dired' 。 - 否则不使用。
你可以通过自定义变量 dired-use-ls-dired 禁用这一检测:
unspecified(默认):执行检测- 其他非 nil 值:直接使用 '
--dired' nil:不使用 '--dired'
在 MS-Windows、MS-DOS 以及部分远程系统上,Emacs 会自行模拟 ls 命令。相关选项与特性见 “MS-Windows 上的 ls 模拟”。
要在另一个窗口显示 Dired 缓冲区,使用: C-x 4 d (dired-other-window) 。要在另一个独立框架显示: C-x 5 d (dired-other-frame)
按 q (quit-window) 会隐藏 Dired 缓冲区;如果该窗口是专门为它新建的,会直接关闭该窗口。
32.3. 用 Dired 删除文件
Dired 最常见的用法之一是:先把文件 flag标记 为待删除 ,再一次性删除所有被标记的文件。
d- 将当前文件标记为待删除 (
dired-flag-file-deletion) 。 u- 取消删除标记 (
dired-unmark) 。 DEL- 光标移到上一行,并取消该行的删除标记 (
dired-unmark-backward) 。 x- 删除所有标记为待删除的文件 (
dired-do-flagged-delete) 。
你可以将光标移到对应文件行,按 d 为文件打上删除标记。删除标记会在行首以 'D' 显示。该命令会自动将光标移到下一行,因此连续按 d 可以依次标记多个文件。数字前缀参数表示重复次数;负数表示标记前面的文件。
如果选区已激活, d 会标记选区中的所有文件,此时不移动光标,也忽略前缀参数。
先标记再删除,而不是直接删除,是为了降低误删风险。在按 x 执行删除前,你可以用 u 或 DEL 取消标记。 u 与 d 操作类似,只是取消标记而非添加标记。 DEL 向上移动并取消标记,相当于带参数 −1 的 u 。这两个命令的数字前缀同样表示次数,负数表示反向操作。若选区激活,则直接取消选区内所有文件的标记,不移动光标。
要真正删除已标记的文件,按 x (dired-do-flagged-delete) 。该命令会显示所有待删文件列表,并要求输入 yes 确认。确认后,Dired 会删除这些文件,并从 Dired 缓冲区中移除对应行,缓冲区保持打开。
如果你回答 no 或用 C-g 退出确认,将直接回到 Dired,所有删除标记保留,文件不会被删除。
你可以像普通文件一样删除空目录,但默认 Dired 不能删除非空目录。如果将变量 dired-recursive-deletes 设为非 nil,Dired 允许递归删除非空目录及其所有内容,这有一定风险。如果设为 always ,则无条件递归删除非空目录,风险更高。
即使你将 dired-recursive-deletes 设为 nil,有时也可能想一次性递归删除多个目录而不想逐个确认。当你标记了多个目录并确定可以安全删除时,对每个非空目录的删除确认,你可以回答 all ,之后所有剩余目录将不再询问直接删除。
如果将变量 delete-by-moving-to-trash 设为 t ,上述删除命令会把文件或目录 移到系统回收站 ,而不是直接永久删除。详见 “杂项文件操作”。
另一种删除方式:先用 m 标记文件,再用 D 删除,见 “对文件执行操作”。
32.4. 批量标记多个文件
# 、 ~ 、 . 、 % & 和 % d 命令可以根据文件名一次性标记多个文件以待删除:
#- 标记所有自动保存文件(文件名以 '#' 开头和结尾的文件)以待删除(见 “自动保存:防止意外丢失”)。
~- 标记所有备份文件(文件名以 '~' 结尾的文件)以待删除(见 “备份文件”)。
.(句号)- 标记多余的数字编号备份文件以待删除。每个文件的最新几个和最旧几个备份会被保留,中间的备份会被标记。
% &- 标记那些文件名表明可以轻松重新生成的文件以待删除。
% d regexp RET- 标记所有文件名匹配指定正则表达式的文件以待删除。
# (dired-flag-auto-save-files) 标记所有文件名看起来是自动保存文件的文件 —— 即文件名以 '#' 开头和结尾的文件。(见 “自动保存:防止意外丢失”)
~ (dired-flag-backup-files) 标记所有文件名表明是备份文件的文件 —— 即文件名以 ~ 结尾的文件。(见 “备份文件”)
. (句号, dired-clean-directory) 只标记部分备份文件:每个文件除了最新几个和最旧几个版本之外的备份都会被标记。通常,每个文件保留的最新版本数量由变量 dired-kept-versions 决定(而非 kept-new-versions ,后者只在保存时生效)。保留的最旧版本数量由变量 kept-old-versions 决定。
带正数字参数的句号命令,例如 C-u 3 . ,会指定保留的最新版本数量,覆盖 dired-kept-versions 。带负数字参数会覆盖 kept-old-versions ,用参数的绝对值指定每个文件保留的最旧版本数量。
% & (dired-flag-garbage-files) 标记文件名匹配变量 dired-garbage-files-regexp 所指定正则表达式的文件。默认情况下,它匹配 TeX 生成的某些文件、'.bak' 文件,以及 patch 生成的 '.orig' 和 '.rej' 文件。
% d (dired-flag-files-regexp) 标记所有文件名匹配指定正则表达式的文件。匹配时只使用文件名中的非目录部分。你可以用 '^' 和 '$' 限定匹配首尾。在使用 % d 时,你可以通过隐藏子目录来避免它们被标记。见 “隐藏子目录”。
32.5. 在目录编辑器中打开文件
Dired 提供多条命令用于访问或查看 Dired 缓冲区中列出的文件。所有这些命令都作用于当前行对应的文件;如果该文件实际是一个目录,这些命令会对该子目录启动 Dired(新建一个独立的 Dired 缓冲区)。
f- 访问当前行所描述的文件,效果等同于输入
C-x C-f并指定该文件名 (dired-find-file) 。参见 “访问文件”。 RETe- 与 f 等效。
o- 与 f 类似,但在另一个窗口中显示文件缓冲区 (
dired-find-file-other-window) 。Dired 缓冲区仍在原窗口可见,类似于使用C-x 4 C-f访问文件。参见 “多窗口”。 C-o- 访问当前行所描述的文件,并在另一个窗口显示其缓冲区,但不激活该窗口 (
dired-display-file) 。 mouse-1mouse-2- 访问你点击的文件 (
dired-mouse-find-file-other-window) ,会在另一个窗口打开,与o命令类似。 v- 以 查看模式(View mode) 查看当前行对应的文件 (
dired-view-file) 。查看模式提供方便的浏览命令,但禁止修改文件;参见 “查看模式”。 ^- 访问当前目录的上级目录 (
dired-up-directory) 。等效于将光标移到..所在行并按f。 - 用户选项:
dired-kill-when-opening-new-dired-buffer - 在 Dired 中访问新的子目录时,Emacs 默认会打开新缓冲区显示该目录,并保留旧的 Dired 缓冲区。如果此选项设为非 nil,则在进入新目录后会关闭旧的 Dired 缓冲区。这样在遍历目录结构时,始终只会保留一个 Dired 缓冲区。
32.6. Dired 中的标记与标记符
除了用 'D' 为文件做删除标识外,你还可以用其他字符(通常是 '*')为文件做 mark标记 。大多数用于操作文件的 Dired 命令,都是对带 '*' 标记的文件生效;唯一对带 'D' 标识文件生效的命令是 x ,用于删除它们。
下面是用于添加 '*' 标记、取消标记以及操作标记的命令。(用于添加和取消删除标识的命令,见 “使用 Dired 删除文件”。)
m* m- 用 '
*' 标记当前文件 (dired-mark)。如果选区已激活,则标记选区中的所有文件;否则,如果提供了数字参数n,则从当前文件开始,标记后面的n个文件(n为负时标记前面的-n个文件)。如果在子目录标题行上执行此命令,会标记该子目录下的所有文件。 * N- 显示已标记文件的数量与总大小信息 (
dired-number-of-marked-files) 。 * *- 用 '*' 标记所有可执行文件 (
dired-mark-executables)。带数字参数时则取消这些文件的标记。 * @- 用 '*' 标记所有符号链接 (
dired-mark-symlinks) 。带数字参数时则取消这些文件的标记。 * /- 用 '*' 标记所有目录(=.= 和
..除外) (dired-mark-directories) 。带数字参数时则取消这些目录的标记。 * s- 标记当前子目录内除
.和..之外的所有文件 (dired-mark-subdir-files)。 - u=
* u- 取消当前行的标记 (
dired-unmark) 。如果选区已激活,则取消选区中所有文件的标记;否则,如果提供了数字参数 n,则从当前文件开始,取消后面 n 个文件的标记(n 为负时取消前面 -n 个文件)。 DEL* DEL- 光标移到上一行,并取消该行的标记 (
dired-unmark-backward) 。如果选区已激活,则取消选区中所有文件的标记;否则,如果提供了数字参数 n,则从当前文件开始,取消前面 n 个文件的标记(n 为负时取消后面 -n 个文件)。 * !U- 取消当前 Dired 缓冲区中所有文件的所有标记 (
dired-unmark-all-marks) 。 * ? markcharM-DEL取消所有使用指定标记字符的标记 (
dired-unmark-all-files) 。使用M-DEL时,命令会提示输入标记字符。标记字符为单个字符,不要用回车结束。(可通过下面介绍的* c命令,将一种标记字符替换为另一种。)带数字参数时,该命令会逐个询问是否取消标记:
y确认取消;n不取消;!对剩余文件直接全部取消。* C-nM-}- 向下跳转到下一个带标记的文件 (
dired-next-marked-file) 。只要文件带有任意类型的标记,就算作 “marked已标记”。 * C-pM-{- 向上跳转到上一个带标记的文件 (
dired-prev-marked-file) 。 t* t- 反转所有 '
*' 标记 (dired-toggle-marks) :带 '*' 的文件取消标记,不带 '*' 的文件加上 '*' 。以其他方式标记的文件不受影响。 * c old-markchar new-markchar将所有使用 old-markchar旧标记字符 的标记,替换为 new-markchar新标记字符 (
dired-change-marks) 。这是创建和使用 '*' 、 'D' 以外标记的主要方式。参数均为单个字符,不要用回车结束。你几乎可以用任意字符作为标记字符,以区分不同类别的文件。
- 如果 old-markchar旧标记字符 是空格(' '),则对所有未标记文件执行操作;
- 如果 new-markchar新标记字符 是空格,则会取消对应文件的标记。
举例说明该命令的用法,下面这组按键可以:
- 给所有无标记文件打上 'D' 删除标识; 同时取消所有已有 'D' 标识的文件
* c D t * c SPC D * c t SPC
前提是没有文件已使用 't' 作为标记。
% m regexp RET* % regexp RET用 '
*' 标记所有文件名匹配指定正则表达式的文件 (dired-mark-files-regexp) 。该命令与% d类似,区别是用 '*' 标记,而不是用 'D' 标识。匹配时只使用文件名中的非目录部分。可用 '
^' 和 '$' 限定首尾。可以临时隐藏子目录,避免它们被标记(见 “隐藏子目录”)。% g regexp RET用 '
*' 标记所有文件内容匹配该正则表达式的文件 (dired-mark-files-containing-regexp) 。与% m类似,区别是搜索文件内容而非文件名。注意:如果文件已在 Emacs 缓冲区中打开,且
dired-always-read-filesystem为nil(默认),该命令会直接读取缓冲区内容,而不是重新读取磁盘文件。如果文件已被修改但未保存,结果可能与磁盘实际内容不一致。若要避免此问题,可以先恢复文件,或在对应缓冲区开启自动恢复模式。如果希望该命令始终重新读取磁盘文件,可将dired-always-read-filesystem设为非 nil。C-/C-x uC-_撤销 Dired 缓冲区中的操作,例如添加或取消标记 (
dired-undo) 。该命令不会撤销实际的文件操作,也无法恢复 lost files已丢失的文件 !它只撤销缓冲区本身里的改动。在某些对文件执行过实际操作的命令之后使用撤销可能会导致不同步。例如重命名文件后再撤销,会使 Dired 缓冲区里的文件名与磁盘实际目录不一致。
touchscreen-hold进入 “点击选择” 模式:在文件名上按下鼠标按键
mouse‑2会切换该文件的标记状态。在使用触屏设备进行文件管理时,该模式非常实用。当在文件名上检测到 “hold长按” 手势时(参见《在触屏上使用 Emacs》),此模式会自动启用;一旦对标记文件执行任意 Dired 命令,该模式便会自动关闭。
32.7. 对文件执行操作
本节介绍用于操作单个或多个文件的基本 Dired 命令。所有这些命令均为 大写字母 ;执行操作前,它们都会使用迷你缓冲区(minibuffer)读取参数或请求确认。它们都允许通过以下方式指定要操作的文件:
- 如果给命令一个数字前缀参数
n,则从当前文件开始,对接下来的n个文件执行操作。(如果n为负数,则对当前行前面的−n个文件执行操作。) - 否则,如果有文件被标记为 '
*' ,则命令对所有这些标记文件执行操作。 - 否则,命令只对当前文件执行操作。
其他一些 Dired 命令(如 ! 和以 '%' 开头的命令)也遵循相同规则来确定操作对象。
除本节介绍的 Dired 命令外,你还可以对 Dired 缓冲区中显示的一个或多个文件执行 版本控制(VC) 命令。参见:版本控制。
需要目标目录的命令(如复制、重命名文件或创建链接)会尝试自动推测默认目标目录。通常它们会使用当前 Dired 缓冲区的默认目录;但如果选项 dired-dwim-target 非 nil,且某个窗口中显示了另一个 Dired 缓冲区,则会改用该缓冲区的目录作为建议。
你可以自定义 dired-dwim-target ,使其优先选择:
- 下一个显示 Dired 缓冲区的窗口
- 最近使用的显示 Dired 缓冲区的窗口
- 或任意其他函数。当值为函数时,该函数将无参调用,并返回一个目录列表,用作默认值(即默认目标与 “历史记录”)。
以下是用于文件操作的 Dired 命令。
C new RET复制指定文件 (
dired-do-copy) 。参数 new 是目标目录;若只复制单个文件,也可以是新文件名。效果类似 Shell 命令 cp。选项
dired-create-destination-dirs控制在复制 / 重命名时,是否自动创建不存在的目标目录:- 默认值
nil:从不创建 - 值为
always:自动创建 - 值为
ask:创建前询问确认
如果同时开启
dired-create-destination-dirs-on-trailing-dirsep,则目标路径末尾的目录分隔符会被特殊处理。例如复制到 'test/' 而 'test' 不存在时,会先创建 'test' 目录,再将源文件 / 目录复制进去。如果
dired-copy-preserve-time非 nil,复制时会保留原文件的修改时间,类似 'cp -p' 。变量
dired-recursive-copies控制是否递归复制目录(类似 'cp -r' )。默认值top表示递归复制目录前会询问。变量
dired-copy-dereference控制符号链接是原样复制还是解引用后复制(类似 'cp -L' )。默认nil,表示新建符号链接进行复制。用户选项
dired-keep-marker-copy控制此命令如何处理文件标记。默认会给所有新复制的文件打上 'C' 标记。- 默认值
D删除指定文件 (
dired-do-delete) 。类似 Shell 命令rm。与本节其他命令一样,作用于标记文件或接下来
n个文件。与之相对,x(dired-do-flagged-delete) 删除所有 标记位(flagged) 文件。E- 使用外部程序 “打开” 指定文件 (
dired-do-open) 。程序按系统惯例选择,由变量shell-command-guess-open决定。 R new RET重命名指定文件 (
dired-do-rename) 。- 重命名单个文件时, new 是新文件名。
- 重命名多个文件时, new 是目标目录(类似 Shell 命令
mv)。
选项
dired-create-destination-dirs控制是否在 new 中创建不存在的目录。若同时设置
dired-create-destination-dirs-on-trailing-dirsep,则目标路径末尾的目录分隔符会被特殊处理。例如将目录 'old' 重命名为 'new/' 而 'new' 不存在时,会先创建 'new' 目录,再将 'old' 移入;否则 'old' 会直接改名为 'new' 。Dired 会自动更新与重命名文件关联的缓冲区的访问文件名,使其指向新名称。
如果变量
dired-vc-rename-file非 nil,则会通过底层版本控制系统命令重命名文件,使用vc-rename-file。参见:删除与重命名版本控制文件。H new RET- 为指定文件创建硬链接 (
dired-do-hardlink) 。类似 Shell 命令ln。参数 new 是链接存放目录;若只创建一个链接,也可以是链接名。 S new RET- 为指定文件创建符号链接 (
dired-do-symlink) 。类似 'ln -s' 。参数 new 是链接存放目录;若只创建一个链接,也可以是链接名。 Y new RET为指定文件创建相对符号链接 (
dired-do-relsymlink) 。参数 new 是链接存放目录;若只创建一个链接,也可以是链接名。与dired-do-symlink类似,但生成相对路径符号链接,例如:foo -> ../bar/foo
而不是绝对路径:
foo -> /path/that/may/change/any/day/bar/foo
M modespec RET- 修改指定文件的权限位 (permission bits) (
dired-do-chmod) 。 modespec 可以是八进制或符号表示法,与chmod程序参数格式一致。该命令不跟随符号链接,在符号链接权限不可修改的平台上会报错。 G newgroup RET- 将指定文件的所属组改为 newgroup (
dired-do-chgrp) 。 O newowner RET将指定文件的所有者改为 newowner (
dired-do-chown) 。(大多数系统上只有超级用户可执行。)变量
dired-chown-program指定实际执行的程序名。(不同系统中chown位置不同,因此需要此变量。)T timestamp RET- 更新指定文件时间戳 (
dired-do-touch) 。将修改时间更新为 timestamp ,默认为当前时间。类似 Shell 命令touch。 P command RET- 打印指定文件 (
dired-do-print) 。必须指定打印命令,小缓冲区会根据变量lpr-command和lpr-switches给出默认建议(与lpr-buffer使用相同变量)。参见:打印硬拷贝。 Z压缩指定文件 (
dired-do-compress) 。若文件看起来已是压缩文件,则执行 解压 。每个标记文件单独压缩;优先使用gzip,否则用compress。对目录执行此命令时,生成的压缩归档格式由用户选项
dired-compress-directory-default-suffix控制。默认为.tar.gz,通过tar管道到gzip实现。对.tar.gz或.tgz文件按Z可解压到同名(去掉后缀)目录。c- 将指定文件压缩为单个归档文件 (
dired-do-compress-to) ,可存放在文件系统任意位置。默认归档格式由dired-compress-directory-default-suffix控制。另见dired-compress-files-alist。 :d- 解密指定文件 (
epa-dired-do-decrypt) 。参见:EasyPG 助手用户手册中的 Dired 集成。 :v- 验证指定文件的数字签名 (
epa-dired-do-verify) 。参见:EasyPG 助手用户手册中的 Dired 集成。 :s- 对指定文件进行数字签名 (
epa-dired-do-sign) 。参见:EasyPG 助手用户手册中的 Dired 集成。 :e- 加密指定文件 (
epa-dired-do-encrypt) 。参见:EasyPG 助手用户手册中的 Dired 集成。 L- 加载指定的 Emacs Lisp 文件 (
dired-do-load) 。参见:Emacs Lisp 代码库。 B- 对指定的 Emacs Lisp 文件进行字节编译 (
dired-do-byte-compile) 。参见:《Emacs Lisp 参考手册》中的字节编译。 I- 对当前文件运行 Info(假定为 Info 格式文件)。
N- 对当前文件运行 man(假定为
nroff格式手册页文件)。 A regexp RET在所有指定文件中正则表达式搜索 regexp (
dired-do-find-regexp) 。该命令是
xref-find-references的变体,会显示*xref*缓冲区,你可在其中浏览匹配项,并使用该缓冲区支持的命令按需显示。如果标记文件中包含目录,此命令会递归搜索这些目录及其所有子目录中的文件,跳过名称匹配
grep-find-ignored-files的文件和匹配grep-find-ignored-directories的目录。Q regexp RET to RET对每个指定文件执行正则表达式查询替换,将 regexp 替换为字符串 to (
dired-do-find-regexp-and-replace) 。该命令是
xref-query-replace-in-results的变体。它会打开一个*xref*缓冲区,列出正则表达式的所有匹配项,你可以在该缓冲区中使用专用命令(参见*xref*缓冲区中的可用命令)。特别地,如果你退出了查询替换循环,仍可在该缓冲区中按r继续替换更多匹配项。参见《使用标识符进行搜索和替换》。与
dired-do-find-regexp一样,如果被标记的文件中包含目录,此命令会递归地在这些目录及其所有子目录下的所有文件中执行替换,但会跳过名称匹配grep-find-ignored-files的文件,以及名称匹配grep-find-ignored-directories的子目录。
32.8. Dired 中的 Shell 命令
Dired 命令 ! (dired-do-shell-command) 会在迷你缓冲区读取一条 Shell 命令字符串,并在一个或多个文件上执行该命令。Shell 命令操作的文件按照 Dired 命令的常规规则确定(见文件操作)。命令 X 是 ! 的别名。
命令 & (dired-do-async-shell-command) 功能与之相同,区别在于它 异步执行 Shell 命令。(你也可以在 ! 的命令末尾加上 '&' 实现异步。)当命令作用于多个文件时,会为每个文件并行启动一条独立的 Shell 命令。一个例外:如果指定的 Shell 命令以 ';' 或 ';&' 结尾,则会在后台 依次 对每个文件执行命令;Emacs 会等待前一条命令结束后再执行下一条。
对于 ! 和 & ,Shell 命令的 工作目录 都是当前 Dired 缓冲区所在的顶层目录。
如果让 ! 或 & 操作多个文件,命令字符串的写法决定了文件如何传给 Shell 命令:
如果命令字符串中使用被空白包围的 '
*', 命令只执行一次 ,并用文件列表替换 '*' 。文件名顺序与 Dired 缓冲区中的显示顺序一致。例如:
! tar cf foo.tar * RET会把所有选中的文件打包进foo.tar。- 如果你想把 '
*' 当作普通 Shell 通配符(带空白),可以写成 “=*""” 。在 Shell 中等价于 ‘*’ ,但因为 ’*’ 没有被空白单独包围,Dired 不会对其特殊处理。这样做时 Emacs 会要求确认,除非将 ~dired-confirm-shell-command~ 设为 =nil。
- 如果你想把 '
- 否则,如果命令字符串包含被空白包围的
?或`?`,Emacs 会 '*' 为每个文件单独执行一次 '*' 命令,每次用当前文件名替换?和`?`。一条命令中可多次使用?或`?`,都会被同一个文件名替换。如果与 '*' 混用会报错。 - 如果命令字符串中既没有 '
*' ,也没有?和`?`,Emacs 仍会 为每个文件执行一次 命令,并把文件名追加到命令末尾。例如:! uudecode RET会对每个文件执行uudecode。
如需以更复杂的方式遍历文件名,可以直接写 Shell 循环。例如,对每个文件执行 uuencode ,并以原文件名加 .uu 作为输出:
for file in * ; do uuencode "$file" "$file" >"$file".uu; done
用 `?` 写法实现同样效果:
uuencode ? ? > `?`.uu
! 和 & 不会自动刷新 Dired 缓冲区来显示新增或修改的文件,因为它们无法预知会改动哪些文件。可使用 g 命令手动刷新 Dired 缓冲区(见刷新 Dired 缓冲区)。
关于在 Dired 之外执行 Shell 命令的信息,参见单条 Shell 命令。
32.9. Shell 命令自动推测
Dired 会根据文件名,尝试推测你可能想要对它执行的 Shell 命令。例如,当光标位于名为 foo.tar 的文件上并按下 ! 时,Dired 会推测你想执行 'tar xvf',并将其作为默认 Shell 命令提示。
你可以按下 M-n 将默认命令填入小缓冲区进行编辑。如果一个文件对应多条可用命令,多次按 M-n 可依次查看每条匹配命令。
Dired 仅对单个文件尝试推测命令,不会对标记文件列表进行推测。
- 变量:
dired-guess-shell-alist-default - 该变量定义了用于推测适用于某些文件的 Shell 命令的 预设规则 。将其设为
nil可关闭自动推测。用户定义的dired-guess-shell-alist-user中的条目会覆盖这些规则。 - 变量:
dired-guess-shell-alist-optional - 该变量与
dired-guess-shell-alist-default类似,但包含适用于各类媒体格式的外部查看器与播放器。设为nil可关闭对应推测功能。dired-guess-shell-alist-user和dired-guess-shell-alist-default会覆盖这些规则。 - 变量:
dired-guess-shell-alist-user 若非 nil,该变量存放用户自定义的「文件正则表达式 → 建议命令」关联列表。运行
dired-do-shell-command时,这些规则优先级高于dired-guess-shell-alist-default和dired-guess-shell-alist-optional中的预设规则。默认值为nil。该关联列表的每个元素格式为:
(regexp command…)
其中每条 command 可以是字符串,或求值后为字符串的 Lisp 表达式。如果给出多条命令,它们会被临时加入命令历史。
Shell 命令中的 '
*' 代表匹配该 regexp正则表达式 的文件名。Emacs 执行命令时,会将每个 '*' 替换为匹配的文件名。若要为 '.foo' 和 '.bar' 文件后缀添加规则,可将以下代码加入配置文件:
(setq dired-guess-shell-alist-user (list (list "\\.foo$" "foo-command") ; 固定规则 ;; 可添加更多规则... (list "\\.bar$" ; 带条件判断的规则 '(if condition "bar-command-1" "bar-command-2"))))
这会覆盖相同后缀的所有预设规则。
你可以通过 M-x customize-group RET dired-guess RET 查看更多用户选项。
32.10. Dired 中的文件名转换
本节介绍用于系统性修改文件名的 Dired 命令。每条命令都会通过对原有文件名进行变换得到新文件名,并对部分或全部标记文件执行操作。
与基本的 Dired 文件操作命令类似(见文件操作),这里介绍的命令可以对接下来的 n 个文件、所有标有 '*' 的文件,或当前文件进行操作。(标记文件的方法见 Dired 标记与标记位对比。)
本节所有命令均为 interactively交互式 工作:会让你对每个待处理文件确认是否执行。因此,你可以先选中比实际需要更多的文件(例如用一条匹配很多文件的正则表达式),然后在命令提示确认时,通过按 y 或 n 来筛选文件名。
% u- 将选中的每个文件重命名为全大写(dired-upcase)。如果原文件名为 Foo 和 bar,新名将为 FOO 和 BAR。
% l- 将选中的每个文件重命名为全小写(dired-downcase)。如果原文件名为 Foo 和 bar,新名将为 foo 和 bar。
% R 匹配规则 RET 替换规则 RET% C from RET to RET% H from RET to RET% S from RET to RET% Y from RET to RET- 这五条命令分别对应:rename重命名、copy复制、make hard links创建硬链接、make soft links创建软链接、make relative soft links创建相对软链接。它们都会通过正则表达式替换,从旧文件名计算出新文件名。
这四条正则替换命令会在选中的文件名上搜索并替换。它们读取两个参数:正则表达式 from 和替换模板 to ;将每个旧文件名与 from 匹配,然后把匹配部分替换为 to 。你可以在 to 中使用 '\&' 和 '\数字' 来引用旧文件名中匹配到的全部或部分内容,用法与 replace-regexp 相同(见正则表达式替换)。如果正则表达式在文件名中多次匹配,只替换第一个匹配。
示例: % R ^.*$ RET x-\& RET 会给每个文件名前面加上 'x-' 。反向操作(从每个文件名开头去掉 'x-')也可以实现:
- 方法一:
% R ^x-\(.*\)$ RET \1 RET - 方法二:
% R ^x- RET RET(可用 '^' 和 '$' 限定必须匹配整个文件名。)
通常,替换过程不考虑文件所在目录名,只对目录内的文件名生效。如果你指定数字前缀 0,替换会影响包含目录在内的完整绝对路径名。(非零数字前缀用于指定要操作的文件个数。)
你可能希望先用即将用于操作的同一个正则表达式 from 来选中文件。做法是:先用 % m from RET 标记这些文件,再在操作命令中使用同一个正则表达式。为方便使用,所有以 % 开头的文件操作命令,都会默认使用上一条 % 命令的正则表达式。
32.11. 通过 Dired 对比文件
= (dired-diff) 命令使用 diff 程序,将当前文件(光标所在文件)与另一个文件(通过迷你缓冲区输入)进行比较。通过迷你缓冲区指定的文件是 diff 的第一个参数,光标所在文件是第二个参数。 diff 程序的输出会在一个采用 Diff 模式的缓冲区中显示(参见文件比较)。
如果选区(region)处于激活状态,迷你缓冲区默认读取的文件是 标记(mark) 所在位置的文件(即普通 Emacs 标记,而非 Dired 标记;参见设置标记)。否则,如果光标所在文件存在备份文件(参见备份文件),则默认使用该备份文件进行比较。
你也可以使用 ediff-files 比较文件,参见《Ediff 用户手册》中的主入口点。
32.12. Dired 中的子目录操作
一个 Dired 缓冲区通常只显示单个目录,但你也可以选择同时显示它的子目录。
在一个 Dired 缓冲区中显示多个目录的最简单方法,是为 ls 命令指定选项 '-lR' 。(如果你在运行 Dired 时给出数字前缀参数,就可以在迷你缓冲区中指定这些选项。)这样会生成 递归目录列表 ,显示所有层级的子目录。
更常见的情况是,你只希望显示 特定的子目录 。可以使用命令 i (dired-maybe-insert-subdir) 实现:
i- 在缓冲区的下方插入某个子目录的内容。
如果你将此命令用在表示目录的那一行上,它会把该目录的内容插入到同一个 Dired 缓冲区中,并跳转到那里。插入的子目录内容接在 Dired 缓冲区的顶层目录之后,就和 'ls -lR' 的输出格式一样。
如果该子目录的内容已经在缓冲区中存在, i 命令只会直接跳转到那里。
无论哪种情况, i 在跳转前都会设置 Emacs 标记,因此使用 C-u C-SPC 可以回到你在 Dired 缓冲区中之前的位置(参见设置标记)。你也可以使用 '^' 在同一个 Dired 缓冲区中返回上一级目录(参见在 Dired 中访问文件)。
使用 l 命令 (dired-do-redisplay) 可以更新子目录的内容,在子目录标题行上使用 C-u k 可以移除该子目录的列表(参见刷新 Dired 缓冲区)。你还可以隐藏和显示已插入的子目录(参见隐藏子目录)。
32.13. Dired 中的子目录切换
你可以使用 C-u i 在 Dired 缓冲区中带指定 ls 开关插入子目录。对于已经插入的、光标所在的子目录,可使用 C-u l 修改它的 ls 开关。
如果你刷新(revert)缓冲区,Dired 会保留这些开关;但删除子目录后,对应的开关会被遗忘。
使用 dired-undo (见 Dired 标记与标记位对比)来重新插入或删除那些 带显式开关 插入的子目录时,会绕过 Dired 原本 “记住 / 遗忘开关” 的机制:
- 通过
dired-undo删除子目录不会忘记它的开关;之后用i重新插入时,会沿用旧的开关。 - 对普通 Dired 命令(非
dired-undo)删除的子目录,用dired-undo恢复时,最初会用旧开关插入;但一旦刷新缓冲区,就会改用缓冲区默认开关。 - 如果上述行为出现问题,你可以直接用
C-u i或C-u l修正。
Dired 不会记住 R 开关。带 R 开关插入子目录,等价于用剩下的所有开关逐个插入其下所有子目录。例如,对带 R 插入的子目录执行更新或删除,不会递归更新 / 删除它的下级子目录。
缓冲区的默认 ls 开关不会影响那些 显式指定过开关 的子目录。具体来说,像 s 这类修改缓冲区开关的命令,不会影响这类子目录;但会影响没有手动指定开关的普通子目录。
你可以使用命令: M-x dired-reset-subdir-switches RET 让 Dired 忘记所有子目录的开关,并 全部改用缓冲区默认开关 重新列出,同时刷新整个 Dired 缓冲区。
32.14. 遍历子目录
当 Dired 缓冲区列出子目录时,你可以使用页面移动命令 C-x [ 和 C-x ] 按整个目录进行跳转(见 “页面操作”)。
下面这些命令用于在同一个 Dired 缓冲区的目录树中前后、上下移动。它们会跳转到目录标题行—— 也就是在目录内容开头、显示目录名称的那一行。
C-M-n- 跳转到下一个子目录标题行,不考虑层级 (
dired-next-subdir) 。 C-M-p- 跳转到上一个子目录标题行,不考虑层级 (
)dired-prev-subdir) 。 C-M-u- 向上跳转到父目录的标题行 (
dired-tree-up) 。 C-M-d- 在目录树中向下跳转到第一个子目录的标题行 (
dired-tree-down) 。 <- 向上跳转到上一个目录文件行 (
dired-prev-dirline) 。这类行是目录在其父目录中作为文件显示的行。 >- 向下跳转到下一个目录文件行 (
dired-next-dirline) 。 M-G- 提示输入一个目录,并跳转到该目录的目录文件行 (
dired-goto-subdir) 。
32.15. 隐藏子目录
hding隐藏 一个子目录,是指让目录内容不可见,只保留其标题行。
$- 隐藏或显示光标所在的子目录,并将光标移到下一个子目录 (
dired-hide-subdir) 。这是一个切换命令。数字前缀参数可作为重复次数。 M-$- 隐藏当前 Dired 缓冲区中的所有子目录,只保留它们的标题行 (
dired-hide-all) 。如果已有子目录被隐藏,则执行此命令会重新显示所有子目录。你可以用这个命令对极深的目录树做概览,或快速跳转到远处的子目录。
普通的 Dired 命令 永远不会处理隐藏子目录内的文件 。例如,操作标记文件的命令会忽略隐藏目录中的文件,即使它们已被标记。因此,你可以用隐藏功能,临时把某些子目录排除在操作范围之外,而不必清除这些目录内文件上的 Dired 标记。
关于如何插入子目录列表,参见 Dired 中的子目录;关于如何删除子目录列表,参见刷新 Dired 缓冲区。
32.16. 更新 Dired 缓冲区
本节介绍用于刷新 Dired 缓冲区,以反映目录和文件在外部(非 Dired 操作)产生的改动,以及删除 Dired 缓冲区部分内容的命令。
g- 刷新 Dired 缓冲区的全部内容 (
revert-buffer) 。 l- 刷新指定的文件 (
dired-do-redisplay) 。指定文件的方式与文件操作命令相同。 k- 删除缓冲区中指定的文件行 —— 只删行,不删实际文件 (
dired-do-kill-lines) 。 s- 在按字母序和按日期 / 时间序之间切换 (
dired-sort-toggle-or-edit) 。 - (no term)
C-u s switches RET::使用指定的开关作为dired-listing-switches,重新刷新 Dired 缓冲区。
按 g (revert-buffer) 可根据文件和目录的实际变化刷新 Dired 缓冲区内容。此操作会保留所有标记, 已被删除的文件除外 。隐藏的子目录会被刷新,但仍保持隐藏状态。
若只想刷新部分文件,按 l (dired-do-redisplay) 。与 Dired 文件操作命令一样,该命令可作用于接下来的 n 个文件(或前面的 −n 个文件)、所有标记文件,或当前文件。刷新文件意味着重新读取其当前状态,并更新缓冲区中对应的行以反映该状态。
如果在子目录标题行上按 l ,会刷新对应子目录的内容。
如果你用 C-x d 或其他 Dired 命令打开一个已在 Dired 缓冲区中显示的目录,Dired 会切换到该缓冲区,但 不会自动刷新 。如果缓冲区内容已过时,Dired 会显示警告,提示你按 g 刷新。你可以将变量 dired-auto-revert-buffer 设为非 nil,让 Emacs 在重新访问时 自动刷新 每个 Dired 缓冲区。
若要 只从缓冲区删除文件行,而不真正删除文件 ,按 k (dired-do-kill-lines) 。与文件操作命令类似,它可作用于接下来的 n 个文件或所有标记文件;但 不会作用于当前文件 ,避免误按 k 带来不便。
如果你用 k 删除已作为子目录插入到 Dired 缓冲区中的目录行,对应的子目录列表也会被一并移除。在子目录标题行上按 C-u k 同样会从 Dired 缓冲区中删除该子目录内容。
g 命令可以恢复被这样删除的普通文件行,但 无法恢复子目录 ;你必须用 i 重新插入子目录。
Dired 缓冲区中的文件默认按文件名 字母序 排列,也可按 日期 / 时间 排序。Dired 命令 s (dired-sort-toggle-or-edit) 在这两种排序方式间切换。Dired 缓冲区的模式行会提示当前是按名称还是按日期排序。
C-u s switches RET 可让你为 dired-listing-switches 指定新的值。
32.17. Dired 与 find 命令
你可以借助 find 工具来筛选文件,更灵活地选取一组文件并在 Dired 缓冲区中显示。
若要按 文件名通配符 搜索文件,使用 M-x find-name-dired 。它会读取两个参数:目录(directory)与匹配模式(pattern),并选取该目录及其子目录下所有文件名与模式匹配的文件。
筛选出的文件会显示在一个 Dired 缓冲区中,普通 Dired 命令均可正常使用。
若想按 文件内容 而非文件名筛选,使用 M-x find-grep-dired 。该命令读取两个迷你缓冲区参数:目录(directory)与正则表达式(regexp);它会选取该目录及其子目录下所有 包含该正则表达式匹配内容 的文件,通过运行 find 和 grep 实现。另见 M-x grep-find (见在 Emacs 中使用 Grep 搜索)。注意:这里的正则表达式是写给 grep 使用的,而非 Emacs 正则。(另一种显示内容匹配正则表达式文件的方法是 % g regexp 命令,见 Dired 标记与标记位对比。)
这一系列命令中最通用的是 M-x find-dired ,它允许你指定 find 所能判断的任意条件。它接收两个参数:目录(directory)与 find 参数(find-args);它会在指定 direcory目录 中运行 find ,并把 find-args 传给 find 作为筛选条件。使用该命令需要了解 find 的用法。
这些命令生成的列表格式由变量 find-ls-option 控制。它是一对选项:第一个选项指定如何调用 find 来生成文件列表,第二个选项告诉 Dired 如何解析输出。
命令 M-x locate 提供与 locate 程序类似的接口。 M-x locate-with-filter 与之相似,但只保留文件名匹配指定正则表达式的文件。
这类缓冲区的行为与普通 Dired 缓冲区 不完全一样 :文件操作可以执行,但不一定会自动刷新缓冲区。使用 g 刷新缓冲区会 删除所有已插入的子目录 ,并清空所有标记(flag)与选中标记(mark)。
32.18. 编辑 Dired 缓冲区
Wdired 是一种特殊模式,允许你 直接编辑 Dired 缓冲区 来执行文件操作(Wdired 中的 “W” 代表 “可写 writable”)。在 Dired 缓冲区中按 C-x C-q (dired-toggle-read-only) 即可进入 Wdired mode;也可以使用菜单选项「Immediate / Edit File Names」。
在 Wdired 模式下,你可以直接编辑 Dired 缓冲区里显示的文件名来 重命名文件 。所有普通的 Emacs 编辑命令(包括矩形操作、查询替换等)都可以使用。编辑完成后,按 C-c C-c(wdired-finish-edit),会应用你的修改并切回普通 Dired 模式。
除了简单重命名,你还可以直接输入新文件名(绝对路径或相对路径),把文件 移动到另一个目录 。想要标记删除某个文件,直接把它的 整个文件名删掉 即可。想要修改符号链接的目标,编辑链接名旁边显示的链接目标路径即可。
如果你通过编辑文件名创建新的子目录,Wdired 会 自动创建这些新目录 。如果要关闭此行为,把 wdired-create-parent-directories 设为 nil。
缓冲区里的其他内容(如文件大小、修改时间)会被标记为只读,无法编辑。但如果你把 wdired-allow-to-change-permissions 设为 t ,就可以编辑 文件权限 。例如,把 '-rw-r--r--' 改成 '-rw-rw-rw-' ,让文件对所有用户可写。这些改动在按下 C-c C-c 后同样会生效。
32.19. 在 Dired 中查看图片缩略图
Image-Dired 是用于浏览图片文件的工具。它可以将图片以缩略图或原图大小显示,既可在 Emacs 内部查看,也可通过外部查看器打开。这与在 Emacs 缓冲区中直接查看图片的 Image 模式不同(参见查看图片文件)。
进入 Image-Dired 的方法:在 Dired 缓冲区中,像平常一样用 m 标记你想要查看的图片文件,然后按 C-t d (image-dired-display-thumbs) 。这会创建并切换到对应这些标记文件的 Image-Dired 缓冲区。
你也可以直接输入 M-x image-dired 进入 Image-Dired。该命令会提示输入一个目录,指定一个存放图片文件的目录即可。它会为该目录下所有图片生成缩略图,并在缩略图缓冲区中全部显示。缩略图在后台生成,生成完毕后自动加载。
Image-Dired 支持三种缩略图生成与存储方式,由选项 image-dired-thumbnail-storage 控制:
image-dired- 默认方式。将缩略图以 JPEG 格式统一存放在变量
image-dired-dir指定的目录中。缩略图文件名由变量image-dired-thumb-naming决定。 standardstandard-largestandard-x-largestandard-xx-large- 遵循缩略图管理标准(Thumbnail Managing Standard)。将缩略图以 PNG 格式存放在环境变量
XDG_CACHE_HOME(默认为~/.cache)下的thumbnails子目录中。 per-directory- 在每个图片所在目录下的
.image-dired子目录中,以 JPEG 格式存放该目录图片的缩略图。
你可以通过自定义变量 image-dired-thumb-size 控制缩略图尺寸,默认为 128 像素。该变量只对上面第一种和第三种存储方式生效;标准缩略图方式使用固定尺寸: standard 为 128 像素, standard-large 为 256 像素等。如果在已有缩略图后修改尺寸,需要删除旧缩略图文件,新尺寸才会生效。
在缩略图缓冲区中:
- 按
RET(image-dired-display-this) :在另一个窗口显示该图片。 - 使用 Emacs 标准移动键或方向键浏览缩略图。
- 按
SPC(image-dired-display-next) :显示下一张图片。 - 按
DEL(image-dired-display-previous) :显示上一张图片。
按 C-RET (image-dired-thumbnail-display-external) :用外部查看器打开图片。你需要先配置 image-dired-external-viewer 。
你也可以通过 Image-Dired 删除图片:
- 按
d(image-dired-flag-thumb-original-file) :在 Dired 缓冲区中标记该图片待删除。 - 按
C-d(image-dired-delete-char) :仅从缩略图缓冲区移除该缩略图, 不标记删除原文件 。
你还可以在 Dired 中直接进行 “inline内嵌式” 操作:
- 按
C-t C-t:在选中的图片文件名前显示缩略图 (image-dired-dired-toggle-marked-thumbs) 。 - 按
C-t i:在 Emacs 中打开光标下的图片。 - 按
C-t x:用外部查看器打开光标下的图片。
更高级的功能包括 image tags图片标签 ,这是用于分类图片的元数据。标签存放在由 image-dired-tags-db-file 指定的纯文本文件中。
- 给图片打标签:在 Dired 中标记文件(也可在缩略图缓冲区按
m标记),然后按C-t t(image-dired-tag-files) ,在小缓冲区输入标签名。 - 标记带有某标签的文件:按
C-t f(image-dired-mark-tagged-files) 。标记后可用C-t d查看这些图片。
你也可以直接在缩略图缓冲区:
- 按
t t:给当前文件打标签。 - 按
t r:移除标签。
每个文件还有一个特殊的 “注释(comment)” 标签(用法与普通标签略有不同),用于输入图片说明。
- 在缩略图缓冲区按
c:为图片添加注释。 - 在 Dired 中按
C-t c:为文件添加注释 (image-dired-dired-comment-files) 。 - 按
C-t e:打开缓冲区编辑注释与标签 (image-dired-dired-edit-comment-and-tags) 。
如果安装了 exiftool ,可以在图片缩略图上执行 image-dired-thumbnail-set-image-description 命令,设置图片的 EXIF 'ImageDescription' 字段。
如果 image-dired-thumb-visible-marks 为非 nil(默认开启),在 Dired 中标记的文件,在 Image-Dired 中也会显示为已标记。
Image-Dired 还提供简单的图片处理功能。在缩略图缓冲区中:
- 按
L:将原图逆时针旋转 90 度。 - 按
R:将原图顺时针旋转 90 度。该旋转为无损旋转,需要先安装外部工具jpegtran。
32.20. Dired 的其他功能
默认情况下,Dired 会在目录列表的第一行、目录名之后,显示该目录所在磁盘的可用空间。你可以通过自定义变量 dired-free-space 控制这一显示:
- 默认值
first:在目录名同一行显示可用空间。 - 设为
separate:在目录名下 单独一行 显示磁盘空间,同时包含当前目录文件占用空间与可用空间。 - 设为
nil: 完全不显示 磁盘空间信息。
命令 + (dired-create-directory) 会读取目录名并创建该目录。若目录已存在,则报错。
命令 (dired-create-empty-file) 会读取文件名并创建空文件。若文件已存在,则报错。
命令 M-s a C-s (dired-do-isearch) 对标记的文件启动多文件增量搜索。如果在一个文件末尾搜索失败,按 C-s 会跳到下一个标记文件并继续搜索;在最后一个标记文件末尾时,搜索会回到第一个标记文件循环。命令 M-s a M-C-s (dired-do-isearch-regexp) 功能相同,只是使用正则表达式搜索。有关搜索重复的说明,参见 “重复增量搜索”。
命令 w (dired-copy-filename-as-kill) 将已标记(或接下来 n 个)文件的名称放入剪切环(kill ring),效果如同用 C-w 剪切。文件名之间用空格分隔。
- 带零前缀参数时:使用每个标记文件的 绝对路径 。
- 仅用
C-u作前缀时:使用相对于当前 Dired 缓冲区默认目录的 相对路径 (若在子目录中仍可能包含斜杠)。 - 特殊情况:若光标位于目录标题行,
w会给出该目录的绝对路径,此时忽略任何前缀参数与已标记文件。
该命令的主要用途是方便你将文件名 粘贴(yank) 到其他 Emacs 命令的参数中。它同时会在回显区显示已加入剪切环的内容,因此也可用于查看当前标记文件列表。
如果文件列表中有 HTML 文件,用浏览器查看会很方便。命令 W (browse-url-of-dired-file) 会使用系统配置的默认浏览器打开该文件。
命令 (dired-hide-details-mode) 用于切换是否在当前 Dired 缓冲区中显示文件详情(如文件所有者、权限等)。默认同时隐藏符号链接目标,以及除标题行与文件 / 目录列表外的其他行。如需修改,可分别自定义选项: dired-hide-details-hide-symlink-targets 、 dired-hide-details-hide-information-lines
如果你访问的目录受版本控制(见VC)管理,普通的 VC 对比与日志命令可直接作用于选中文件。
命令 M-x dired-compare-directories 用于对比当前 Dired 目录与另一个目录。它会标记出两个目录中存在差异的所有文件,并在所有显示这些文件的 Dired 缓冲区中加上标记(当然也包括当前缓冲区)。
默认对比方式(直接按 RET ): 只对比文件名 —— 文件不存在于另一目录即视为不同。你可以输入一个 Lisp 表达式来指定更严格的对比条件,可用变量包括:
- size1、size2:文件大小
- mtime1、mtime2:最后修改时间(浮点数秒数)
- fa1、fa2:文件属性列表(由 file-attributes 返回)
该表达式会对每一对同名文件求值,结果非 nil 即视为文件不同。
示例: M-x dired-compare-directories RET (> mtime1 mtime2) RET 会标记:
- 当前目录中比另一目录 更新 的文件
- 另一目录中比当前目录更旧的文件
- 以及两边目录中 不存在对应文件 的文件(始终标记)
在 X Window 系统下,Emacs 支持 拖放协议 。你可以从其他程序拖曳文件对象到 Dired 缓冲区,实现移动、复制或创建链接,具体行为由源程序决定。
同时也支持从 Dired 缓冲区向外拖曳文件:开启用户选项 dired-mouse-drag-files 后,可用鼠标将文件拖到其他程序。
- 设为
link:让文件管理器等程序创建符号链接 - 设为
move:执行移动操作 - 设为其他非 nil 值:执行打开或复制操作。拖放时按下的键盘修饰键也可控制目标程序对文件的操作。
33. 日历与日记
Emacs 提供桌面日历功能,并可记录计划或已发生的事件日记。它还能管理你的日程安排,并统计你在特定项目上花费的时间。
输入 M-x calendar 即可进入日历。该命令会显示以当前月份为中心的三个月日历,光标定位在当天日期。若带数字参数,例如 C-u M-x calendar ,会提示你指定月份和年份,作为三个月日历的中心月份。日历使用独立缓冲区,其主模式为日历模式(Calendar mode)。
在日历中点击 mouse-3 (鼠标右键)可弹出针对特定日期的操作菜单; mouse-2 (鼠标中键)弹出与具体日期无关的常用日历功能菜单。输入 q 退出日历。
33.1. 日历中的移动
Calendar mode日历模式 提供多种命令,可按天、周、月、年等逻辑时间单位在日历中移动。如果你移动到了最初显示的三个月范围之外,日历会自动滚动,让选中的日期可见。移动到某一日期后,你可以查看该日的节假日或日记条目,或将其转换为其他历法;长时间跨度的移动也可用于快速翻阅日历。
33.1.1. 按标准时间长度移动
日历缓冲区中的移动命令,与文本移动命令的设计思路类似。你可以按天、周、月、年向前或向后跳转。
C-f- 光标向前移动一天 (
calendar-forward-day) 。光标向右移动1天。方向与现代含义有区别,见滚动操作 C-b- 光标向后移动一天 (
calendar-backward-day) 。 C-n- 光标向前移动一周 (
calendar-forward-week) 。 C-p- 光标向后移动一周 (
calendar-backward-week) 。 M-}- 光标向前移动一月 (
calendar-forward-month) 。 M-{- 光标向后移动一月 (
calendar-backward-month) 。 C-x ]- 光标向前移动一年 (
calendar-forward-year) 。 C-x [- 光标向后移动一年 (
calendar-backward-year) 。
按天和按周移动的命令,对应 Emacs 平常按字符、按行移动的逻辑。就像普通模式下 C-n 会跳到下一行同一列,在日历模式中它绑定到 calendar-forward-week ,会跳到下一周的同一星期几; C-p (calendar-backward-week) 则跳至上一周的同一星期几。 C-f (calendar-forward-day) 和 C-b (calendar-backward-day) 按天前后移动。
方向键功能与 C-f 、 C-b 、 C-n 、 C-p 等价,和其他模式一致。
按月、按年移动的命令用法与按周类似,只是跨度更大。月命令 M-} (calendar-forward-month) 和 M-{ (calendar-backward-month) 整月前后跳转;年命令 C-x ] (calendar-forward-year) 和 C-x [ (calendar-backward-year) 整年前后跳转。
方便记忆的方式:把 月 类比成文本里的 段落 ,把 年 类比成 页面 。但日历命令与普通文本命令并不完全相同:普通段落命令会跳到段落开头或结尾,而日历中的月、年命令会保持当月、当年的同一日期,整体移动一个月或一整年。
所有这些命令都接受数字前缀参数作为重复次数。为方便使用,在日历模式下,即使不按 Meta 键,直接按数字键和减号也能指定数字参数。例如: 100 C-f 会从当前位置向后移动 100 天
33.1.2. 星期、月份、年份的开始或结尾
一周(或一月、一年)不只是一段天数;我们通常认为周(月、年)是从特定日期开始的。因此日历模式提供了跳至周、月、年开头或结尾的命令:
C-a- 光标移至本周开头 (
calendar-beginning-of-week) 。 C-e- 光标移至本周结尾 (
calendar-end-of-week) 。 M-a- 光标移至本月开头 (
calendar-beginning-of-month) 。 M-e- 光标移至本月结尾 (
calendar-end-of-month) 。 M-<- 光标移至本年开头 (
calendar-beginning-of-year) 。 M->- 光标移至本年结尾 (
calendar-end-of-year) 。
这些命令同样接受数字前缀参数作为重复次数,参数表示向前或向后移动多少周、月、年。
默认情况下,一周从星期日开始。如果想改为从星期一开始,可将变量 calendar-week-start-day 设置为 1。若要修改哪些日期标题会高亮显示为周末,可设置变量 calendar-weekend-days 。
33.1.3. 指定日期跳转
日历模式提供了多种命令,可跳转到以不同方式指定的具体日期。
g d- 跳转到指定日期 (
calendar-goto-date) 。 g D- 跳转到年内指定天数 (
calendar-goto-day-of-year) 。 g w- 跳转到年内指定周(ISO 周) (
calendar-iso-goto-week) 。 o- 以指定月份为中心重新显示三个月日历 (
calendar-other-month) 。 .- 跳转到今天的日期 (
calendar-goto-today) 。
g d (calendar-goto-date) 会提示输入年、月、日,然后跳转到该日期。由于日历包含当前纪元的所有日期,年份必须完整输入;例如输入 2010,而不是 10。
g D (calendar-goto-day-of-year) 提示输入年份和天数,然后跳转到对应日期。负数天数表示从年末向前倒数。
g w (calendar-iso-goto-week) 提示输入年份和周数,跳转到对应周。
o (calendar-other-month) 提示输入月份和年份,然后以该月为中心显示三个月日历。
使用 . (calendar-goto-today) 可以快速回到今天。
33.2. 日历滚动
当你移动超出日历当前可见区域时,日历会 自动滚动 。你也可以手动滚动。可以把日历窗口想象成一长条印有月份的纸带,滚动日历就是水平移动这条纸带,让新的月份显示在窗口中。
>- 向前滚动一个月 (
calendar-scroll-left) 。 <- 向后滚动一个月 (
calendar-scroll-right) 。 C-vPageDownnext- 向前滚动三个月 (
calendar-scroll-left-three-months) 。 M-vPageUpprior- 向后滚动三个月 (
calendar-scroll-right-three-months) 。
最基础的日历滚动命令每次滚动 一个月 ,这意味着滚动前后的显示内容会有 两个月重叠 。 > (calendar-scroll-left) 让时间上更晚的月份显示出来; < (calendar-scroll-right) 让时间上更早的月份显示出来。
命令 C-v (calendar-scroll-left-three-months) 和 M-v (calendar-scroll-right-three-months) 一次滚动一整屏(三个月),用法和它们在普通文本中的含义类似。 C-v 显示更晚的日期, M-v 显示更早的日期。
这些命令都接受数字前缀参数作为滚动次数。特别地, C-u 会把下一条命令的执行次数乘以 4,所以 C-u C-v 会向前滚动一整年, C-u M-v 会向后滚动一整年。
功能键 PageDown (next) 和 PageUp (prior) 分别等价于 C-v 和 M-v ,与其他模式一致。
33.3. 天数计算
M-=- 显示当前区域内的天数 (
calendar-count-days-region) 。
要统计一段日期范围内的天数,请使用 C-SPC 在一个日期上设置标记,将光标移动到另一个日期,然后输入 M-= (calendar-count-days-region) 。显示的天数是包含首尾的,即同时计入标记所在日期和光标所在日期。
33.4. 日历杂项命令
p d- 显示年内第几天 (
calendar-print-day-of-year) 。 C-c C-l- 重新生成日历窗口 (
calendar-redraw) 。 SPC- 向上滚动另一个窗口 (
scroll-other-window) 。 DELS-SPC- 向下滚动另一个窗口 (
scroll-other-window-down) 。 q- 退出日历 (
calendar-exit) 。
要查看从年初到当天已过的天数,以及本年剩余天数,可使用命令 p d (calendar-print-day-of-year) 。这两个数值都会显示在回显区:已过天数 包含 当前选中日期,剩余天数 不包含 当前日期。
如果日历窗口内容显示错乱,可输入 C-c C-l (calendar-redraw) 重新绘制。(这种情况只会在你使用了非日历模式的编辑命令时出现。)
在日历模式中,你可以用 SPC 和 DEL 分别向上 / 向下滚动另一个窗口(如果存在)。当你在另一个窗口中查看节假日或日记条目时,这会很方便。
输入 q (calendar-exit) 即可退出日历。该操作会隐藏所有与日历相关的缓冲区,并切换到其他缓冲区。(如果某个框架是专门的日历窗口,退出时会根据变量 calendar-remove-frame-by-deleting 的值决定删除或最小化该框架。)
33.5. 编写日历文件
你可以将日历与日记条目导出为 HTML 和 LaTeX 文件。
HTML 导出
日历 HTML 命令会生成包含日历、节假日和日记条目的 HTML 文件。每个文件对应一个月份,文件名为 yyyy-mm.html 格式,其中 yyyy 是 4 位年份, mm 是 2 位月份。
- 变量
cal-html-directory指定 HTML 文件的默认输出目录。 - 若要隐藏节假日,可自定义
cal-html-holidays。
被 < 和 > 包裹的日记条目会被当作 HTML 标签解析(例如:这是一条带有 <font color="red">红色文字</font> 的日记)。你可以通过 HTML 文件所在目录中的样式表 cal.css 来修改页面整体外观(如颜色、标题样式等)。相关样式设置可参考变量 cal-html-css-default 。
H m- 生成当月日历 (
cal-html-cursor-month) 。 H y- 为一年中每个月生成日历文件,并生成索引页 (
cal-html-cursor-year) 。默认会写入以年份命名的子目录中;若修改此设置,年份之间的部分超链接可能失效。
若变量 cal-html-print-day-number-flag 不为 nil,月历会显示年内第几天。变量 cal-html-year-index-cols 设置年度索引页的列数。
LaTeX 导出
日历 LaTeX 命令会生成可打印为日历的 LaTeX 代码缓冲区。根据所用命令不同,可输出光标所在的日、周、月、年日历。
t m- 生成单月日历 (
cal-tex-cursor-month)。 t M- 生成横向打印的单月日历 (
cal-tex-cursor-month-landscape) 。 t d- 生成单日日历 (
cal-tex-cursor-day) 。 t w 1- 生成带小时的单周单页日历 (
cal-tex-cursor-week) 。 t w 2- 生成带小时的单周双页日历 (
cal-tex-cursor-week2) 。 t w 3- 生成 ISO 风格、无小时的单周日历 (
cal-tex-cursor-week-iso) 。 t w 4- 生成以周一为起始、带小时的单周日历 (
cal-tex-cursor-week-monday) 。 t w W- 生成无小时的单周双页日历 (
cal-tex-cursor-week2-summary) 。 t f w- 生成 Filofax 风格双周概览日历 (
cal-tex-cursor-filofax-2week) 。 t f W- 生成 Filofax 风格单周概览日历 (
cal-tex-cursor-filofax-week) 。 t y- 生成年历 (
cal-tex-cursor-year) 。 t Y- 生成横向打印的年历 (
cal-tex-cursor-year-landscape) 。 t f y- 生成 Filofax 风格年历 (
cal-tex-cursor-filofax-year) 。
部分命令会横向打印日历(横版模式);部分使用 Filofax 纸张尺寸(3.75in × 6.75in)。所有这些命令都接受前缀参数,用于指定要打印的天数、周数、月数或年数(从当前选中日期开始)。
- 若变量
cal-tex-holidays为非 nil(默认 nil ),打印日历会显示calendar-holidays中的节假日。 - 若变量
cal-tex-diary为非 nil(默认为 nil),会同时包含日记条目(仅支持月历、Filofax、ISO 周历)。 - 若变量
cal-tex-rules为非 nil(默认为 nil),在空间足够的样式中会显示带行线的页面。
可查阅各个 cal-tex 函数的文档,了解哪些日历支持哪些功能。
如需,可使用变量 cal-tex-preamble-extra 在生成文档的导言区插入额外 LaTeX 命令。
33.6. 节假日显示
Emacs 日历内置了众多大小节假日并可以显示它们。你可以在默认列表中添加自定义节假日。
mouse-3鼠标右键 –> 'Holidays'h- 显示选中日期的节假日 (
calendar-cursor-holidays) 。 x- 在日历窗口中标记节假日(假日分布情况) (
calendar-mark-holidays) 。 u- 取消日历窗口中的标记 (
calendar-unmark) 。 a- 在另一个窗口列出当前显示三个月内的所有节假日 (
calendar-list-holidays) 。 M-x holidays- 在另一个窗口列出以今天为中心的三个月内的所有节假日。
M-x list-holidays- 在另一个窗口列出指定年份范围内的节假日。
想要查看某一天是否有节假日,在日历窗口中将光标移到该日期,然后使用 h (calendar-cursor-holidays) 命令。或者用 mouse-3 鼠标右键点击该日期,在弹出的菜单中选择 'Holidays' 。两种方式都会显示当天的节假日:内容较短则显示在回显区,较长则在独立窗口中显示。
想要查看当前日历中所有节假日的分布情况,使用 x (calendar-mark-holidays) 命令。该命令会用不同的字体高亮显示节假日日期,可参见 calendar-holiday-marker 。标记不仅作用于当前可见月份,也会作用于后续滚动显示出来的月份。输入 u (calendar-unmark) 可关闭标记并清除当前高亮,同时也会清除日记标记(见 “日记” 章节)。如果变量 calendar-mark-holidays-flag 设为非 nil,创建或更新日历时会自动标记节假日。
想要更详细的信息,可以使用 a (calendar-list-holidays) 命令,它会在独立缓冲区中列出当前三个月内的所有节假日。你可以在日历窗口中用 SPC 和 DEL 分别上下滚动该列表。
命令 M-x holidays 会显示当前月及前后各一个月的节假日列表;即使没有打开日历窗口也能使用。如果变量 calendar-view-holidays-initially-flag 设为非 nil,创建日历时会自动以这种方式显示节假日。如果想以其他月份为中心显示节假日,可以使用 C-u M-x holidays ,它会提示你输入月份和年份。
Emacs 已知的节假日包括 United States(美国节假日),以及 Bahá’í(巴哈伊教)、Chinese(中国节日)、Christian(基督教)、Islamic(伊斯兰教)、Jewish(犹太教)主要节日;同时还包含solstices and equinoxes(冬至、夏至、春分、秋分)等节气。
命令 M-x holiday-list 会列出一段年份范围内的节假日。该函数会提示你输入起始年份和结束年份,并允许你选择查看全部节日或某一类节日。即使没有打开日历窗口,也可以使用该命令。
Emacs 所使用的节假日日期基于现行通用规则,而非严格历史事实。例如,退伍军人纪念日始于 1919 年,但在更早的年份里也会显示。
33.7. 日出日落时间
专用的日历命令可以为你精确到一两分钟内,显示任意日期的日出与日落时间。
mouse-3 Sunrise/sunsetS- 显示所选日期的日出与日落时间 (
calendar-sunrise-sunset) 。 M-x sunrise-sunset- 显示今天的日出与日落时间。
C-u M-x sunrise-sunset- 显示指定日期的日出与日落时间。
M-x calendar-sunrise-sunset-month- 显示所选月份每一天的日出与日落时间。
在日历中,要在回显区显示某地的当地日出与日落时间,只需将光标移到目标日期,然后按 S (calendar-sunrise-sunset) 。你也可以在日期上单击鼠标 mouse-3 ,然后在弹出的菜单中选择「Sunrise/sunset」。
在日历之外,可使用命令 M-x sunrise-sunset 查看今天或指定日期的信息。若要指定非今天的日期,使用 C-u M-x sunrise-sunset ,它会提示你输入年、月、日。
使用 C-u C-u M-x sunrise-sunset ,可以显示任意地点、任意日期的日出日落时间。该命令会依次询问你:经度、纬度、与世界协调时间(UTC)的分钟差、日期,然后显示该地在该日的日出日落时间。
由于日出日落时间取决于地球上的具体位置,使用这些命令前,你需要先告诉 Emacs 你的纬度、经度和位置名称。以下是设置示例:
(setq calendar-latitude 40.1) (setq calendar-longitude -88.2) (setq calendar-location-name "Urbana, IL")
calendar-latitude 和 calendar-longitude 的值请使用一位小数。
时区也会影响当地的日出日落时间。Emacs 通常从操作系统获取时区信息,但如果这些值不符合你的需求(或操作系统未提供),你需要自行设置。示例:
(setq calendar-time-zone -360) (setq calendar-standard-time-zone-name "CST") (setq calendar-daylight-time-zone-name "CDT")
calendar-time-zone 的值是你当地标准时间与世界协调时间(又称 “格林威治时间”)的分钟差值。 calendar-standard-time-zone-name 和 calendar-daylight-time-zone-name 是你所在时区使用的标准时与夏令时缩写。Emacs 会根据夏令时对日出日落时间进行修正。夏令时的判定方式见「夏令时」一节。
如果你希望显示数字形式的时区(如 "+0100"),而非符号缩写(如 "CET"),可将变量 calendar-time-zone-style 设为 numeric 。
作为用户,你可以在 .emacs 配置文件中设置常用地理位置的日历相关变量,方便使用。如果你是系统管理员,也可以在 default.el 中为所有用户统一设置这些变量。参见「Emacs 初始化文件」。
33.8. 月相显示
本节介绍的日历命令可显示月相(new moon新月、first quarter上弦月、full moon满月、last quarter下弦月)的日期与时间。该功能在调试与月相相关的问题时很有用。
M- 显示当前日历所展示的三个月期间内所有月相的日期与时间 (
calendar-lunar-phases) 。 M-x lunar-phases- 显示以今天为中心、前后共三个月的月相日期与时间。
在日历界面内,使用命令 M (calendar-lunar-phases) 可在独立缓冲区中显示当前三个月范围内的月相。列出的日期与时间精度可达几分钟以内。
在日历界面外,使用命令 M-x lunar-phases 可显示本月、上月与下月的月相列表。如需查看其他月份,可使用 C-u M-x lunar-phases ,该命令会提示你输入月份与年份。
月相对应的日期与时间均以本地时间给出(必要时会做夏令时修正)。参见上一节的说明(日出与日落时间)。
33.9. 与其他日历系统的转换
Emacs 显示的日历始终是公历( Gregorian calendar格里高利历 ),也被称作 New Style calendar新历 ,是当今世界大部分地区所使用的历法。但这种历法在 16 世纪之前并不存在,18 世纪之前也未被广泛使用;直到 20 世纪初,它才彻底取代儒略历并获得普遍认可。
Emacs 日历可以显示公元 1 年 1 月以来的任意月份,但 显示的始终是公历 ,即便对于公历尚未出现的历史日期也是如此。
虽然 Emacs 不能直接显示其他历法,但可以在公历与 其他多种历法之间进行日期转换 。
33.9.1. 支持的日历系统
ISO 商业历常用于商业领域。
Julian calendar儒略历 以尤利乌斯・恺撒命名,是欧洲中世纪通用的历法,许多国家一直使用到 19 世纪。
天文学家使用一种简单的纪日方式:从儒略历公元前 4713 年 1 月 1 日星期一中午起累计经过的天数。这个累计天数称为儒略日数或天文日数。
Hebrew calendar希伯来历 是犹太教传统使用的历法。Emacs 日历使用希伯来历推算犹太教节日日期。希伯来历的日期以日落为起止点。
Islamic calendar伊斯兰历 在许多以伊斯兰教为主的国家使用。Emacs 用它推算伊斯兰教节日。伊斯兰世界对该历法并无完全统一的标准;Emacs 使用的是广为接受的版本,但伊斯兰教节日的确切日期通常由宗教权威宣告,而非仅依靠计算,因此实际庆祝日期可能与 Emacs 计算结果略有出入。伊斯兰历的日期以日落为起止点。
French Revolutionary calendar法国共和历 是雅各宾派在 1789 年法国大革命后制定的历法,旨在以更世俗、贴近自然的方式表达年度周期,并推行 10 天一旬的制度,类似公制度量衡的合理化改革。法国政府于 1805 年底正式废除该历法。
中美洲的玛雅人使用三套独立且相互重叠的历法: long count长计历 、 tzolkin卓尔金历 和 haab哈布历 。Emacs 支持这三种历法。玛雅历与现行公历的确切对应关系在专家中仍有争议;Emacs 在计算中采用古德曼–马丁内斯–汤普森对应法。
科普特历基于古埃及 solar calendar太阳历 ,一年分为 12 个 30 天的月份,外加额外 5 天。每四年在这一额外时段中增加 1 天闰日,变为 6 天。埃塞俄比亚历结构与之相同,但年份与月份名称不同。
Persians波斯历 是基于莪默・伽亚谟设计的太阳历。一年 12 个月:前 6 个月各 31 天,中间 5 个月各 30 天,最后 1 个月平年 29 天、闰年 30 天。闰年以复杂的规律每 4 年或 5 年出现一次。此处实现的是比拉什克算术波斯历,以 2820 年为周期,与基于天文现象的天文波斯历不同。截至本文撰写时,预计首次差异将出现在 2025 年 3 月 20 日,届时伊朗官方将使用何种历法尚不清楚。
Chinese calendar中国农历 是一套复杂的阴阳合历,以阴历月配合阳历年。年份以 60 年为一周期;平年 12 个月,闰年 13 个月;每月 29 天或 30 天。年、普通月份和日期由 ten celestial stems十天干 与 twelve terrestrial branches十二地支 组合命名,共 60 个名称,以 60 为周期循环。
Bahá’í calendar巴哈伊历 以太阳年为基础,每年 19 个月,每月 19 天。剩余 4 天置于第 18 个月与第 19 个月之间,作为闰日。
33.9.2. 转换到其他日历系统
以下命令用于以其他各种历法显示选中的日期(光标所在日期):
mouse-3–> Other calendarsp o- 以其他多种历法显示选中的日期 (
calendar-print-other-dates) 。 p c- 显示选中日期对应的 ISO commercial calendar ISO 商业历 日期 (
calendar-iso-print-date) 。 p j- 显示选中日期的 Julian儒略历 日期 (
calendar-julian-print-date) 。 p a- 显示选中日期的 astronomical (Julian) 天文(儒略) 日数 (
calendar-astro-print-day-number) 。 p h- 显示选中日期的 Hebrew希伯来历 日期 (
calendar-hebrew-print-date) 。 p i- 显示选中日期的 Islamic伊斯兰历 日期 (
calendar-islamic-print-date) 。 p f- 显示选中日期的 French Revolutionary法国共和历 日期 (
calendar-french-print-date) 。 p b- 显示选中日期的 Bahá’í巴哈伊历 日期 (
calendar-bahai-print-date) 。 p C- 显示选中日期的 Chinese中国农历 日期 (
calendar-chinese-print-date) 。 p k- 显示选中日期的 Coptic科普特历 日期 (
calendar-coptic-print-date) 。 p e- 显示选中日期的 Ethiopic埃塞俄比亚历 日期 (
calendar-ethiopic-print-date) 。 p p- 显示选中日期的 Persian波斯历 日期 (
calendar-persian-print-date) 。 p m- 显示选中日期的 Mayan玛雅历 日期 (
calendar-mayan-print-date) 。
使用方法:将光标移动到要转换的日期,然后输入上表中以 p 开头的对应命令。前缀 p 是 “print(打印)” 的助记符,Emacs 会在回显区 “打印” 出对应日期。 p o (calendar-print-other-dates) 会以 Emacs 支持的所有历法格式显示该日期。
你也可以用鼠标右键( mouse-3 )点击日期,然后在菜单中选择 'Other calendars' 。它会以菜单形式列出 Emacs 支持的所有历法下的对应日期。(从该菜单中选择某一项并不会执行其他操作,仅用于显示。)
33.9.3. 从其他日历系统转换
你可以使用 Emacs 支持的其他历法来指定要跳转到的日期。本节介绍玛雅历之外的历法跳转命令;玛雅历相关用法见下一节。
g c- 跳转到 ISO 商业历指定的日期 (
calendar-iso-goto-date) 。 g w- 跳转到 ISO 商业历指定的周 (
calendar-iso-goto-week) 。 g j- 跳转到 Julian儒略历 指定的日期 (
calendar-julian-goto-date) 。 g a- 根据天文(儒略)日数跳转到对应日期 (
calendar-astro-goto-day-number) 。 g b- 跳转到 Bahá’í巴哈伊历 指定的日期 (
calendar-bahai-goto-date) 。 g h- 跳转到 Hebrew希伯来历 指定的日期 (
calendar-hebrew-goto-date) 。 g i- 跳转到 Islamic伊斯兰历 指定的日期 (
calendar-islamic-goto-date) 。 g f- 跳转到 French Revolutionary法国共和历 指定的日期 (
calendar-french-goto-date) 。 g C- 跳转到 Chinese中国农历 指定的日期 (
calendar-chinese-goto-date) 。 g p- 跳转到 Persian波斯历 指定的日期 (
calendar-persian-goto-date) 。 g k- 跳转到 Coptic科普特历 指定的日期 (
calendar-coptic-goto-date) 。 g e- 跳转到 Ethiopic埃塞俄比亚历 指定的日期 (
calendar-ethiopic-goto-date) 。
这些命令会提示你输入其他历法的日期,然后将光标跳转到公历对应的日期,并在回显区显示该历法日期。当 Emacs 提示你输入月份名称时,会使用 严格补全 (参见补全退出),因此你不必担心希伯来语、伊斯兰语或法语名称的拼写问题。
关于希伯来历的一个常见问题是计算忌日周年,称为雅尔茨伊特(yahrzeit)。Emacs 日历内置了相关计算功能:若已在日历中,命令 M-x calendar-hebrew-list-yahrzeits 会提示你输入年份范围,然后列出光标所在日期在这些年份里对应的雅尔茨伊特日期。
若不在日历中,该命令会先提示你输入忌日日期和年份范围,再显示对应的雅尔茨伊特日期列表。
33.10. 日记
Emacs 日记可以配合日历, 逐日记录日程安排或其他事件 。要使用日记功能,必须先创建一个日记文件,写明事件及其日期。之后 Emacs 就能自动筛选并显示 今天、近期或任意指定日期 的事件。
虽然你通常会先手动创建日记,但 Emacs 还提供了一系列命令,用于查看、添加和修改日记条目。
33.10.1. 日记文件
diary file日记文件 用于记录与特定日期相关的事件。日记文件名由变量 diary-file 指定,默认为 ~/.emacs.d/diary ;为兼容旧版本,若存在 ~/diary ,Emacs 会优先使用它。
日记文件中的每一项记录描述一个事件,由一行或多行组成。每条记录 必须以日期格式顶格开头 ,其余内容为事件描述。如果记录超过一行,从第二行开始必须以 空白字符 开头,表示续行。不以有效日期开头、也不是续行的行会被忽略。示例:
12/22/2015 结婚二十周年纪念日!
10/22 Ruth 的生日。
* 21, *: 发薪日
Tuesday--每周上午10点与研究生例会
Supowit, Shen, Bitner, 和 Kapoor 参加。
1/13/89 黑色星期五!
thu 4pm 与 Lloyd 打壁球。
mar 16 爸爸的生日
April 15, 2016 报税截止。
* 15 考勤表截止。
本例用额外空格对齐大部分条目的事件描述,这种格式纯属个人习惯。
你也可以使用这种格式:日记条目第一行 只写日期或星期 (后面无空格或标点),例如:
02/11/2012
Bill B. 今日访问普林斯顿
2pm 认知研究委员会会议
2:30–5:30 Liz 在劳伦斯维尔
4:00pm 牙医预约
7:30pm 在 George 家晚餐
8:00–10:00pm 音乐会
如果使用简易日记显示模式,这种条目会隐藏开头的日期行,只显示续行内容。该风格在只查看单日记录时更整洁,但同时显示多天记录时容易混淆。
33.10.2. 显示日记
创建日记文件后,你可以通过日历查看日记内容,也可以在非日历模式下查看当天事件。以下按键绑定均针对日历缓冲区。
mouse-3 Diaryd- 显示所选日期的所有日记条目 (
diary-view-entries) 。 s- 显示整个日记文件 (
diary-show-all-entries) 。 m- 标记所有包含日记条目的可见日期 (
diary-mark-entries) 。 u- 取消日历窗口中的标记 (
calendar-unmark) 。 M-x diary-print-entries- 将当前显示的日记内容打印输出。
M-x diary- 显示今天日期的所有日记条目。
M-x diary-mail-entries- 向自己发送即将到来的日记条目邮件提醒。
使用 d (diary-view-entries) 显示日记条目时,会在独立缓冲区中展示日历中所选日期的日记内容。新缓冲区的模式行会显示日记条目的日期。节假日会显示在缓冲区或模式行中,具体取决于你选择的显示方式(见日记显示)。如果为 d 指定数字前缀参数,则会显示连续对应天数的所有日记条目。例如, 2 d 会显示所选日期及次日的所有条目。
另一种显示某日期日记条目的方法是:在日期上单击鼠标右键 mouse-3 ,然后在弹出菜单中选择 'Diary entries' 。如果变量 calendar-view-diary-initially-flag 设为非 nil,则创建日历时会自动列出当前日期的日记条目(前提是当前日期可见)。
若要更直观地查看日记中涉及哪些日期,可使用 m (diary-mark-entries) 命令。该命令会用不同的外观标记出有日记条目的日期。详见 diary-entry-marker。
该命令不仅作用于当前可见的月份,也会作用于滚动后后续显示的月份。输入 u (calendar-unmark) 可关闭标记并清除当前标记,同时也会取消节假日标记(见节假日)。如果变量 calendar-mark-diary-entries-flag 设为非 nil,则创建或刷新日历时会自动标记日记日期。
若要让某条日记条目 不在日历上标记日期 ,可在条目开头、日期之前插入变量 diary-nonmarking-symbol 指定的字符串(默认为 '&' )。这对日记缓冲区中的条目显示没有影响,只影响日历中的日期标记。无需标记的条目对于通用条目很有用,否则会标记许多不同日期。
若要查看完整日记文件而非部分条目,可使用 s (diary-show-all-entries) 命令。
命令 M-x diary 可独立于日历显示当前日期的日记条目,还可选择性显示未来几天的内容;变量 diary-number-of-entries 用于指定包含的天数。详见 diary-number-of-entries。
如果在 .emacs 文件中加入 (diary),启动 Emacs 时会自动显示包含当日日记条目的窗口。
部分用户希望通过邮件接收日记事件通知。使用 M-x diary-mail-entries 可向自己发送此类邮件。前缀参数指定检查的天数(从今天开始);否则由变量 diary-mail-days 指定天数。
33.10.3. 日期格式
下面是一些日记条目示例,展示不同的日期写法。示例均使用美式日期格式(月/日/年),但日历模式也支持欧式格式(日/月/年)和 ISO 格式(年/月/日)。
4/20/12 切换到新的制表系统 apr. 25 开始统计年度结果 4/30 四月结果提交截止 */25 月度周期结束 Friday 备份文件再离开
第一条只在 2012 年 4 月 20 日出现一次。第二条和第三条每年指定日期生效。第四条月份用通配符(星号 '*'),表示每月 25 号生效。最后一条每周星期五生效。
你可以只用数字表示日期,格式为 'month/day' 或 'month/day/year' ,后面必须跟非数字字符。月份和日期可以是 1~2 位数字,年份可省略前两位,如 '11/12/2012' 或 '11/12/12' 。
日期也可以写成:'monthname day' 或 'monthname day, year' 月份名可全写或缩写(可带点号)。月份和星期的缩写方式可通过变量 calendar-abbrev-length 、 calendar-month-abbrev-array 、 calendar-day-abbrev-array 设置。默认使用前 3 个字母,大小写不敏感。
日期可以是 通用日期 (部分不指定),表示匹配所有符合条件的日期。不写年份即表示每一年都生效。月份、日期、年份都可以用 '*' 表示任意。例如: '3/*/*' 或 'march *' 表示任意年份 3 月的每一天。
如果你更喜欢欧式日期(日在前,月在后)或 ISO 格式(年 / 月 / 日),可以在日历时执行 M-x calendar-set-date-style ,或自定义变量 calendar-date-style 。这会影响日记日期的解析、显示以及部分命令的参数顺序。
你可以直接用星期几作为通用日期,表示每周这一天都生效。星期名可全写或缩写,大小写不敏感。
33.10.4. 添加日记条目的命令
在日历界面中,可以使用多个命令来创建日记条目。这里列出基础命令;更高级的命令见下一节(特殊日记条目)。日记条目也可以基于非公历创建,参见使用非公历的日记条目。
i d- 为选中的日期添加一条日记条目 (
diary-insert-entry) 。 i w- 为选中的星期几添加每周重复的日记条目 (
diary-insert-weekly-entry) 。 i m- 为选中的日期添加每月重复的日记条目 (
diary-insert-monthly-entry) 。 i y- 为选中的日期添加每年重复的日记条目 (
diary-insert-yearly-entry) 。
你可以在日历窗口中选中某个日期,然后按 i d (diary-insert-entry) 为该日期创建日记条目。此命令会在另一个窗口中打开日记文件末尾并插入日期,之后你可以输入条目剩余内容。
如果想创建 每周固定星期几 重复的条目,选中该星期的任意一天,按 i w (diary-insert-weekly-entry) 。它会将该星期几作为通用日期插入,你再补充后续内容。每月重复的条目用法类似:选中日期,使用 i m (diary-insert-monthly-entry) ,然后输入条目内容。同样,你可以用 i y (diary-insert-yearly-entry) 插入每年重复的日记条目。
以上所有命令默认创建 会在日历上标记 的日记条目。如果要创建不标记的条目,给命令加上前缀参数。例如: C-u i w 创建一个不标记的每周重复日记条目。
修改日记文件后,退出 Emacs 前请记得保存。使用上述插入命令后保存日记文件,会自动更新日历窗口里的日记标记(如适用)。你也可以随时使用命令 calendar-redraw 强制刷新。
33.10.5. 特殊日记条目
除了基于公历日期的条目,日记文件还可以包含 Lisp 表达式(sexp)条目,用于记录周年纪念日等周期性事件。这类条目以 '%%' 开头,后接一对括号包裹的 Lisp 表达式,由 Emacs 求值来判断该条目适用于哪些日期。
日历模式提供了插入常用表达式条目的命令:
i a- 为选中日期添加周年纪念日日记条目 (
diary-insert-anniversary-entry) 。 i b- 为当前选中区域添加连续时间段日记条目 (
diary-insert-block-entry) 。 i c- 从指定日期开始添加循环周期日记条目 (
diary-insert-cyclic-entry) 。
如果要为某个特定日期创建纪念日日记条目,将光标移到该日期并使用 i a (diary-insert-anniversary-entry) 命令。该命令会在另一个窗口显示日记文件末尾并插入纪念日描述,你只需补充剩余内容即可。条目格式示例:
%%(diary-anniversary 10 31 1988) Arthur's birthday
该条目在 1988 年之后的每年 10 月 31 日生效;'10 31 1988' 为起始日期。(若使用欧洲或 ISO 日历格式,月、日、年的输入顺序会不同。)要求填写起始年份,是为了让高级日记功能可以计算已过去的周年数。
连续日期块日记条目 适用于一段连续日期。示例:2012 年 6 月 24 日至 2012 年 7 月 10 日全部生效:
%%(diary-block 6 24 2012 7 10 2012) Vacation
'6 24 12' 为起始日期, '7 10 12' 为结束日期。(若使用欧洲或 ISO 日历格式,月、日、年顺序不同。)
插入块条目时,将光标和标记分别设在起止日期上,然后按 i b (diary-insert-block-entry)。命令会在另一窗口打开日记文件末尾并插入块描述,你再补充完整条目即可。
周期性日记条目 按固定天数重复。选中起始日期后使用 i c (diary-insert-cyclic-entry) ,命令会提示输入间隔天数,生成类似如下条目:
%%(diary-cyclic 50 3 1 2012) Renew medication
该条目从 2012 年 3 月 1 日开始,每 50 天重复一次。(欧洲 / ISO 日历格式下日期输入顺序不同。)
以上三条命令默认生成带标记的日记条目。若要插入不带标记的条目,给命令加上前缀参数即可。例如: C-u i a 插入无标记的纪念日日记条目。
在日历中标记表达式日记条目可能比较耗时,因为日历窗口中每个可见日期都要单独判断。因此建议尽可能将表达式条目设为无标记(用 '&')。
另一种高级表达式条目是浮动日记条目,它通过日、周、月的偏移量来指定定期事件,功能类似于 cron 工具的定时任务。下面是一条无标记、匹配每年 11 月第四个星期四的浮动日记条目:
&%%(diary-float 11 4 4) American Thanksgiving
- 第 1 个 11:表示 11 月(第 11 个月)
- 第 1 个 4:表示星期四(周日为 0,依次计数)
- 第 2 个 4:表示第四个(1 = 第一个,2 = 第二个,-2 = 倒数第二个,依此类推)
月份可以是单个月份或月份列表。例如将 11 改为 ‘'(1 2 3)’ ,条目就会匹配 1、2、3 月的第四个星期四。若月份为 t ,则条目全年每个月都生效。示例:每月第三个星期四的后两天(即星期六)开会:
%%(diary-offset '(diary-float t 3 4) 2) Monthly committee meeting
2 表示在 '(diary-float t 3 4) 匹配的日期之后再顺延 2 天。这在跨时区会议、定期活动顺延等场景中非常实用。
所有标准表达式日记条目都支持一个可选参数,用于指定在日历中标记时使用的外观(face)或单个字符。更通用地,表达式日记条目可以通过任意计算来决定生效日期。详见:表达式条目与高级日记显示
33.10.6. 约会提醒
如果你的某条日记条目对应一个预约,并且该条目以可识别的时刻开头,Emacs 可以 提前提醒 你即将到来的预约。Emacs 会按照变量 appt-display-format 指定的格式显示提醒消息。如果变量 appt-audible 不为 nil,提醒还会包含 声音提示 。此外,如果 appt-display-mode-line 不为 nil,Emacs 会在 模式行 上显示距离预约还有多少分钟。
如果 appt-display-format 的值为 window ,则变量 appt-display-duration 控制提醒窗口的显示时长;变量 appt-disp-window-function 和 appt-delete-window-function 分别指定用于创建和销毁提醒窗口的函数。
要启用预约提醒功能,输入 M-x appt-activate 。
- 带正数值前缀参数:启用提醒
- 带负数值前缀参数:禁用提醒
- 不带参数:切换启用 / 禁用状态
启用提醒时,Emacs 还会从日记文件中生成 今日预约列表 ,提取所有带可识别时刻的日记条目,并在每个预约即将到来时提醒你。
例如,假设日记文件包含以下内容:
Monday 9:30am Coffee break 12:00pm Lunch
那么每周一,你会在上午约 9:20 收到休息提醒,约 11:50 收到午餐提醒。变量 appt-message-warning-time 指定提前多少分钟提醒(默认为 12 分钟)。这是全局默认提醒时间。每个预约可以通过匹配 appt-warning-time-regexp 的片段单独指定提醒时间(详情见该变量的文档)。
你可以使用 12 小时制(12:00am 表示午夜,12:00pm 表示中午)或 24 小时制 书写时间,两种格式可以混用。时刻必须写在日记条目 开头 ,才能被系统识别。
Emacs 会在午夜过后自动从日记文件更新当日预约列表。你也可以通过重新启用预约提醒来 手动强制更新 。除非将 appt-display-diary 设为 nil ,否则上述操作都会显示当天的日记缓冲区。每当日记文件(或其包含的文件,见高级日记显示)被保存时,预约列表也会自动更新。如果你使用 Org 模式并在 Org 议程文件中记录预约,可以使用 org-agenda-to-appt 命令将这些预约加入提醒列表,相关内容参见《Org 手册》中的 “预约提醒” 一节。
你还可以把预约提醒功能当作 闹钟 使用。
- 命令
M-x appt-add:直接向预约列表添加条目,不修改日记文件。 - 命令
M-x appt-delete:从预约列表中删除条目。
33.10.7. 日记条目的导入与导出
你可以在 Emacs 日记文件与多种其他格式之间互相转换日记条目。
可以从 Outlook 生成的预约邮件中 导入 日记条目。在 Rmail 或 Gnus 中查看此类邮件时,执行 M-x diary-from-outlook 即可导入条目。通过自定义变量 diary-outlook-formats ,可以让该命令识别更多预约邮件格式。其他邮件客户端可将 diary-from-outlook-function 设置为合适的值。
icalendar 包支持在 Emacs 日记文件与 iCalendar 文件之间交换数据,iCalendar 格式定义于 RFC 2445《互联网日历与日程安排核心对象规范(iCalendar)》 (同时也兼容更早的 vCalendar 格式)。
命令 icalendar-import-buffer 从当前缓冲区提取 iCalendar 数据并添加到你的日记文件中。该函数也适用于自动提取 iCalendar 数据;例如在 Rmail 中可以这样配置:
(add-hook 'rmail-show-message-hook 'icalendar-import-buffer)
命令 icalendar-import-file 导入一个 iCalendar 文件并将结果添加到 Emacs 日记文件中。示例:
(icalendar-import-file "/here/is/calendar.ics" "/there/goes/ical-diary")
你可以使用 #include 指令将导入文件的内容加入主日记文件(如果二者不是同一个文件)。详见「高级日记显示」。
使用 icalendar-export-file 可交互式地将整个 Emacs 日记文件 导出 为 iCalendar 格式。如果只想导出日记文件的一部分,先标记相应区域,再调用 icalendar-export-region 。两种方式下,Emacs 都会将结果追加到目标文件末尾。
33.11. 夏令时
Emacs 能够区分标准时间与 daylight夏令时 —— 日出、日落、二至点、二分点以及月相的计算时间都会把夏令时考虑在内。夏令时规则因地而异,且在历史上不同年份也会发生变化。要正确计算,Emacs 需要知道应采用哪套规则。
部分操作系统会记录你所在地区适用的夏令时规则;在这类系统上,Emacs 会自动从系统获取所需信息。如果这些信息部分或全部缺失,Emacs 会使用美国马萨诸塞州剑桥市当前采用的规则来补足。若得到的规则不符合你的需求,可以通过设置以下变量来指定 Emacs 使用的规则: calendar-daylight-savings-starts 和 calendar-daylight-savings-ends 。
这些变量的值应当是引用了变量 year 的 Lisp 表达式,求值后得到夏令时开始或结束的公历日期,格式为列表 (月份 日期 年份) 。如果你所在地区不使用夏令时,应将其值设为 nil 。
Emacs 使用这些表达式来确定节假日列表中的夏令时起始日期,并修正日月相关计算中的时刻。
马萨诸塞州剑桥市的对应取值如下:
(calendar-nth-named-day 2 0 3 year) (calendar-nth-named-day 1 0 11 year)
含义为:当年 3 月(第 3 个月)的第 2 个星期日(第 0 天),以及当年 11 月(第 11 个月)的第 1 个星期日。
如果要把夏令时改为从 10 月 1 日开始,可将 calendar-daylight-savings-starts 设置为:
(list 10 1 year)
如果你所在地区 不使用夏令时 ,或希望所有时间都按标准时间计算,可将 calendar-daylight-savings-starts 和 calendar-daylight-savings-ends 都设为 nil 。
变量 calendar-daylight-time-offset 用于指定夏令时与标准时间的差值,单位为分钟。马萨诸塞州剑桥市的该值为 60。
最后,两个变量 calendar-daylight-savings-starts-time 和 calendar-daylight-savings-ends-time 分别指定:在当地时间午夜过后多少分钟,发生进入或退出夏令时的切换。马萨诸塞州剑桥市这两个变量的值均为 120
33.12. 时间间隔求和
timeclock(时间计时)包可以对时间间隔进行求和,方便你(例如)跟踪在特定项目上花费的时间。(更高级的替代方案是使用 Org 模式的计时功能,参见《Org 手册》中的「工作时间计时」。)
开始处理一个项目时使用 M-x timeclock-in ,完成时使用 M-x timeclock-out 。每次操作都会为该项目的记录添加一个时间间隔。你可以使用 M-x timeclock-change 切换到另一个项目。
在积累了多个时间间隔数据后,你可以:
- 使用
M-x timeclock-workday-remaining查看今天还需要工作多长时间(默认按每天 8 小时计算)。 - 使用
M-x timeclock-when-to-leave计算可以下班的时间。
如果你希望 Emacs 在模式行中显示当日剩余工作时长,可以:
- 自定义变量
timeclock-mode-line-display并将其设为t; - 或者直接执行命令
M-x timeclock-mode-line-display。
关闭当前 Emacs 会话是否意味着你已停止工作,默认情况下 Emacs 会向你询问。你可以将变量 timeclock-ask-before-exiting 设为 nil 来关闭这个询问;这样,只有显式执行 M-x timeclock-out 或 M-x timeclock-change 才会结束当前计时。
timeclock 功能会把数据保存在文件 ~/.emacs.d/timelog 中。你可以通过自定义变量 timeclock-file 来指定其他文件名。
如果你手动编辑了时间日志文件,或修改了 timeclock 的任何可自定义变量,应当执行命令 M-x timeclock-reread-log ,让 Emacs 从文件重新加载并更新数据。
33.13. 日历与日记的高级功能
本节介绍日历与日记的一些更高级、更专用的功能。首先介绍你可以用来自定义日历和日记、使其符合个人使用习惯的多种方式。
33.13.1. 定制日历
遗憾的是,日历显示无法从三个月的布局改为其他形式,但你可以通过设置以下变量来自定义空白间距: calendar-left-margin 、 calendar-day-header-width 、 calendar-day-digit-width 、 calendar-column-width 以及 calendar-intermonth-spacing 。若要在月份之间显示文本(例如周数),可按照文档说明自定义变量 calendar-intermonth-header 和 calendar-intermonth-text 。
变量 calendar-month-header 控制日历中每个月份上方显示的文本,默认显示月份与年份。变量 calendar-day-header-array 控制每个月中每日列上方显示的文本,默认显示每周日期名称的前两个字母。
变量 calendar-holiday-marker 指定如何标记节假日日期,其值可以是一个单字符字符串(显示在日期旁),也可以是用于渲染日期的样式(face)名称。同样,变量 diary-entry-marker 指定如何标记含有日记条目的日期。函数 calendar-mark-today 使用 calendar-today-marker 来标记当天日期。默认情况下,日历分别使用名为 holiday、diary 和 calendar-today 的样式来实现这些标记。
启动日历时会运行常规钩子 calendar-initial-window-hook ,重新计算日历显示时不会运行该钩子。但如果你使用 q 命令退出日历后再次进入,该钩子会重新运行。
变量 calendar-today-visible-hook 是一个常规钩子,当日历缓冲区已准备好、且当前日期在窗口中可见时运行。该钩子的一种用途是标记当天日期,可使用以下任一函数实现: calendar-mark-today 或 calendar-star-date :
(add-hook 'calendar-today-visible-hook 'calendar-mark-today)
与之类似的常规钩子 calendar-today-invisible-hook 会在当前日期不在窗口中可见时运行。
每一个日历光标移动命令都会在移动光标之后运行钩子 calendar-move-hook 。
33.13.2. 定制节假日
Emacs 内置了多个列出默认节假日的变量,它们分别是: holiday-general-holidays 、 holiday-local-holidays 、 holiday-solar-holidays 、 holiday-bahai-holidays 、 holiday-christian-holidays 、 holiday-hebrew-holidays 、 holiday-islamic-holidays 、 holiday-oriental-holidays 和 holiday-other-holidays 。这些变量名含义一目了然;例如 holiday-solar-holidays 列出与太阳、月亮相关的节假日。
你可以根据自身需求自定义这些节假日列表,按照下面的说明删除或添加节假日。将其中任意变量设为 nil 即可不显示对应的节假日。
通用节假日 默认是全美国通用的节假日。相比之下, holiday-local-holidays 和 holiday-other-holidays 默认均为空,分别用于 系统级设置 和 个人自定义 。
默认情况下,Emacs 不会包含它所支持的所有宗教节日,只包含世俗日历中常见的那些。如需显示更完整的宗教节假日,可以将以下一个或全部变量设为 t : calendar-bahai-all-holidays-flag 、 calendar-christian-all-holidays-flag 、 calendar-hebrew-all-holidays-flag 或 calendar-islamic-all-holidays-flag 。
每个节假日变量都是一个 节假日格式(holiday form) 列表,每个格式描述一个(或一组)节假日。下表列出所有可用的节假日格式。日期与月份从 1 开始计数,但星期几从周日为 0 开始。参数 string 始终是节假日的描述字符串。
(holiday-fixed month day string)- 公历中的固定日期。
(holiday-float month dayname k string &optional day)- 公历月份 month、日期 day 前后的第 k 个星期 dayname(0 = 周日,依此类推)。k 为负数表示从月末倒数。可选参数 day 在 k 为正时默认为 1,为负时默认为当月最后一天。
(holiday-chinese month day string)- 农历中的固定日期。
(holiday-hebrew month day string)- 希伯来历中的固定日期。
(holiday-islamic month day string)- 伊斯兰历中的固定日期。
(holiday-julian month day string)- 儒略历中的固定日期。
(holiday-sexp sexp string)- 由 Lisp 表达式 sexp 计算出的日期。该表达式应使用变量
year计算并返回节假日日期,格式为列表(month day year);若当年无该节日则返回nil。 (if condition holiday-form)- 仅当 condition 为真时才生效的节假日。
(function [args])- 由函数 function 调用参数 args 计算得到的日期列表。
示例: 例如,你想添加法国 7 月 14 日的巴士底日,可以这样写:
(setq holiday-other-holidays '((holiday-fixed 7 14 "Bastille Day")))
许多节日在每月某个特定星期几。以下是维尔京群岛 7 月第四个周一的 “飓风祈祷日”:
(holiday-float 7 1 4 "Hurricane Supplication Day")
这里 7 = 七月,1 = 周一(0 = 周日,2 = 周二),4 = 当月第四个。1 = 第一个,-1 = 最后一个,-2 = 倒数第二个,依此类推。
你也可以指定巴哈伊、农历、希伯来历、伊斯兰历、儒略历的固定节日,例如:
(setq holiday-other-holidays '((holiday-hebrew 10 2 "Last day of Hanukkah") (holiday-islamic 3 12 "Mohammed's Birthday") (holiday-julian 4 2 "Jefferson's Birthday")))
添加了光明节最后一天(因为希伯来历的月份从尼散月开始计为 1)、纪念穆罕默德诞辰的伊斯兰节日(因为伊斯兰历的月份从穆哈兰姆月开始计为 1),以及托马斯・杰斐逊的生日 —— 按儒略历计算为 1743 年 4 月 2 日。
如需 条件生效 的节日,可使用 Emacs Lisp 的 if 或 holiday-sexp 。例如,美国总统选举在能被 4 整除的年份 11 月第一个周一之后的第一个周二:
(holiday-sexp '(if (zerop (% year 4))
(calendar-gregorian-from-absolute
(1+ (calendar-dayname-on-or-before
1 (+ 6 (calendar-absolute-from-gregorian
(list 11 1 year)))))))
"US Presidential Election")
或:
(if (zerop (% displayed-year 4)) (holiday-fixed 11 (calendar-extract-day (calendar-gregorian-from-absolute (1+ (calendar-dayname-on-or-before 1 (+ 6 (calendar-absolute-from-gregorian (list 11 1 displayed-year))))))) "US Presidential Election"))
有些节日无法用上述格式表示,因为需要特殊计算。这种情况下你需要编写 Lisp 函数。例如添加日食、月食,可在 holiday-other-holidays 中加入 (eclipses) ,并编写函数 eclipses 返回公历日期与描述的列表,格式如下:
(((6 4 2012) "Lunar Eclipse") ((11 13 2012) "Solar Eclipse") ... )
33.13.3. 玛雅日历转换
以下是基于玛雅日历选择日期的命令:
g m l- 跳转到由长计历指定的日期 (
calendar-mayan-goto-long-count-date) 。 g m n t- 跳转到卓尔金历中下一个出现的指定日期 (
calendar-mayan-next-tzolkin-date) 。 g m p t- 跳转到卓尔金历中上一个出现的指定日期 (
calendar-mayan-previous-tzolkin-date) 。 g m n h- 跳转到哈布历中下一个出现的指定日期 (
calendar-mayan-next-haab-date) 。 g m p h- 跳转到哈布历中上一个出现的指定日期 (
calendar-mayan-previous-haab-date) 。 g m n c- 跳转到日历轮中下一个出现的指定组合日期 (
calendar-mayan-next-calendar-round-date) 。 g m p c- 跳转到日历轮中上一个出现的指定组合日期 (
calendar-mayan-previous-calendar-round-date) 。
要理解这些命令,你需要了解玛雅历法。长计历是按以下单位对天数进行计数:
1 金(kin) = 1 天 1 维纳尔(uinal) = 20 金 1 吞(tun) = 18 维纳尔 1 卡吞(katun) = 20 吞 1 巴克吞(baktun) = 20 卡吞
因此,长计历日期 12.16.11.16.6 表示:12 巴克吞、16 卡吞、11 吞、16 维纳尔、6 金。Emacs 日历可处理最早为 7.17.18.13.3 的玛雅长计历日期,更早则不支持。使用 g m l 命令时,请以点号分隔baktun, katun, tun, uinal, and kin的方式输入玛雅长计历日期。
玛雅 卓尔金历(tzolkin) 是由 13 天和 20 天两个独立周期组成的 260 天循环。由于该周期无限循环,Emacs 提供了在周期中向前、向后跳转的命令。输入 g m p t 可跳转到上一个卓尔金日期;Emacs 会提示你输入一个卓尔金日期,并将光标移至该日期上一次出现的位置。同理,输入 g m n t 可跳转到该日期下一次出现的位置。
玛雅 哈布历(haab) 是 365 天的循环,分为 18 个月,每月 20 天,最后再加 5 天无月份时段。与卓尔金历类似,该周期也无限循环,同样提供向前、向后跳转命令。输入 g m p h 可跳转到上一个哈布日期;Emacs 会提示你输入哈布日期,并将光标移至该日期上一次出现的位置。同理,输入 g m n h 可跳转到该日期下一次出现的位置。
玛雅人还会将卓尔金历与哈布历结合使用。这种组合形成约 52 年的周期,称为日历轮(calendar round)。输入 g m p c 时,Emacs 会同时要求你输入哈布日期与卓尔金日期,然后将光标移至该组合上一次出现的位置。使用 g m n c 可跳转到该组合下一次出现的位置。若你输入的哈布 / 卓尔金日期组合在历法中不合法,这些命令会报错。
每当 Emacs 要求你输入玛雅名称时,都会使用 严格补全 (见补全退出),因此你不必担心拼写问题。
33.13.4. 日期显示格式
你可以通过设置 calendar-date-display-form 来自定义日期在日记、模式行和消息中的显示方式。该变量是一个表达式列表,表达式中可以使用以下变量:
- month、day、year:均为字符串形式的数字
- monthname、dayname:均为字母形式的字符串
美式风格下,该列表的默认值如下:
((if dayname (concat dayname ", ")) monthname " " day ", " year)
而欧式风格的默认值为:
((if dayname (concat dayname ", ")) day " " monthname " " year)
ISO 日期的默认表示形式为:
((format "%s-%.2d-%.2d" year (string-to-number month)
(string-to-number day)))
另一种常见的美式格式为:
(month "/" day "/" (substring year -2))
33.13.5. 时间显示格式
日历与日记默认采用美式常规时间格式显示时刻,即小时为 1–12、后跟分钟,并标注 am(上午)或 pm(下午)。若你偏好欧洲制式(在美国也称为军用时间),即小时采用 00–23 的 24 小时制,可以修改变量 calendar-time-display-form 。
该变量是一个表达式列表,可使用以下变量:
- 12-hours、24-hours、minutes:均为字符串形式的数字
- am-pm、time-zone:均为字母字符串
默认取值为:
(12-hours ":" minutes am-pm (if time-zone " (") time-zone (if time-zone ")"))
以下取值可实现欧洲制式时间:
(24-hours ":" minutes (if time-zone " (") time-zone (if time-zone ")"))
注意:目前只有极少数日历函数会返回具体时刻(现阶段仅有太阳相关函数)。
33.13.6. 定制日记
通常情况下,日记窗口会在模式行或缓冲区本身中,标注日记条目对应日期的节假日。检查节假日的过程可能较慢,具体取决于已定义的节假日数量。此时,将 diary-show-holidays-flag 设置为 nil 可以加快日记显示速度。
变量 diary-number-of-entries 用于控制一次显示多少天的日记条目。它会影响 calendar-view-diary-initially-flag 为 t 时的初始显示,以及命令 M-x diary 的行为。例如:值为 1(默认)时,只显示当天的日记条目;值为 2 时,还会显示次日的条目。该变量的值也可以是一个包含 7 个整数的向量。例如,值为 [0 2 2 2 2 4 1] 时:
- 周日不显示任何日记条目;
- 周一至周四显示当天及次日的日记条目;
- 周五显示周五到下周一的条目;
- 周六只显示当天的条目。
你可以通过设置变量 diary-date-forms 来自定义日记文件中的日期格式。该变量是一个用于识别日期的模式列表。每个日期模式都是一个列表,其元素可以是:
- 正则表达式(详见《Emacs Lisp 参考手册》中的正则表达式);
- 或符号:month、day、year、monthname、dayname。
所有这些元素都作为模式,匹配日记文件中的特定文本。只有当所有元素连续匹配时,整个日期模式才算匹配成功。
日期模式中的正则表达式按常规方式匹配,使用经过调整的标准语法表,其中 '*' 被视为词构成成分。
符号 month、day、year、monthname、dayname 分别匹配:月份数字、日期数字、年份数字、月份名称、星期名称。匹配数字的符号允许前导零;匹配名称的符号允许大小写与缩写(由 calendar-month-abbrev-array 和 calendar-day-abbrev-array 指定)。所有符号都可以匹配 '*' ;因为日记条目中的 '*' 表示 “任意日”“任意月” 等,无论当前日期是什么都应匹配。
美式风格下 diary-date-forms 的默认值由 diary-american-date-forms 提供:
((month "/" day "[^/0-9]") (month "/" day "/" year "[^0-9]") (monthname " *" day "[^,0-9]") (monthname " *" day ", *" year "[^0-9]") (dayname "\\W"))
变量 diary-european-date-forms 和 diary-iso-date-forms 提供其他默认风格。
列表中的日期模式必须 互斥 ,且 只能匹配日期和一个空白字符 ,不能匹配日记条目本身的内容。如果为了互斥,模式必须匹配日期结束空白之外的日记条目内容,则日期模式的 第一个元素必须是 backup 。这会让日期识别器在匹配完成后,回退到日记条目当前词的开头。即使使用 backup,日期模式也绝对不能匹配日记条目第一个词以外的内容。
例如, diary-european-date-forms 的默认值为:
((day "/" month "[^/0-9]") (day "/" month "/" year "[^0-9]") (backup day " *" monthname "\\W+\\<\\([^*0-9]\\|\\([0-9]+[:aApP]\\)\\)") (day " *" monthname " *" year "[^0-9]") (dayname "\\W"))
注意第三个模式中使用了 backup ,因为它需要匹配日期之外的部分词内容,以便与第四个模式区分开。
33.13.7. 使用非公历的日记条目
除基于标准公历的条目外,你的日记还可使用巴哈伊历、农历、希伯来历或伊斯兰历日期。不过,识别这类条目可能比较耗时;且由于大多数用户并不使用,你必须显式启用该功能。例如,若希望日记识别希伯来历日期条目,需添加如下配置:
(add-hook 'diary-nongregorian-listing-hook 'diary-hebrew-list-entries) (add-hook 'diary-nongregorian-marking-hook 'diary-hebrew-mark-entries)
同理,要启用伊斯兰历、巴哈伊历、农历条目,可分别添加:
- 伊斯兰历:
diary-islamic-list-entries、diary-islamic-mark-entries - 巴哈伊历:
diary-bahai-list-entries、diary-bahai-mark-entries - 农历:
diary-chinese-list-entries、diary-chinese-mark-entries
这类日记条目的格式与公历条目基本一致,区别在于需要在日期前添加对应历法的标识:
- 巴哈伊历:
diary-bahai-entry-symbol(默认为 B) - 农历:
diary-chinese-entry-symbol(默认为 C) - 希伯来历:
diary-hebrew-entry-symbol(默认为 H) - 伊斯兰历:
diary-islamic-entry-symbol(默认为 I)
此外,非公历月份名称不允许缩写(因为前三个字母通常不唯一)。(同时注意:若要表示平年希伯来历的亚达月,必须写作 "Adar I"。)
示例:希伯来历 Heshvan 25 日的日记条目可写作:
HHeshvan 25 Happy Hebrew birthday!
该条目会在对应希伯来历 Heshvan 25 的公历日期当天显示。伊斯兰历 Dhu al-Qada 25 日的日记条目示例:
IDhu al-Qada 25 Happy Islamic birthday!
与公历条目一样,如果在非公历条目前加上 diary-nonmarking-symbol (默认为 '&' ),则该条目不做标记显示。
下表为在日历中创建巴哈伊历、农历、希伯来历、伊斯兰历日记条目的命令:
i h d- diary-hebrew-insert-entry
i h m- diary-hebrew-insert-monthly-entry
i h y- diary-hebrew-insert-yearly-entry
i i d- diary-islamic-insert-entry
i i m- diary-islamic-insert-monthly-entry
i i y- diary-islamic-insert-yearly-entry
i B d- diary-bahai-insert-entry
i B m- diary-bahai-insert-monthly-entry
i B y- diary-bahai-insert-yearly-entry
i C d- diary-chinese-insert-entry
i C m- diary-chinese-insert-monthly-entry
i C y- diary-chinese-insert-yearly-entry
i C a- diary-chinese-insert-anniversary-entry
这些命令的用法与普通日记条目命令类似:作用于光标所在的日历日期,并在日记文件末尾仅插入日期部分,之后你需要手动补充条目内容。
- 基础命令:为指定的非公历日期创建条目
- 'monthly' 命令:每月对应非公历日创建条目
- 'yearly' 命令:每年对应非公历月、日创建条目
33.13.8. 日记显示
diary日记 显示的工作流程是:先生成日记条目列表,再运行由变量 diary-display-function 指定的函数。其默认值 diary-fancy-display 会将日记条目与节假日复制到一个仅用于显示的专用缓冲区中进行展示。将条目复制到独立缓冲区,可以对显示内容进行美化处理 —— 例如按日期对条目进行排序。
默认情况下,精美日记缓冲区 不会显示没有任何日记条目的日期 ,即便当天是节假日。如果你希望在缓冲区中显示这些空白日期,可将变量 diary-list-include-blanks 设置为 t 。
精美日记缓冲区会自动启用查看模式(View mode)。
另一种显示方式 diary-simple-display 则直接显示 真实的日记缓冲区 ,并通过隐藏文本的方式隐藏不相关的条目。节假日信息会显示在模式行中。这种方式的优点是:你可以直接编辑缓冲区,并将修改保存到日记文件中。但它不如精美显示方式灵活,例如无法对条目排序。另一个缺点是隐藏文本可能造成困扰:比如选中区域复制时,可能会包含隐藏的文本;而且由于你看到的日记缓冲区是 “经过伪装” 的,直接打印缓冲区可能与屏幕显示不一致。
因此,Emacs 提供了专门用于按所见效果打印日记的命令: M-x diary-print-entries 该命令对两种显示方式都有效;不过在精美显示模式下,你也可以像普通缓冲区一样直接打印。
若要打印一周的逐日日记录:将光标定位到该周第一天,输入 7 d ,然后执行 M-x diary-print-entries 。和之前一样,显示节假日会略微降低速度,你可以将 diary-show-holidays-flag 设置为 nil 来加快速度。
该命令会创建一个临时缓冲区,只包含日记缓冲区中当前可见的条目。与简单显示不同,这些无关条目是真正被移除,而不只是隐藏。创建完缓冲区后,会运行钩子 diary-print-entries-hook 。该钩子的默认行为是使用 lpr-buffer 命令直接将内容发送到打印机(参见 “打印硬拷贝”)。如果你想使用其他命令进行打印,只需修改这个钩子的值。你也可以用它实现其他功能,例如按日期和时间重新排序行。
你可以在简单显示的日记窗口中编辑条目,但要记住:当前显示的缓冲区实际上是完整的日记文件,只是部分内容被隐藏了。这意味着,例如 C-f (forward-char) 可能会把光标移到看起来是行尾、但实际是某隐藏行中间的位置。
在简单显示模式下编辑日记时请格外小心! 在可见行中间插入行或增删字符通常不会有问题,但在行尾编辑可能达不到预期效果。删除一行时,可能会同时删除其后隐藏的其他条目。在简单日记缓冲区编辑前,建议先按 s (diary-show-all-entries) 显示整个文件。
33.13.9. 高级日记显示
以下功能仅在精美日记显示模式下生效。
你可以使用常规钩子 diary-list-entries-hook ,将每天的日记条目按当天时间排序。配置方法如下:
(add-hook 'diary-list-entries-hook 'diary-sort-entries t)
这样会对每天中以可识别时间开头的日记条目按时间排序,不带时间的条目排在最前面。注意排序命令放在钩子列表的末尾,以防前面的钩子函数改变了日记条目顺序或新增内容。
你可以在日记条目中书写注释:只需将变量 diary-comment-start 和 diary-comment-end 设置为用于界定注释的字符串即可。精美显示模式不会显示注释内容。你可以在注释中存放供其他扩展包使用的元信息(例如提醒包 appointment,参见「提醒」相关内容)。
主日记文件可以 包含其他文件 。这允许多人共享一份适用于所有人的公共日记文件。在日记文件中,以 diary-include-string 开头的行:
#include "filename"
会将文件 filename 中的日记条目包含到精美日记缓冲区中。该包含机制支持 递归 ,即被包含的文件还可以继续包含其他文件(当然要注意避免循环包含)。启用包含功能的配置:
(add-hook 'diary-list-entries-hook 'diary-include-other-diary-files) (add-hook 'diary-mark-entries-hook 'diary-mark-included-diary-files)
包含机制仅在精美日记显示模式下有效,因为简单日记显示模式是直接从你的日记文件中展示条目。
33.13.10. Sexp 条目与高级日记显示
Sexp 格式日记条目不仅能设置复杂的生效条件,还能实现更多功能。在日记文件中,Sexp 条目必须以 diary-sexp-entry-symbol (默认为 '%%' )开头。在精美日记显示模式下,Sexp 条目可以根据日期自动生成条目文本。
例如,纪念日日记条目可以将纪念日至今的年数插入到条目中。下面这条日记条目中的 '%d' :
%%(diary-anniversary 10 31 1948) Arthur's birthday (%d years old)
会被替换为年龄。因此在 1990 年 10 月 31 日,精美日记缓冲区中会显示为:
Arthur's birthday (42 years old)
如果日记文件中是这样写:
%%(diary-anniversary 10 31 1948) Arthur's %d%s birthday
那么 1990 年 10 月 31 日的显示效果为:
Arthur's 42nd birthday
同理,循环日记条目可以插入已重复的次数:
%%(diary-cyclic 50 1 1 2012) Renew medication (%d%s time)
在 2012 年 9 月 7 日的精美日记显示中会变成:
Renew medication (5th time)
还有一种提前提醒的 Sexp 日记函数,它不仅在事件当天显示条目,还会在之前的日期显示。例如,想在纪念日提前一周提醒:
%%(diary-remind '(diary-anniversary 12 22 1968) 7) Ed's anniversary
精美日记会在12 月 15 日和 12 月 22 日都显示 Ed's anniversary。
函数 diary-date 用于匹配由月、日、年组合描述的日期,每个部分可以是:整数、整数列表或 t(表示所有值)。例如:
%%(diary-date '(10 11 12) 22 t) Rake leaves
会让精美日记在每年 10、11、12 月的 22 日都显示:
Rake leaves
函数 diary-float 可用于描述相对日期,例如 “11 月第三个周五”“4 月最后一个周二”。参数为:month、dayname、序号 n。条目会出现在当月 1 日之后的第 n 个指定星期。
- dayname:0 = 周日,1 = 周一,依此类推
- n 为负时,从月末倒数计数
- 月份可以是列表、单个月或 t(所有月份) 例如:
%%(diary-float t 1 -1) Pay rent
表示每个月最后一个周一显示:
Pay rent
Sexp 日记条目的通用性极强,任何能用算法描述的规则都可以实现。Sexp 条目包含一个表达式,用于计算该条目是否适用于给定日期:
- 表达式返回非 nil → 当天显示
- 表达式返回 nil → 当天不显示
- 表达式中可使用变量 date 获取当前判断的日期,格式为公历列表
(月 日 年)。
表达式返回非 nil 时,部分值有特殊含义:返回字符串:该字符串作为当天事件的描述。返回 (mark . string) : mark 控制日历中日期的标记方式,string 是事件描述。
- mark 为单个字符:显示在日期旁
- mark 为 Face 名称:用该样式显示日期
- mark 为 nil:不特殊高亮
举个实用例子:如果每月 21 日是工作日就发工资,若是周末则提前到上一个周五。对应的 Sexp 条目:
&%%(let ((dayname (calendar-day-of-week date)) (day (cadr date))) (or (and (= day 21) (memq dayname '(1 2 3 4 5))) (and (memq day '(19 20)) (= dayname 5))) ) Pay check deposited
以下 Sexp 日记条目可在精美日记显示中根据日期动态生成内容:
%%(diary-sunrise-sunset)- 生成条目,显示当天当地日出日落时间。
%%(diary-lunar-phases)- 生成条目,显示月相。
%%(diary-day-of-year)- 生成条目,显示今天是当年第几天、还剩几天。
%%(diary-iso-date)- 生成条目,显示对应 ISO 商业日期。
%%(diary-julian-date)- 生成条目,显示对应儒略日期。
%%(diary-astro-day-number)- 生成条目,显示对应天文(儒略)日数。
%%(diary-bahai-date)- 生成条目,显示对应巴哈伊历日期。
%%(diary-chinese-date)- 生成条目,显示对应农历日期。
%%(diary-coptic-date)- 生成条目,显示对应科普特历日期。
%%(diary-ethiopic-date)- 生成条目,显示对应埃塞俄比亚历日期。
%%(diary-french-date)- 生成条目,显示对应法国共和历日期。
%%(diary-hebrew-date)- 生成条目,显示对应希伯来历日期。
%%(diary-islamic-date)- 生成条目,显示对应伊斯兰历日期。
%%(diary-mayan-date)- 生成条目,显示对应玛雅历日期。
%%(diary-persian-date)- 生成条目,显示对应波斯历日期。
例如,在日记中加入:
&%%(diary-hebrew-date)
使用精美日记显示时,每天都会显示对应的希伯来历日期。(简单显示模式下只会原样显示这行代码。)
以下函数用于生成希伯来相关的标准 Sexp 日记条目:
%%(diary-hebrew-rosh-hodesh)- 显示希伯来新月(月初)的到来与宗教宣告。
%%(diary-hebrew-parasha)- 每周六显示当周犹太教堂诵读的经文段落。
%%(diary-hebrew-sabbath-candles)- 每周五显示当地安息日点灯时间。
%%(diary-hebrew-omer)- 适当日期显示俄默计数。
%%(diary-hebrew-yahrzeit 月 日 年) 姓名- 显示忌日纪念日。日期为公历,会在希伯来历对应日及前一天显示。(参数的顺序会根据日历日期格式而变化;例如在欧洲格式下为日、月、年。)
%%(diary-hebrew-birthday 月 日 年)- 按希伯来历显示生日。
以上所有函数都接受可选参数 mark ,用于指定在日历中如何标记日期。如果函数判定当天生效,会返回包含 mark 信息的值。
34. 发送邮件
要在 Emacs 中发送电子邮件,按 C-x m 。这会切换到名为 *unsent mail* 的缓冲区,你可以在其中编辑邮件的正文和头部。完成后,按 C-c C-s 或 C-c C-c 发送邮件。
C-x m- 开始撰写邮件 (
compose-mail) 。 C-x 4 m- 功能同上,但在另一个窗口中打开 (
compose-mail-other-window) 。 C-x 5 m- 功能同上,但在新框架中打开 (
compose-mail-other-frame) 。 C-c C-s- 在邮件缓冲区中,发送邮件 (
message-send) 。 C-c C-c- 在邮件缓冲区中,发送邮件并隐藏该缓冲区 (
message-send-and-exit) 。
邮件缓冲区是一个普通的 Emacs 缓冲区,因此你在撰写邮件时可以切换到其他缓冲区。如果你想在完成当前邮件之前发送另一封邮件,再次按 C-x m ,会打开一个带有不同数字后缀的新邮件缓冲区(参见 “杂项缓冲区操作”)。(这仅在使用默认的 Message 模式撰写邮件时有效;参见 “邮件命令”。)
如果你想继续编辑之前未完成的邮件,使用前缀参数执行该命令: C-u C-x m ,Emacs 会切换到你上次使用的邮件缓冲区,从中断处继续编辑。
命令 C-x 4 m (compose-mail-other-window) 与 C-x m 功能相同,只是在另一个窗口显示邮件缓冲区。命令 C-x 5 m (compose-mail-other-frame) 则在新框架中打开。
当你按 C-c C-c 或 C-c C-s 发送邮件时,Emacs 可能会询问你邮件的投递方式 ——是直接通过 SMTP,还是使用其他方式。详情参见 “发送邮件”。
34.1. 邮件缓冲区格式
下面是一个邮件缓冲区内容的示例
To: [email protected] CC: [email protected], [email protected] Subject: Re: What is best in life? From: [email protected] --text follows this line-- To crush your enemies, see them driven before you, and to hear the lamentation of their women.
邮件缓冲区的顶部是一组 header fields头字段 ,用于指定邮件的收件人、主题等信息。上面的缓冲区包含 'To'、'CC'、'Subject' 和 'From' 头字段。在合适的情况下,部分头字段会在邮件缓冲区中自动预先填充。
标注为 '--text follows this line--' 的这一行,用于将 邮件头 与 / body (or text)邮件正文/ 分隔开。该行以上的所有内容都被视为邮件头;该行以下的所有内容都被视为邮件正文。这条分隔线本身 不会 出现在实际发送的邮件中。
你可以使用普通的编辑命令来插入和修改头字段。有关专门用于编辑邮件头的命令,请参见「邮件头编辑」章节。某些头字段(如 'Date' 和 'Message-Id' )通常不会出现在邮件缓冲区中,而是在邮件发送时自动生成。
34.2. 邮件头字段
邮件缓冲区中的头字段以一行开头的字段名开始,以冒号结束。字段名不区分大小写。冒号与可选的空白字符之后是该字段的内容。
你可以为头字段使用任意名称,但通常人们只使用具有公认含义的 标准字段名 。
'From' 头字段标识发送邮件的人(也就是你)。它应当是一个合法的邮件地址,因为回复邮件通常会发送到该地址。此字段的默认内容由变量 user-full-name (指定你的全名)和 user-mail-address (你的邮件地址)计算得出。在某些操作系统上,Emacs 会通过环境变量初始化这两个变量(参见 “通用变量”)。如果这些信息不可用或不正确,你需要自行定制这些变量(参见 “简易定制界面”)。
除 'From' 之外,下面是常用字段一览表:
- '
To' - 邮件的目标收件地址。填写多个地址时,用逗号分隔。
- '
Subject' - 邮件主题。
- '
CC' - 邮件的抄送地址。与 '
To' 类似,但这些接收者不应将邮件视为直接发给自己。 - '
BCC' - 邮件的密送地址。这些地址 不会 出现在实际发送的邮件头部中。 '
BCC' 是 “盲抄送”(blind carbon copies)的缩写。 - '
FCC' - 一个文件名,已发送邮件的副本会追加到该文件中。Emacs 以 mbox 格式写入邮件,除非该文件是 Babyl 格式(Emacs 23 之前 Rmail 使用的格式),此时会以 Babyl 格式写入。如果某个 Rmail 缓冲区正在访问该文件,Emacs 会相应地更新它。如需指定多个文件,可使用多个 '
FCC' 字段,每个字段填写一个文件名。 - '
Reply-To' - 回复邮件时应发送到的地址,替代 '
From' 地址。当你的 'From' 地址无法接收回复时会用到此字段。 - '
Mail-Reply-To' - 优先级高于 '
Reply-To' 。之所以使用此字段,是因为某些邮件列表会出于自身目的设置 'Reply-To' (这一做法存在一定争议)。 - '
Mail-Followup-To' - 后续跟进邮件默认使用的一个或多个地址。通常用于你订阅了邮件列表并回复列表中的邮件,且希望回复直接发到列表、而不额外发给自己的场景。
- '
In-Reply-To' - 你正在回复的邮件的标识符。大多数邮件阅读器会用该信息将相关邮件归为一组。通常,在 Emacs 内置的任意邮件程序中回复邮件时,此字段会 自动填充 。
- '
References' - 此前相关邮件的标识符列表。与 '
In-Reply-To' 类似,通常也会自动为你填充。
'To' 、 'CC' 和 'BCC' 字段可以出现任意多次,每个头字段都可包含多个以逗号分隔的地址。通过这种方式,你可以指定任意数量的收件目标。这些字段还支持 续行 :在字段起始行之后,以空白字符开头的一行或多行,都会被视为该字段的一部分。下面是带续行的 'To' 字段示例:
你可以通过将变量 mail-default-headers 设置为一个字符串,让 Emacs 在邮件缓冲区中自动插入某些默认头部。之后使用 C-x m 时就会把该字符串插入到邮件头部。例如,下面的代码会为每封邮件添加 'Reply-To' 和 'FCC' 头:
(setq mail-default-headers "Reply-To: [email protected]\nFCC: ~/Mail/sent")
如果默认头部不适用于某封邮件,在发送前按需编辑即可。
34.3. 邮件别名
你可以定义 mail aliases邮件别名 ,即用简短易记的名称来代表一个或多个邮件地址。默认情况下,邮件别名定义在文件 ~/.mailrc 中。你可以通过设置变量 mail-personal-alias-file 来指定其他文件。
在 ~/.mailrc 中 定义别名 的格式如下:
alias nick别名 fulladdresses完整地址列表
这表示 nick 会展开为对应的 fulladdresses ,后者可以是单个地址,也可以是用空格分隔的多个地址。例如,让 maingnu 代表 [email protected] 以及你自己的一个本地地址,可以写:
alias maingnu [email protected] local-gnu
如果地址中包含空格,需要用一对双引号将整个地址括起来:
alias jsmith "John Q. Smith <[email protected]>"
注意:你不需要给地址里的单独部分(比如人名)加引号,Emacs 会在需要时自动添加。例如上面的地址会被插入为: "John Q. Smith" <[email protected]> 。
编辑 ~/.mailrc 文件后,或该文件在 Emacs 外部被修改时,可以在已运行的 Emacs 中执行: M-x rebuild-mail-abbrevs RET 来更新当前使用的邮件别名。命令会提示输入别名文件,默认使用 mail-personal-alias-file 指定的文件。类似的命令 merge-mail-abbrevs 会提示选择一个别名文件,然后将该文件中的别名 合并 到已有的别名中。
你也可以直接用 Emacs 命令定义邮件别名: define-mail-abbrev 会提示输入别名和完整地址,然后将该别名定义为展开为对应地址。Emacs 在执行保存所有文件( C-x s 或 C-x C-c )时,会像保存其他缩写一样自动保存新增的别名(参见 “保存缩写”)。
Emacs 同样支持在 ~/.mailrc 中使用 包含命令 ,格式如下:
source filename
~/.mailrc 并非 Emacs 专用文件;许多其他邮件客户端也用它存放别名,文件中还可以包含其他各类命令。但 Emacs 只识别 别名定义 和 包含命令 ,其余内容都会忽略。
邮件别名会像普通缩写一样展开:即在别名后输入 分词字符 时立即展开(参见 “缩写”)。展开只发生在以下头字段内:To、From、CC、BCC、Reply-To(以及它们以 Resent- 开头的变体);在其他头字段(如 Subject)中不会展开。
你还可以直接插入别名对应的地址,使用命令: M-x mail-abbrev-insert-alias 它会带补全地读取一个别名,并在光标处插入其展开后的地址。
命令 mail-abbrev-complete-alias 用于对光标前的邮件别名进行 补全
34.4. 邮件命令
*mail* 缓冲区默认的主模式称为消息模式(Message mode)。它在很多方面与文本模式(Text mode)相似,但在 C-c 前缀键下提供了若干额外命令,让编辑邮件更加方便。
本节将介绍消息模式中一些最常用的命令。消息模式还有专门的手册,会更详细地描述其功能。参见《Message》手册中的 Message 节点。
34.4.1. 邮件发送
C-c C-c- 发送邮件,并隐藏邮件缓冲区 (
message-send-and-exit) 。 C-c C-s- 发送邮件,并保持邮件缓冲区为当前选中状态 (
message-send) 。
发送邮件最常用的命令是 C-c C-c (message-send-and-exit) 。它会发送邮件并将邮件缓冲区 隐藏 ,降低其重新被选中的优先级。如果你希望发送后直接 关闭 邮件缓冲区,可以将变量 message-kill-buffer-on-exit 设为 t 。
命令 C-c C-s (message-send) 会发送邮件,但 不退出 邮件缓冲区。适合需要继续修改邮件(例如添加新收件人)并再次发送的场景。
发送邮件时会运行钩子 message-send-hook 。同时会将邮件缓冲区标记为 未修改 ,除非该缓冲区同时关联到文件(这种情况下只有保存文件才会标记为未修改,重复发送同一邮件也不会出现警告)。
变量 message-send-mail-function 控制邮件的发送方式(Mail mode 下使用 send-mail-function )。 send-mail-function 可以取以下函数之一:
sendmail-query-once- 询问一次发送方式(从下面列表中选择),本次邮件使用该方式发送,并将方式保存到
send-mail-function,以后默认使用该方式。这是默认值,除非你已经通过smtpmail-send-it设置过发信相关变量。 smtpmail-send-it- 通过外部邮件服务器发送邮件,例如你的网络服务提供商提供的外发 SMTP 服务器。如果尚未告诉 Emacs 如何连接 SMTP 服务器,Emacs 会提示你输入信息,这些信息会保存在变量
smtpmail-smtp-server以及文件~/.authinfo中。参见《通过 SMTP 发送邮件》中的 “Emacs SMTP 库” 一节。 sendmail-send-it- 使用系统默认的
sendmail程序(或等价程序)发送邮件。这要求系统已配置为可直接通过 SMTP 投递邮件。 mailclient-send-it- 将邮件缓冲区交给系统指定的邮件客户端处理。详情参见文件
mailclient.el中的注释说明。 feedmail-send-it- 与
sendmail-send-it类似,但允许你将邮件加入队列,稍后再统一发送。详情参见文件feedmail.el中的注释说明。
当你发送包含非 ASCII 字符的邮件时,这些字符需要通过 编码系统 进行编码(见 Coding Systems).。通常编码系统会由你当前选择的语言环境自动指定 (see Language Environments)。你可以通过设置变量 sendmail-coding-system 显式指定外发邮件使用的编码系统(see Recognizing Coding Systems)。如果自动确定的编码无法处理邮件中的某些字符,Emacs 会让你从可选编码列表中选择一个编码。See Choosing Coding Systems for Output.
34.4.2. 邮件头编辑
邮件编辑模式提供了以下专用命令,用于跳转到指定的邮件头部字段,并自动补全邮件地址。
C-c C-f C-t- 跳转到「收件人(To)」头部 (
message-goto-to) 。 C-c C-f C-s- 跳转到「主题(Subject)」头部 (
message-goto-subject) 。 C-c C-f C-c- 跳转到「抄送(CC)」头部 (
message-goto-cc) 。 C-c C-f C-b- 跳转到「密送(BCC)」头部 (
message-goto-bcc) 。 C-c C-f C-r- 跳转到「回复地址(Reply-To)」头部 (
message-goto-reply-to) 。 C-c C-f C-f- 跳转到「邮件后续回复地址(Mail-Followup-To)」头部字段 (
message-goto-followup-to) 。 C-c C-f C-w- 添加新的「FCC」头部字段,支持文件名补全 (
message-goto-fcc) 。 C-c C-b- 跳转到邮件正文起始位置 (
message-goto-body) 。 TAB- 补全邮件地址 (
message-tab) 。
这些将光标定位到指定头部字段的命令,均以前缀键 C-c C-f 开头( 'C-f' 代表「字段 field」)。如果目标字段不存在,该命令会自动创建该字段(唯一例外是 'FCC' 头部,每次执行都会新建一个)。
命令 C-c C-b (message-goto-body) 会将光标移动到头部分隔线之后,即邮件正文的开头。
在编辑包含地址的头部字段(如 'To:' 、'CC:'、'BCC:')时,可按 TAB (message-tab) 补全地址。系统会通过多种方式尝试补全对应完整姓名,包括使用 EUDC 库(支持多种目录服务器协议,参见《Emacs 统一目录客户端》中的 EUDC 章节)。若上述方式失败,则会尝试将地址作为邮件别名进行展开(参见「邮件别名」)。如果光标位于不接收地址的头部字段,或在邮件正文中,按 TAB 则直接插入制表符。
34.4.3. 邮件引用
C-c C-y- 从邮件阅读器中提取选中的邮件,作为引用内容插入 (
message-yank-original) 。 C-c C-q- 对从其他邮件引用过来的每个段落进行自动换行排版 (
message-fill-yanked-message) 。
你可以使用命令 C-c C-y (message-yank-original) 来引用你正在回复的邮件。该命令会将原邮件内容插入到邮件编辑缓冲区中。只有当邮件编辑缓冲区是从 Emacs 内置的邮件阅读器(如 Rmail)中打开时,此命令才有效。
默认情况下,Emacs 会在引用内容的每一行前插入符号 '> ' ;这个前缀符号由变量 message-yank-prefix 指定。如果带前缀参数调用 message-yank-original ,则不会插入引用前缀。
使用 C-c C-y 之后,你可以输入 C-c C-q (message-fill-yanked-message) 对引用邮件的段落进行自动排版。执行一次 C-c C-q 就会对所有引用段落分别进行排版。如果只想对某一段引用内容排版,可使用 M-q 。如果自动排版无法正确处理你使用的引用前缀,可以尝试显式设置填充前缀。参见「文本填充」。
你可以通过钩子 mail-citation-hook 来自定义邮件引用方式。例如,可以使用 Supercite 扩展包,它提供更灵活的引用格式(参见 Supercite 中的「简介」章节)
34.4.4. 邮件杂项功能
在邮件编辑缓冲区中,输入 C-c C-a (mml-attach-file) 可以为待发送邮件 attach添加 附件 。添加附件遵循多用途互联网邮件扩展(MIME)标准。
mml-attach-file 命令会提示你输入文件名、附件的内容类型、 description描述 和 disposition处理方式 。 内容类型 通常会自动检测,直接按 RET 接受默认值即可。 描述 是一段单行文本,收件人会在附件旁看到这段文字;你也可以选择留空。 disposition处理方式 有两种:
- '
inline' :收件人会在邮件正文内看到附件链接 - '
attachment' :附件链接与正文分开显示
mml-attach-file 是 Message 模式专用命令;在 Mail mode下请改用 mail-add-attachment 。该命令仅提示输入文件名,并自动判断内容类型与处理方式。如果你想为附件添加描述,可以直接写在邮件正文中。
附件的实际内容 不会 插入到邮件缓冲区中,而是插入一段占位文本,示例如下:
<#part type="text/plain" filename="~/foo.txt" disposition=inline> <#/part>
当你输入 C-c C-c 或 C-c C-s 发送邮件时,附件会随邮件一同发送。
撰写邮件时,输入 M-x ispell-message 可以对邮件文本进行拼写检查。如果你将收到的邮件引用到草稿中,该命令会跳过引用内容,只检查你自己输入的文本(它通过缩进或 mail-yank-prefix 区分引用行和你输入的内容)。参见「拼写检查与更正」。
启用 Message 模式( C-x m 会自动启用)会运行常规钩子 text-mode-hook 和 message-mode-hook 。初始化一封新的待发邮件会运行常规钩子 message-setup-hook ;如果你需要修改邮件缓冲区的外观,可以使用这个钩子。参见「钩子」。
这些钩子的主要区别在于触发时机:
- 每当你输入
C-x m,message-mode-hook会在邮件缓冲区创建后立即运行。 - 之后
message-setup函数会插入缓冲区默认内容。 - 默认内容插入完成后,
message-setup-hook才会运行。
如果你使用 C-x m 继续编辑已有的草稿, message-mode-hook 会在切换到邮件缓冲区后立即运行。如果缓冲区未被修改,或你选择清空并重写, message-setup-hook 会在默认内容插入后运行。
34.5. 邮件签名
你可以在每封邮件末尾添加一段固定文本 —— 即 mail signature邮件签名 ,签名中可包含电话、地址等信息。变量 message-signature 用于控制 Emacs 如何处理邮件签名。
message-signature 的默认值为 t ,表示 Emacs 会从文件 ~/.signature 中读取你的邮件签名。如果该文件存在,其内容会自动插入到邮件缓冲区的末尾。你可以通过变量 message-signature-file 修改签名文件路径。
若将 message-signature 设置为一个 字符串 ,则该字符串会直接作为签名内容。
若将 message-signature 设置为 nil ,Emacs 不会自动插入签名。你可以在邮件缓冲区中按下 C-c C-w (message-insert-signature) 手动插入签名,Emacs 仍会从签名文件中读取内容。
如果你使用的是 Mail mode而非 Message 模式撰写邮件,对应的控制变量为 mail-signature 和 mail-signature-file 。
按照惯例,邮件签名应以一行 '– ' 开头。如果你的签名没有这行前缀,Emacs 会自动补上。签名的其余内容建议不超过四行。
34.6. 邮件娱乐功能
M-x spook 会往待发送的邮件里添加一行随机选取的关键词。这些关键词选自一份词表,会让人觉得你在讨论某些敏感、“颠覆性” 的话题。
这个功能的初衷是:有人怀疑美国国家安全局(NSA19)及其他情报机构会监听所有包含 “敏感关键词” 的电子邮件。(这些机构声称并不会这么做,但他们本来就会这么说。)设计思路是:如果大量用户都在邮件里加入可疑词汇,情报机构就会被大量虚假信息淹没,从而不得不放弃全部审阅。不管这是否真的有效,至少能博一些人一笑。
你可以使用 fortune 程序,在发出的邮件里插入一段类似 “幸运签语” 的文字。做法是把 fortune-to-signature 添加到 mail-setup-hook 中:
(add-hook 'mail-setup-hook 'fortune-to-signature)
使用之前,你可能还需要先设置变量 fortune-file 。
34.7. 邮件撰写方式
本章中我们介绍了 Emacs 里用于编辑和发送邮件的默认模式 ——Message 模式。但它只是多种可用模式中的一种。在 Emacs 23.2 之前,默认模式是 Mail 模式。它在很多方面与 Message 模式相似,但功能更少:例如它只支持基础的 MIME 功能,可以添加附件,但缺少更复杂的 MIME 特性。另一种可用模式是 MH-E(参见《Emacs MH 接口》中的 MH-E 章节)。
你可以任选其中一种 mail user agents邮件客户端 ,作为编辑和发送邮件的 首选方式 。命令 C-x m 、 C-x 4 m 和 C-x 5 m 都会使用你指定的工具;Emacs 中其他发送邮件的功能(见 错误报告)也是如此。
要指定邮件客户端,只需自定义变量 mail-user-agent 。目前合法的值包括:
message-user-agent(Message 模式)sendmail-user-agent(Mail 模式)gnus-user-agentmh-e-user-agent
可能还有其他可选值,详情可查看对应邮件客户端包的手册。你也可以用 define-mail-user-agent 自定义一个邮件客户端。
如果你选择了其他邮件撰写方式,本章中关于邮件缓冲区和 Message 模式的内容将 不再适用 :其他方式会在不同缓冲区中使用不同的文本格式,快捷键命令也不一样。
类似地,若要指定 首选的邮件阅读 方式,可以自定义变量 read-mail-command 。其默认值为 rmail (参见《使用 Rmail 阅读邮件》)。
35. 使用 Rmail 阅读邮件
Rmail 是 Emacs 中的一个子系统,用于阅读和处理收到的邮件。Rmail 将邮件消息存储在称为 Rmail 文件 的文件中。在 Rmail 文件中阅读邮件会在一个专用的主模式 ——Rmail 模式下进行,该模式重新定义了大部分按键,用于执行邮件管理相关命令。
Emacs 还自带一个功能更强大、更灵活的邮件阅读子系统,名为 Gnus。Gnus 是一个非常庞大的软件包,因此在独立的手册中说明,详见《Gnus 新闻阅读器》。
35.1. Rmail 基本概念
最简单的 Rmail 使用方式是:只有一个 Rmail 文件 ~/RMAIL ,所有邮件都保存在这里,它被称为 primary Rmail file(主 Rmail 文件) 。命令 M-x rmail 会读取你的主 Rmail 文件,从收件箱合并新邮件,显示你尚未阅读的第一封邮件,并让你开始阅读。变量 rmail-file-name 用于指定主 Rmail 文件的文件名。
Rmail 一次只显示 Rmail 文件中的 一封邮件 ,当前显示的邮件称为 current message当前邮件 。Rmail 模式的专用命令可以执行如下操作:删除当前邮件、将其复制到另一个文件、发送回复,或切换到另一封邮件。你也可以创建多个 Rmail 文件(参见文件处理),并使用 Rmail 在它们之间移动邮件(参见将邮件复制到文件)。
在 Rmail 文件中,邮件通常按 接收顺序 依次排列;你也可以指定其他排序方式(参见对 Rmail 文件排序)。邮件用连续整数标识,即 message numbers邮件编号 。当前邮件的编号会显示在 Rmail 的模式行上,后面跟着文件中的邮件总数。你可以使用 j 键指定邮件编号来跳转到该邮件(参见在邮件间切换)。
按照 Emacs 的常规规则,对 Rmail 文件的修改只有在 保存文件 后才会永久生效。你可以用 s (rmail-expunge-and-save) 保存,它会先从文件中彻底清除已删除的邮件(参见删除邮件)。若要 不清除已删邮件 直接保存,使用 C-x C-s 。Rmail 在从收件箱文件合并新邮件后会 自动保存 Rmail 文件(参见 Rmail 文件与收件箱)。
你可以用 q (rmail-quit) 退出 Rmail;它会先清除已删邮件并保存 Rmail 文件,然后隐藏 Rmail 缓冲区及其摘要缓冲区(如果有)(参见摘要)。但不必正式退出。如果你从 Rmail 切换到其他缓冲区编辑且不再回来,就相当于退出了。只要最终记得保存 Rmail 文件即可(和其他修改过的文件一样)。 C-x s 是合适的保存方式(参见文件保存命令)。Rmail 命令 b (rmail-bury) 可以隐藏 Rmail 缓冲区及其摘要, 不清除、不保存 Rmail 文件。
35.2. 邮件内滚动
当 Rmail 显示的邮件长度超过一屏时,你需要滚动屏幕来阅读剩余内容。虽然可以使用常规滚动命令: C-v 、 M-v 和 M-< (参见滚动) ,但在 Rmail 中滚动操作非常频繁,因此提供了更简便的按键。
SPC- 向前滚动一屏 (
scroll-up-command) 。 DELS-SPC- 向后滚动一屏 (
scroll-down-command) 。 .- 滚动到当前邮件开头 (
rmail-beginning-of-message) 。 /- 滚动到当前邮件末尾 (
rmail-end-of-message) 。
由于阅读邮件时最常见的操作是按屏滚动,Rmail 将 SPC 和 DEL (或 S-SPC ) 分别设为与 C-v 、 M-v 功能相同。
命令 . (rmail-beginning-of-message) 会滚动回当前选中邮件的开头。它与 M-< 不完全相同:一方面,它不会设置标记;另一方面,如果你之前修改过当前邮件的缓冲区边界 (例如通过编辑操作,参见在邮件内编辑) ,它会重置这些边界。
同理,命令 / (rmail-end-of-message) 会向前滚动到当前选中邮件的末尾
35.3. 邮件间移动
对消息最基本的操作就是阅读。在 Rmail 中,这需要先将该消息设为当前消息。通常的做法是按文件中的顺序依次移动,这也是消息的接收顺序。进入 Rmail 时,光标会定位在你尚未设为当前消息的第一条(即带有「unseen」属性的第一条消息;参见 Rmail 属性)。向后移动可查看其他新消息;向前移动可重新查看旧消息。
n- 跳转到下一条未被删除的消息,跳过中间已删除的消息 (
rmail-next-undeleted-message) 。 p- 跳转到上一条未被删除的消息 (
rmail-previous-undeleted-message) 。 M-n- 跳转到下一条消息(包含已删除消息) (
rmail-next-message) 。 M-p- 跳转到上一条消息(包含已删除消息) (
rmail-previous-message) 。 C-c C-n- 跳转到与当前消息主题相同的下一条消息 (
rmail-next-same-subject) 。 C-c C-p- 跳转到与当前消息主题相同的上一条消息 (
rmail-previous-same-subject) 。 j- 跳转到第一条消息。带参数 n 时,跳转到编号为 n 的消息 (
rmail-show-message) 。 >- 跳转到最后一条消息 (
rmail-last-message) 。 <- 跳转到第一条消息 (
rmail-first-message) 。 M-s regexp RET- 跳转到下一条匹配该正则表达式的消息 (
rmail-search) 。 - M-s regexp RET- 跳转到上一条匹配该正则表达式的消息。(这是带负参数的
M-s。)
在 Rmail 中, n 和 p 是最常用的消息间移动方式。它们会按顺序遍历消息,但跳过已删除消息,这通常符合使用习惯。对应的命令名为 rmail-next-undeleted-message 和 rmail-previous-undeleted-message 。如果你不想跳过已删除消息(例如要去恢复某条消息),可以使用变体命令 M-n 和 M-p (rmail-next-message 和 rmail-previous-message) 。这些命令都可以接受数字参数作为重复次数。
在 Rmail 中,直接输入数字即可指定数字参数,无需先按 C-u 。输入 '-' 即可指定负参数。
M-s (rmail-search) 是 Rmail 专用的搜索命令。普通的增量搜索 C-s 在 Rmail 中也可用,但它只在当前消息内搜索。 M-s 的作用是在所有消息之间搜索。它会非增量地读取一个正则表达式(参见正则表达式语法),然后从下一条消息的开头开始查找匹配项,并选中该消息。如果正则表达式为空, M-s 会复用上一次使用的表达式。
要反向在文件中搜索消息,给 M-s 一个负参数即可。在 Rmail 中可直接用 - M-s ,它会从上一条消息的末尾开始向前搜索。
你也可以根据标签搜索消息,参见「标签」一节。
C-c C-n (rmail-next-same-subject) 会跳转到与当前消息主题相同的下一条消息。前缀参数为重复次数。带负参数时,该命令会向前跳转,效果等同于 C-c C-p (rmail-previous-same-subject) 。在比较主题时,这些命令会忽略回复邮件主题中常见的前缀(如 'Re:' )。它们非常适合阅读同一主题的所有消息,也就是对话串 / 线程。
要跳转到指定绝对编号的消息,使用 j (rmail-show-message) 并将消息编号作为参数。不带参数时, j 选中第一条消息。 < (rmail-first-message) 同样选中第一条消息. > (rmail-last-message) 选中最后一条消息。
35.4. 删除邮件
当你不再需要保留某条消息时,可以将其 delete删除 。这会将它 标记 为可忽略,部分 Rmail 命令会当作它不存在;但它在 Rmail 文件中的位置和消息编号仍然保留。
压缩(Expunging) Rmail 文件会真正移除已删除的消息。剩余的消息会重新连续编号。
d- 删除当前消息,并跳转到下一条未删除的消息 (
rmail-delete-forward) 。 C-d- 删除当前消息,并跳转到上一条未删除的消息 (
rmail-delete-backward) 。 u- 恢复当前消息,或回退到上一条已删除消息并将其恢复 (
rmail-undelete-previous-message) 。 x- 压缩 Rmail 文件(真正删除已标记删除的消息) (
rmail-expunge) 。
Rmail 有两条删除消息的命令,两者都会删除当前消息并选中另一条。 d (rmail-delete-forward) 会向后跳转,跳过已删除消息;而 C-d (rmail-delete-backward) 会跳转到上一条未删除消息。如果在指定方向上没有未删除消息可跳转,刚被删除的消息会保持为当前消息。数字前缀参数可作为 重复次数 ,允许单条命令一次性删除多条消息。负参数会反转 d 和 C-d 的行为方向。
每当 Rmail 删除一条消息时,都会运行钩子 rmail-delete-message-hook 。钩子函数被调用时,该消息已被标记为删除,但它仍是 Rmail 缓冲区中的当前消息。
要让所有已删除消息 真正从 Rmail 文件中消失 ,输入 x (rmail-expunge) 。在此之前,你仍然可以恢复已删除消息。恢复命令 u (rmail-undelete-previous-message) 在大多数情况下用于取消 d 命令的效果。如果当前消息已删除,它会恢复当前消息;否则它会向前回退,直到找到一条已删除消息并将其恢复。数字前缀参数可作为重复次数,允许单条命令一次性恢复多条消息。
通常你可以用 u 撤销 d ,因为 u 会回退并恢复 d 刚删除的那条消息。但当 d 跳过了其后若干条已删除消息时,这一行为会失效;此时 u 会恢复最后一条被跳过的已删除消息。没有完美的方法避免这个问题。不过,重复执行 u 命令,最终总能回到你想要恢复的那条消息。你也可以用 M-p 命令选中某条特定的已删除消息,然后输入 u 恢复它。
已删除消息带有 'deleted' 属性,因此当当前消息被删除时,模式行上会显示 'deleted' 。实际上,删除或恢复消息只不过是 添加或移除这个属性 而已。参见 Rmail 属性。
35.5. Rmail 文件与收件箱
当你在本地接收邮件时,操作系统会将发给你的 incoming 邮件存放在一个文件中,我们称之为你的收件箱(inbox)。启动 Rmail 时,它会运行一个名为 movemail 的 C 程序,把新邮件从收件箱复制到你的 主 Rmail 文件 中,该文件还保存着之前 Rmail 会话里留存的其他邮件。你实际在 Rmail 中阅读的,正是这个文件里的邮件。这一操作叫作 getting new mail收取新邮件 。在 Rmail 中随时按 g 都可以收取新邮件。
变量 rmail-primary-inbox-list 存放着主 Rmail 文件对应的收件箱文件列表。如果你没有显式设置这个变量,Rmail 会使用环境变量 MAIL ;作为最后备选,则会使用基于 rmail-spool-directory 的默认收件箱。默认收件箱文件因操作系统而异,通常是: /var/mail/用户名 、 /var/spool/mail/用户名 或 /usr/spool/mail/用户名 。
你可以用命令 set-rmail-inbox-list 为当前会话中的任意 Rmail 文件指定收件箱文件,详见「多个 Rmail 文件」一节。
将 Rmail 文件与收件箱分开,有两个原因:
- 收件箱文件格式在不同操作系统、不同邮件软件中各不相同。Rmail 只需其中一部分模块了解这些差异,并且只需要知道如何把它们统一转换成 Rmail 自身格式即可。
- 在不丢失邮件的前提下直接访问收件箱文件非常麻烦,因为必须与邮件投递过程进行互斥(interlock)。而且不同系统使用的互斥方式不同。把邮件一次性从收件箱移到独立的 Rmail 文件中,Rmail 其余部分就不再需要互斥处理,因为只有 Rmail 会操作这个 Rmail 文件。
Rmail 采用 Unix 和 GNU 系统用于收件箱的标准 'mbox' 格式 作为其内部文件格式。(实际上 mbox 有几种略有差异的版本,差别不大。你可以设置变量 rmail-mbox-format 告诉 Rmail 系统使用的是哪种格式,详见该变量的文档。)
收取新邮件时,Rmail 会先把新邮件从收件箱复制到 Rmail 文件,然后保存 Rmail 文件,最后清空收件箱。这样一来,即使系统崩溃,最多只会在收件箱和 Rmail 文件中出现重复邮件, 不会丢失邮件 。
如果 rmail-preserve-inbox 不为 nil,Rmail 收取新邮件时 不会清空收件箱 。你可能需要开启这个选项,例如在便携机上通过 POP 查收邮件时,让邮件保留在服务器上,之后再在主力台式机上保存。
某些情况下,Rmail 会 间接 从收件箱复制新邮件:先运行 movemail 程序,把邮件从收件箱移到一个中间文件,文件名为 .newmail-收件箱名 ,存放在 Rmail 文件所在目录。然后 Rmail 从该文件合并新邮件、保存 Rmail 文件,之后才删除中间文件。如果恰好在此时崩溃,该文件会保留下来,下次从该收件箱收信时 Rmail 会再次使用它。
如果 Rmail 无法把 .newmail-xxx 中的数据转换成 mbox 格式,会把文件重命名为 RMAILOSE.n (n 为保证唯一的整数),避免再次出错。你需要查看该文件,找到让 Rmail 无法解析的消息(通常是包含了控制下划线字符,八进制码 037)并删除,然后用 1 g 从修正后的文件重新收信。
35.6. 多个 Rmail 文件
Rmail 默认操作你的 主 Rmail 文件 ,该文件名为 ~/RMAIL ,并从系统收件箱文件接收新邮件。但你也可以创建其他 Rmail 文件,并用 Rmail 编辑它们。这些文件可以通过各自的收件箱接收邮件,也可以通过显式的 Rmail 命令把消息移入其中(参见 “将消息复制到文件”)。
i file RET- 读取文件到 Emacs 并在其上运行 Rmail (
rmail-input) 。 g- 从当前 Rmail 文件对应的收件箱合并新邮件 (
rmail-get-new-mail) 。 C-u g file RET- 从指定的收件箱文件合并新邮件。
要在非主 Rmail 文件上运行 Rmail,可以在 Rmail 中使用 i (rmail-input) 命令。该命令会以 Rmail 模式打开该文件。即使不在 Rmail 中,也可以使用 M-x rmail-input ;更简便的方式是输入 C-u M-x rmail ,效果相同。
用 i 读取的文件通常应为合法的 mbox 文件。如果不是,Rmail 会尝试将其文本转换为 mbox 格式,并在缓冲区中打开转换后的文本。保存缓冲区时,文件也会被转换。
如果指定的文件名不存在, i 会初始化一个新缓冲区,用于创建新的 Rmail 文件。
你也可以从菜单中选择 Rmail 文件:在 Classify 菜单中选择 'Input Rmail File' ,然后选中想要的 Rmail 文件。
变量 rmail-secondary-file-directory 和 rmail-secondary-file-regexp 用于指定菜单中显示哪些文件:
- 第一个变量指定存放这些文件的目录;
- 第二个变量指定该目录下哪些文件会被列出(所有匹配该正则表达式的文件)。
如果没有匹配的文件,则无法选中该菜单项。这些变量同样适用于选择输出文件(参见 “将消息复制到文件”)。
要使用的收件箱文件由变量 rmail-inbox-list 指定,该变量在 Rmail 模式下是 缓冲区局部变量 。一个特殊例外:如果未为主 Rmail 文件指定任何收件箱文件,它会使用环境变量 MAIL ,或系统标准收件箱。
g 命令 (rmail-get-new-mail) 会从当前 Rmail 文件的收件箱将邮件合并进来。如果该 Rmail 文件没有收件箱, g 不执行任何操作。命令 M-x rmail 同样会将新邮件合并到你的主 Rmail 文件。
要从 非常规收件箱 的文件中合并邮件,给 g 一个数字参数,例如 C-u g 。然后它会读取一个文件名,并从该文件合并邮件。使用带参数的 g 时,收件箱文件 不会被删除或修改 。因此,这是将一个消息文件合并到另一个文件的通用方法。
35.7. 将邮件复制到外部文件
这些命令用于将邮件从 Rmail 文件 复制 到另一个文件。
o file RET- 将当前邮件的完整副本追加到指定文件 (
rmail-output) 。 C-o file RET- 将当前邮件按显示状态的副本追加到指定文件 (
rmail-output-as-seen) 。 w file RET- 仅将邮件正文输出到指定文件,默认文件名取自邮件的「Subject」标题。
命令 o 和 C-o 会将当前邮件复制到指定文件,并追加在文件末尾。正的前缀参数表示重复次数:从当前邮件开始,连续指定数量的邮件会被复制到文件,已删除邮件会被忽略。
这两个命令的主要区别在于复制内容:
o复制完整邮件头,即使部分标题并未显示;C-o只复制当前实际显示的标题。详见 “邮件显示” 一节。
此外,如果输出文件是 Babyl 格式(Emacs 22 及更早版本 Rmail 使用的格式), o 会将邮件转为 Babyl 格式;而 C-o 完全不支持输出到 Babyl 文件。
如果输出文件当前已在 Emacs 缓冲区中打开,输出命令会将邮件追加到该缓冲区,最终需要你手动保存缓冲区到文件。
有时你收到的邮件正文本身就是一个文件内容。可以用 w 命令 (rmail-output-body-to-file) 将正文单独保存到文件(不含邮件头)。这类邮件通常在「Subject」字段中包含了预期的文件名,因此 w 命令会用「Subject」字段作为输出文件的默认名(会替换掉文件名中不可跨平台使用的字符)。文件名通过小缓冲区读取,你可以随时指定其他名称。
你也可以通过菜单将邮件输出到 Rmail 文件:在 Classify 菜单中选择 'Output Rmail File' ,然后选择目标 Rmail 文件。效果与 o 命令一样,将当前邮件输出到该文件。
变量 rmail-secondary-file-directory 和 rmail-secondary-file-regexp 用于控制菜单显示哪些文件:前者指定目录,后者指定该目录下哪些文件会被列出(匹配正则表达式的文件)。无匹配文件时该菜单项不可用。
使用 o 或 C-o 复制邮件后,原邮件会被打上 'filed' 属性,当该邮件为当前邮件时,模式行会显示 'filed' 。
如果你希望每份邮件只保留一份副本,可以将变量 rmail-delete-after-output 设为 t ;这样 o、C-o、w 命令在复制完成后会自动删除原邮件。(之后可随时恢复删除,见 “删除邮件” 一节。)
默认情况下, o 输出邮件时会保留原邮件的删除状态;即输出前已删除的邮件,在输出文件中也会标记为已删除。将变量 rmail-output-reset-deleted-flag 设为非 nil 可取消这一行为:输出的副本会 清除删除状态 ,在输出文件中显示为未删除。此外,当该变量非 nil 时,给 o 指定正参数时,在连续输出时不会忽略已删除邮件。
变量 rmail-output-file-alist 允许你根据当前邮件内容,智能设置输出文件的默认名。其值为一个列表,元素格式如下:
(regexp . name-exp)
如果当前邮件匹配正则表达式 regexp,输出默认文件名就是 name-exp 。多个元素匹配时, 第一个匹配项 生效。 name-exp 可以是直接字符串,也可以是任意能返回文件名的 Lisp 表达式。 rmail-output-file-alist 对 o 和 C-o 都生效。
Rmail 可以根据变量 rmail-automatic-folder-directives ,将主 Rmail 文件(由 rmail-file-name 指定)中的邮件自动保存到其他文件。该变量是一组 “规则”,指明哪些邮件保存到哪里。每条规则是一个列表:第一个元素是输出文件,后面是一个或多个「标题名 + 正则表达式」对。只要邮件标题匹配对应正则表达式,就会被保存到指定文件。多条标题规则时需要 全部匹配 。Rmail 在显示主 Rmail 文件的邮件时检查规则,并应用第一条匹配项。如果输出文件为 nil ,邮件会被直接删除而不保存。例如,你可以用此功能将来自特定发件人、或特定主题的邮件自动归档到专用文件。
35.8. 标签
每封邮件都可以被赋予多种标签,用于分类。每个标签都有名称;不同名称即为不同标签。对于某一封邮件,任意标签要么存在,要么不存在。有少数标签名称具有标准含义,会在合适时由 Rmail 自动赋予邮件,这些特殊标签称为 attributes属性 。(见 Rmail 属性。)其余所有标签只能由用户手动赋予。
a label RET- 为当前邮件添加指定标签 (
rmail-add-label) 。 k label RET- 从当前邮件移除指定标签 (
rmail-kill-label) 。 C-M-n label RET- 跳转到下一条带有指定标签中任意一个的邮件 (
rmail-next-labeled-message) 。 C-M-p labels RET- 跳转到上一条带有指定标签中任意一个的邮件 (
rmail-previous-labeled-message) 。 l labels RET- C-M-l labels RET- 为所有带有指定标签中任意一个的邮件生成摘要 (
rmail-summary-by-labels) 。
a (rmail-add-label) 和 k (rmail-kill-label) 命令允许你为当前邮件添加或移除任意标签。如果标签参数为空,则表示添加或移除 最近一次 使用过的标签。
在你按需要给邮件打上标签分类后,有三种使用标签的方式:跳转邮件、生成摘要、排序。
C-M-n labels RET (rmail-next-labeled-message) 跳转到下一条带有指定标签中任意一个的邮件。参数 labels 可以是一个或多个标签名,用逗号分隔。 C-M-p (rmail-previous-labeled-message) 用法类似,只是向前跳转。这两个命令都可以使用数字参数作为跳转次数。
C-M-l labels RET (rmail-summary-by-labels) 命令只显示 至少带有指定标签中 一个的邮件摘要。参数 labels 为一个或多个标签名,用逗号分隔。关于摘要的更多信息,见 “摘要” 一节。
如果给 C-M-n 、 C-M-p 或 C-M-l 的标签参数为空,则表示 复用最近一次 这些命令用过的标签组。
关于使用标签对 Rmail 文件中的邮件排序,见 “对 Rmail 文件排序” 一节。
35.9. Rmail 属性
有些标签(如 'deleted' 和 'filed' )具有内置含义,Rmail 会在合适时机自动把它们赋给邮件;这类标签称为属性。以下是 Rmail 所有属性列表:
- '
unseen' - 表示该邮件从未被查看过。邮件从收件箱文件导入时会被加上该属性,在邮件被设为当前邮件时移除。启动 Rmail 时,默认显示第一个带有此属性的邮件。
- '
deleted' - 表示该邮件已被标记删除。由删除命令添加,由恢复删除命令移除(见 “删除消息”)。
- '
filed' - 表示该邮件已被归档复制到其他文件。由
o和C-o输出命令自动添加(见 “将邮件复制输出到文件”)。 - '
answered' - 表示你已回复过该邮件。由
r命令 (rmail-reply) 添加。见 “发送回复”。 - '
forwarded' - 表示你已转发过该邮件。由
f命令 (rmail-forward) 添加。见 “发送回复”。 - '
edited' - 表示你已在 Rmail 内部编辑过该邮件的文本。见 “在邮件内编辑”。
- '
resent' - 表示你已重发过该邮件。由命令
M-x rmail-resend添加。见 “发送回复”。 - '
retried' - 表示你已重试发送过一封失败的外发邮件。由命令
M-x rmail-retry-failure添加。见 “发送回复”。
所有其他标签都只能由用户手动添加或删除,没有预定义含义。
35.10. 发送回复
Rmail 提供多条命令用于发送外发邮件。关于使用 Message 模式(包括一些专门配合 Rmail 使用的功能),详见 “发送邮件” 一节。本节主要记录 Rmail 中用于进入邮件缓冲区以撰写外发邮件的专用命令。注意:常规发送邮件的快捷键 C-x m 、 C-x 4 m 、 C-x 5 m 在 Rmail 模式下同样正常可用。
m- 撰写一封新邮件 (
rmail-mail) 。 c- 继续编辑已开始撰写的外发邮件 (
rmail-continue)。 r- 回复当前 Rmail 邮件 (
rmail-reply)。 f- 将当前邮件转发给其他用户 (
rmail-forward)。 C-u f- 重发当前邮件给其他用户 (
rmail-resend)。 M-m- 重试发送被退回的邮件 (
rmail-retry-failure)。
在 Rmail 中发送邮件最常见的场景,是回复正在阅读的邮件。为此,输入 r (rmail-reply)。它会在另一个窗口中显示邮件撰写缓冲区,用法类似 C-x 4 m ,但会根据你正在回复的邮件 自动预填 以下邮件头字段:Subject、To、CC、In-Reply-To、References。
- To 字段初始为原邮件发件人地址。
- CC 字段初始为原邮件的所有其他收件人。
你可以使用变量 mail-dont-reply-to-names ,将某些收件人自动排除在回复之外。它的值应为一个正则表达式;任何匹配的收件人都会从 'CC' 字段中排除。它们也会从 'To' 字段排除,除非这样会使 'To' 为空。如果该变量为 nil ,则第一次撰写回复时会被初始化为一个匹配你自己地址的默认值。
如果只想仅回复给原发件人,可以给回复命令带上数字参数: C-u r 或 1 r 。这会在本次回复中完全省略 'CC' 字段。
邮件撰写缓冲区初始化完成后,编辑与发送邮件的操作与平常一致(见 “发送邮件”)。你可以修改预设的邮件头字段,也可以使用如 C-c C-y 这样的命令插入正在回复的原邮件内容(见 “邮件命令”)。你也可以切回 Rmail 缓冲区,选中另一封邮件,再切回撰写缓冲区插入新的当前邮件。
有时邮件无法送达目的地,邮件系统通常会将失败邮件退回给你,并包含在一封退信中。Rmail 命令 M-m (rmail-retry-failure) 会准备重新发送同一封邮件:它会创建一个与之前文本、头字段完全相同的撰写缓冲区。如果直接输入 C-c C-c ,就会和第一次一模一样地重发;你也可以先编辑正文或头部再发送。
变量 rmail-retry-ignored-headers (格式与 rmail-ignored-headers 相同,见 “邮件显示”)用于控制在重试发送时,从失败邮件中 剔除哪些头部字段 。
在 Rmail 中发送邮件的另一常见场景是转发当前邮件给他人。 f (rmail-forward) 简化了这一操作:自动在邮件撰写缓冲区中填入当前邮件作为正文,并生成形如 [from: subject] 的主题。你只需填写收件人并发送即可。转发邮件时,收件人会看到发件人是你,原邮件作为内容包含在内。
Rmail 提供两种转发邮件格式:
- 默认使用 MIME 格式(见 “邮件显示”),将原邮件作为独立附件。
- 若将变量
rmail-enable-mime-composing设为nil,则使用更简单的格式:
原邮件直接包裹在分隔行之间,并对所有以短横线开头的行自动在行首插入 '- ' ,避免格式冲突。
当你收到这种格式的转发邮件时(例如里面包含代码等非纯文本内容),可以撤销该转换:选中这封转发邮件,执行 M-x unforward-rmail-message 。该命令会提取原始转发邮件,删除插入的 '- ' 字符串,并将其作为一封独立邮件插入到当前邮件之后。
重发(Resending) 是与转发类似的另一种方式,区别在于:重发的邮件发件人仍显示为原作者,就像邮件直接从对方发来一样,只额外添加 'Resent-From' 和 'Resent-To' 头部,表示经由你转发。在 Rmail 中重发邮件使用 C-u f 。( f 命令默认执行 rmail-forward ,带上数字参数时会调用 rmail-resend 。)
使用 m (rmail-mail) 命令开始撰写非回复类的新邮件,头部字段为空。它与 C-x 4 m 的唯一区别是:和 r 一样,允许在撰写时用 C-c C-y 从 Rmail 缓冲区插入内容。
c (rmail-continue) 命令用于继续编辑邮件撰写缓冲区,完成之前未写完的外发邮件,或修改已发送过的邮件。
如果将变量 rmail-mail-new-frame 设为非 nil,那么所有 Rmail 发信命令都会新建一个框架(frame)来编辑邮件,发送邮件后该框架会被删除(但如果是当前显示器上唯一可见框架,或文本模式框架,则不会删除)。如果无法删除,Emacs 会在下一次撰写邮件时复用该框架。
所有 Rmail 发送邮件的命令,都会使用你选定的邮件撰写方法(见 “邮件撰写方法”)。
35.11. 摘要
summary摘要 是一个缓冲区,其中每一行对应一封邮件,让你概览 Rmail 文件中的所有邮件。每一行会显示:邮件编号、日期、发件人、行数、标签以及主题。在摘要缓冲区中移动光标到某一行,就会选中对应的那封邮件。几乎所有 Rmail 命令在摘要缓冲区中同样有效;在摘要中使用时,这些命令会作用于当前行所对应的邮件。
一个摘要缓冲区只对应一个 Rmail 文件;如果你在编辑多个 Rmail 文件,每个文件都可以有自己的摘要缓冲区。摘要缓冲区的名称是在 Rmail 缓冲区名称后加上 '-summary' 。通常一次只显示一个摘要缓冲区。
35.11.1. 生成摘要
以下是为当前 Rmail 缓冲区创建摘要的命令。一旦 Rmail 缓冲区拥有了摘要,Rmail 缓冲区里的改动(如删除或清除邮件、收取新邮件)都会自动更新摘要。
hC-M-h- 为所有邮件生成摘要(rmail-summary)。
l labels RETC-M-l labels RET- 为带有指定标签中至少一个的邮件生成摘要 (
rmail-summary-by-labels)。 C-M-r rcpts RET- 为匹配指定收件人的邮件生成摘要 (
rmail-summary-by-recipients) 。 C-M-t topic RET- 为主题中匹配指定正则表达式的邮件生成摘要 (
rmail-summary-by-topic) 。 C-M-s regexp RET- 为邮件头匹配指定正则表达式的邮件生成摘要 (
rmail-summary-by-regexp) 。 C-M-f senders RET- 为匹配指定发件人的邮件生成摘要 (
rmail-summary-by-senders)。
h 或 C-M-h (rmail-summary) 命令会在当前 Rmail 缓冲区的摘要缓冲区中填入所有邮件的摘要,并在另一个窗口显示并选中该摘要缓冲区。
C-M-l labels RET (rmail-summary-by-labels) 只生成包含至少一个指定标签的邮件摘要。多个标签用逗号分隔。
C-M-r rcpts RET (rmail-summary-by-recipients) 只生成收件人匹配正则表达式的邮件摘要。匹配会检查 To、From 和 CC 头(使用前缀参数可排除 CC)。
C-M-t topic RET (rmail-summary-by-topic) 只生成主题匹配正则表达式的邮件摘要。使用前缀参数时,会匹配整个邮件而非仅主题。
C-M-s regexp RET (rmail-summary-by-regexp) 只生成邮件头(含日期与主题行)匹配正则表达式的邮件摘要。
C-M-f senders RET (rmail-summary-by-senders) 只生成 'From' 字段匹配正则表达式的邮件摘要。
注意:每个 Rmail 缓冲区只有一个摘要缓冲区;生成任何新摘要都会覆盖之前的摘要。
变量 rmail-summary-window-size 用于设置摘要窗口的行数。变量 rmail-summary-line-count-flag 控制摘要行是否显示邮件行数。将其设为 nil 可加快摘要生成速度。
35.11.2. 在摘要中编辑
你可以使用 Rmail 摘要缓冲区完成几乎所有在 Rmail 缓冲区本身能做的操作。实际上,一旦拥有摘要缓冲区,就无需切回 Rmail 缓冲区。
只需在摘要缓冲区中将光标移动到不同行,即可从中选择并显示 Rmail 缓冲区中的各类邮件。无论你使用何种 Emacs 命令移动光标,命令执行后光标所在的行,都会在 Rmail 缓冲区中选中对应邮件。
几乎所有 Rmail 命令在摘要缓冲区和 Rmail 缓冲区中都能生效。因此,在摘要缓冲区中按 d 删除当前邮件, u 取消删除, x 彻底清除。(但在摘要缓冲区中,若对应方向已无未删除邮件,删除命令会跳转到第一封或最后一封邮件,而非停留在当前邮件。) o 和 C-o 将当前邮件输出到文件; r 回复该邮件;等等。你可以在摘要缓冲区中使用 SPC 和 DEL 滚动当前邮件。不过,在摘要缓冲区中,用 SPC 或 DEL 滚动超出邮件末尾或开头时,会分别跳转到下一封或上一封未删除邮件。将 rmail-summary-scroll-between-messages 选项设为 nil 可禁用跳转到下一封 / 上一封邮件的功能。
M-u (rmail-summary-undelete-many) 恢复摘要中所有已删除邮件。带前缀参数表示恢复此前指定数量的已删除邮件。
用于在邮件间切换的 Rmail 命令在摘要缓冲区中同样有效,但有一个区别:它们只会遍历摘要中包含的邮件。这些命令还会确保 Rmail 缓冲区显示在屏幕上(不同于光标移动命令,后者仅更新 Rmail 缓冲区内容,若其未在窗口中显示则不会主动展示)。以下是这些命令列表:
n- 跳转到下一行(跳过标记为 “已删除” 的行)并选中对应邮件 (
rmail-summary-next-msg) 。 p- 跳转到上一行(跳过标记为 “已删除” 的行)并选中对应邮件 (
rmail-summary-previous-msg)。 M-n- 跳转到下一行并选中对应邮件 (
rmail-summary-next-all)。 M-p- 跳转到上一行并选中对应邮件 (
rmail-summary-previous-all)。 >- 跳转到最后一行并选中对应邮件 (
rmail-summary-last-message)。 <- 跳转到第一行并选中对应邮件 (
rmail-summary-first-message)。 jRET- 选中当前行对应的邮件(确保 Rmail 缓冲区显示在屏幕上;
rmail-summary-goto-msg)。带参数 n 则选中第 n 封邮件并跳转到其在摘要缓冲区的行;若该邮件未在摘要中列出,会报错。 M-s pattern RET- 从当前邮件开始搜索指定模式;选中找到的邮件,并将光标移到摘要缓冲区中该邮件的行 (
rmail-summary-search) 。前缀参数为重复次数;负数参数表示反向搜索(等价于rmail-summary-search-backward)。 C-M-n labels RET- 跳转到下一封带有至少一个指定标签的邮件 (
rmail-summary-next-labeled-message)。labels 为逗号分隔的标签列表。前缀参数为重复次数。 C-M-p labels RET- 跳转到上一封带有至少一个指定标签的邮件 (
rmail-summary-previous-labeled-message)。 C-c C-n RET- 跳转到下一封与当前邮件主题相同的邮件 (
rmail-summary-next-same-subject)。前缀参数为重复次数。 C-c C-p RET- 跳转到上一封与当前邮件主题相同的邮件 (
rmail-summary-previous-same-subject)。
在 Rmail 缓冲区中执行删除、取消删除、收取新邮件,甚至切换选中邮件时,都会自动更新摘要缓冲区。若变量 rmail-redisplay-summary 不为 nil,这些操作还会将摘要缓冲区重新显示在屏幕上。
使用完摘要后,按 Q (rmail-summary-wipe) 关闭摘要缓冲区窗口。你也可以在摘要界面退出 Rmail: q (rmail-summary-quit) 关闭摘要窗口,随后保存 Rmail 文件并切换到其他缓冲区,从而退出 Rmail。此外,按 b (rmail-summary-bury) 可直接隐藏 Rmail 摘要及缓冲区。
35.12. Rmail 文件排序
C-c C-s C-dM-x rmail-sort-by-date- 按日期对当前 Rmail 缓冲区的邮件排序。
C-c C-s C-sM-x rmail-sort-by-subject- 按主题对当前 Rmail 缓冲区的邮件排序。
C-c C-s C-aM-x rmail-sort-by-author- 按发件人姓名对当前 Rmail 缓冲区的邮件排序。
C-c C-s C-rM-x rmail-sort-by-recipient- 按收件人姓名对当前 Rmail 缓冲区的邮件排序。
C-c C-s C-cM-x rmail-sort-by-correspondent- 按通信对方姓名对当前 Rmail 缓冲区的邮件排序。
C-c C-s C-lM-x rmail-sort-by-lines- 按行数对当前 Rmail 缓冲区的邮件排序。
C-c C-s C-k RET labels RETM-x rmail-sort-by-labels RET labels RET- 按标签对当前 Rmail 缓冲区的邮件排序。参数 labels 应为逗号分隔的标签列表。这些标签的顺序决定邮件顺序:带有第一个标签的邮件排在最前,第二个标签次之,依此类推。不包含这些标签的邮件排在最后。
Rmail 的排序命令采用 stable sort稳定排序 :若两封邮件无明确排序优先级,它们的相对顺序保持不变。你可以利用这一点实现 多条件排序 。例如,先按日期排序 rmail-sort-by-date ,再按作者排序 rmail-sort-by-author ,同一作者的邮件就会按日期顺序排列。
所有这些命令带上前缀参数时,会反转排序顺序:即按最新到最旧、最大到最小,或逆字母序排列。
在摘要缓冲区中,相同快捷键执行类似功能;例如 C-c C-s C-l 对应 rmail-summary-sort-by-lines 。即使摘要只显示部分邮件,这些命令也始终对 整个 Rmail 缓冲区 排序。
注意: 排序操作无法撤销 ,因此建议在排序前先保存 Rmail 缓冲区。
35.13. 邮件显示
本节介绍 Rmail 如何显示邮件头、MIME 段落与附件、URL 以及加密邮件。
t- 切换显示完整邮件头 (
rmail-toggle-header) 。
在首次显示每封邮件前,Rmail 会重新格式化邮件头,隐藏无关的头字段以减少杂乱。 t (rmail-toggle-header) 命令可在格式化后的简化头与完整原始头之间切换。带正前缀参数时显示简化头;带 0 或负前缀参数时显示完整头。重新选中该邮件时也会按需重新格式化。
变量 rmail-ignored-headers 存放一个正则表达式,用于指定要隐藏的头字段;匹配的行会被隐藏。变量 rmail-nonignored-headers 会覆盖它:即使匹配前者,只要匹配后者就会显示。变量 rmail-displayed-headers 是另一种方案:若非 nil,它用正则表达式指定只显示哪些头(默认为 nil )。
Rmail 会高亮特别重要的头字段,默认是 'From' 和 'Subject' 字段。高亮使用 rmail-highlight 样式。变量 rmail-highlighted-headers 存放正则表达式,匹配开头的整个头字段会被高亮。要关闭此功能,将其设为 nil 。
如果邮件是 MIME(多用途互联网邮件扩展)格式并包含多个部分(MIME 实体),Rmail 会为每个部分显示一个标签行(tagline),汇总该部分的序号、大小和内容类型。根据内容类型不同,标签行还可能包含一个或多个按钮,用于执行保存到文件等操作。
RET- 隐藏或显示光标处的 MIME 部分 (
rmail-mime-toggle-hidden)。 TAB- 将光标移到下一个 MIME 标签行按钮 (
rmail-mime-next-item)。 S-TAB- 将光标移到上一个 MIME 部分 (
rmail-mime-previous-item)。 v- 在 MIME 显示与原始邮件之间切换 (
rmail-mime)。
每个纯文本 MIME 部分默认直接显示在标签行后,作为 Rmail 缓冲区的一部分(除非邮件包含 HTML 部分,见下文)。其他类型的 MIME 部分默认只显示标签行,内容隐藏。无论哪种情况,在该部分或其标签行(非按钮区域)按 RET 都可以切换显示 / 隐藏。按 RET (或鼠标点击)激活标签行按钮,用 TAB 在按钮间切换。
v (rmail-mime) 命令在上述默认 MIME 显示与 未解码的原始 MIME 数据 之间切换。带前缀参数时,只切换光标所在实体的显示。
如果邮件包含 HTML MIME 部分,且 Emacs 能渲染 HTML20,Rmail 优先显示 HTML 而非纯文本。要改为优先显示纯文本,将变量 rmail-mime-prefer-html 设为 nil 。
若要让 Rmail 不处理 MIME 解码,将 rmail-enable-mime 设为 nil 。此时 v (rmail-mime) 会创建临时缓冲区显示当前 MIME 邮件。
如果当前邮件是加密邮件,使用 C-c C-d (rmail-epa-decrypt) 解密,依赖 EasyPG 库(见《EasyPG 助手用户手册》中的 EasyPG)。
你可以通过 Goto Address 模式高亮并激活 Rmail 缓冲区中的 URL:
(add-hook 'rmail-show-message-hook 'goto-address-mode)
之后可用鼠标中键(或快速点击左键)浏览 URL,或将光标移至 URL 并按 C-c RET 。参见 “激活 URL”。
35.14. Rmail 与编码系统
Rmail 会自动解码包含非 ASCII 字符的邮件,就像 Emacs 处理你打开的文件和子进程输出一样。Rmail 会利用邮件中标准的 'charset=字符集' 头(如果存在),判断发件人对邮件的编码方式。它会将 charset字符集 映射到对应的 Emacs 编码系统(见 “编码系统”),并用该编码系统解码邮件文本。如果邮件头没有 'charset' 说明,或该字符集无法识别,Rmail 会使用 Emacs 常规的启发式规则与默认值选择编码系统(见 “识别编码系统”)。
偶尔会出现邮件解码错误的情况,原因可能是:在缺少 'charset' 说明时 Emacs 猜错了编码系统,或是该说明本身不准确。例如,配置错误的邮件客户端可能发出标有 'charset=iso-8859-1' 的邮件,但实际编码却是 koi8-r 。当你看到邮件文本乱码、部分字符显示为十六进制码或空白方框时,通常就是发生了这种问题。
如果你能判断或猜出正确的编码系统,可以用它重新解码邮件来修正问题。方法是执行命令 M-x rmail-redecode-body 。它会读取一个编码系统名称,然后用你指定的编码系统重新解码邮件。只要编码系统选对,邮件就会恢复正常可读。
在 Rmail 中收取新邮件时,每封邮件都会自动从其原始编码系统转换,就像它们是独立文件一样。这一过程使用你指定的编码系统优先级列表。如果 MIME 邮件指定了字符集,Rmail 会遵循该设置。对于 Rmail 文件本身的读取与保存,Emacs 使用变量 rmail-file-coding-system 指定的编码系统。其默认值为 nil ,表示 Rmail 文件不进行转换(直接以 Emacs 内部字符编码读写)。
35.15. 邮件内编辑
Rmail 模式下可以使用大部分常用的 Emacs 按键绑定,尽管少数按键(如 C-M-n 和 C-M-h )会被 Rmail 重新定义为其他用途。不过,Rmail 缓冲区默认是 只读 的,并且大部分字母键都被重新定义为 Rmail 命令。如果你想要编辑邮件正文,必须使用 Rmail 命令 e 。
e- 以普通文本形式编辑当前邮件。
e 命令 (rmail-edit-current-message) 会从 Rmail 模式切换到 Rmail 编辑模式,这是另一种主模式,与 Text 模式基本一致。模式行上会显示这个变化。
在 Rmail 编辑模式下,字母按键会像平常一样插入字符,Rmail 命令不再生效。你可以编辑邮件正文和头字段。编辑完成后,按 C-c C-c (rmail-cease-edit) 即可切回 Rmail 模式。另外,你也可以按 C-c C-] (rmail-abort-edit) 返回 Rmail 模式,并放弃本次所有编辑修改。
进入 Rmail 编辑模式时会先运行钩子 text-mode-hook ,然后运行 rmail-edit-mode-hook (见 Hooks)。如果在编辑模式中做了任何修改,切回普通 Rmail 模式后,邮件会被加上 'edited' (已编辑)属性(见 Rmail Attributes)。
35.16. 邮件摘要
合集邮件(digest message) 是一种用来容纳、承载多封其他邮件的邮件。合集邮件常用于某些邮件列表:在一天等一段时间内,发送到该列表的所有邮件会被打包进一封合集邮件,再发送给订阅者。虽然总大小相同,但传输一封合集邮件比单独传输每一封邮件更节省计算机资源,因为网络邮件传输中每封邮件都有固定开销。
当你收到一封合集邮件时,最方便的阅读方式是 undigestify将其拆封 :把它还原成多封独立邮件。之后你就可以按自己的需要阅读、删除这些单封邮件。
操作方法:选中该合集邮件,执行命令 M-x undigestify-rmail-message 。该命令会把其中的子邮件提取为独立的 Rmail 邮件,并插入在合集邮件之后。合集邮件本身会被标记为已删除。
35.17. 阅读 Rot13 加密邮件
某些可能冒犯或引起部分读者不适的邮件列表消息,会使用一种名为 rot13 的简单编码进行处理 —— 之所以叫这个名字,是因为它将字母表循环偏移 13 位。这种编码 并非用于保密 ,而是让不想看到原文的人可以选择不查看。例如,电影影评可能会用 rot13 隐藏关键剧情。
要查看使用 rot13 编码的缓冲区内容,使用命令 M-x rot13-other-window 。该命令会在另一个窗口中显示当前缓冲区,并在显示时自动解码。
如果你只想处理某一区域,可使用命令 M-x rot13-region 。它会原地对选中区域进行编码 / 解码。如果缓冲区是只读的,它会尝试在回显区显示明文;如果文本过长,超出回显区长度,命令会弹出一个临时缓冲区来显示编码 / 解码后的内容。
35.18. movemail 程序
Rmail 使用 movemail 程序将邮件从收件箱移动到你的 Rmail 文件(见 Rmail 文件与收件箱)。首次加载时,Rmail 会尝试查找 movemail 程序并检测其版本。
movemail 有两个版本:
- GNU Mailutils 版本(见 GNU Mailutils 手册中的 movemail)
- Emacs 专用版本:除非配置 Emacs 时使用了
--with-mailutils,否则会编译并安装此版本。
两个版本的 movemail 支持相同的命令行语法和基本选项集,但 Mailutils 版本提供更多功能且更安全。
Emacs 版 movemail 可以读取常见的 Unix 邮箱格式。 警告 :虽然它也支持 POP3 协议,但 不推荐使用 ,因为它不支持基于 TLS 加密的 POP3。
Mailutils 版 movemail 支持多种邮箱格式,如普通 Unix 邮箱、maildir、MH 等;可通过 POP3 或 IMAP4 协议访问远程邮箱,并能使用 TLS 加密通道收取邮件;同时支持以 URL 格式 指定邮箱。邮箱 URL 的详细说明见 GNU Mailutils 手册。
简单来说,URL 格式为:
proto://[user[:password]@]host-or-file-name[:port]
方括号内为可选部分。
proto- 指定邮箱协议或格式,后续 URL 元素的具体含义取决于 proto。
user- 访问远程邮箱的用户名。
password- 访问远程邮箱的密码。
host-or-file-name- 远程邮箱为服务器主机名,本地邮箱为文件路径。
port- 可选端口号,不写则使用协议默认端口。
proto 可取以下值:
mbox- 标准 Unix 邮箱格式。不使用 user、pass、port,host-or-file-name 为邮箱文件路径。示例:
mbox:///var/spool/mail/smith mh- MH 格式本地邮箱。不使用 user、pass、port,host-or-file-name 为 MH 文件夹路径。示例:
mh:///Mail/inbox maildir- Maildir 格式本地邮箱。不使用 user、pass、port,host-or-file-name 为邮箱目录路径。示例:
maildir:///mail/inbox file- 任意本地邮箱文件,
movemail会自动识别格式。 poppops- 使用 POP3 协议访问远程邮箱(pops 为加密版本)。详情见 “从远程邮箱收取邮件”。
imapimaps- 使用 IMAP4 协议访问远程邮箱(imaps 为加密版本)。详情见 “从远程邮箱收取邮件”。
你也可以直接指定邮箱文件路径,等价于使用 file 协议:
/var/spool/mail/user ≡ file:///var/spool/mail/user
变量 rmail-movemail-program 控制使用哪个版本的 movemail 。
- 若为字符串:指定 movemail 可执行文件的绝对路径。
- 若为
nil:Rmail 会依次在rmail-movemail-search-path、exec-path(见 Running Shell Commands from Emacs)、exec-directory中查找。该变量应在启动 Rmail 前配置;若启动后修改,需将rmail-movemail-variant-in-use设为nil并重启 Rmail。
35.19. 从远程邮箱获取邮件
有些站点使用名为 POP3 的方式来存取用户的收件箱数据,而不是将数据存放在收件箱文件中。Mailutils 版的 movemail 默认支持带 TLS 加密的 POP3。警告:虽然 Emacs 自带的 movemail 也支持 POP3,但 不推荐使用 ,因为它不支持加密连接,而 Mailutils 版本支持。两个版本的 movemail 都只支持 POP3,不支持更早版本的 POP 协议。
你可以使用 POP3 URL 来指定一个 POP3 收件箱(见 movemail 程序)。POP3 URL 格式为: 'pop://username@hostname:port
- hostname 和 port 是远程邮件服务器的主机名(或 IP 地址)与端口号。
- username 是该服务器上的用户名。
你还可以在邮箱 URL 中直接指定密码: 'pop://username:password@hostname:port'
- 这种情况下,URL 中的 password密码 会优先于 rmail-remote-password(见下文)。如果你有多个使用不同密码的远程邮箱,这会非常方便。
如果使用 Mailutils 版 movemail 且服务器支持加密连接,movemail 会尝试使用加密;用 'pops:' 代替 'pop:' 可以 强制 使用加密连接。
为了向后兼容,Rmail 还支持另一种指定远程 POP3 邮箱的方式:以 'po:username:hostname:port' 形式指定收件箱名,等价于 'pop://username@hostname:port' 。如果省略 :hostname ,则由环境变量 MAILHOST 指定 POP3 服务器所在主机。
另一种存取远程邮箱的方式是 IMAP。该方式 仅由 Mailutils 版 movemail 支持 ,使用 IMAP4 协议。在收件箱列表中指定 IMAP 邮箱的 URL 格式为: 'imap://username[:password]@hostname:port' 。其中 password密码 部分是可选的,规则同上。如果服务器支持, movemail 会尝试使用加密连接;使用 'imaps:' 可以强制加密。
访问远程邮箱通常需要密码。Rmail 按以下顺序获取密码:
- 如果邮箱 URL 中已包含密码(见上文),直接使用该密码。
- 如果变量
rmail-remote-password-required为nil,Rmail 认为不需要密码。 - 如果变量
rmail-remote-password非空,使用其值作为密码。 - 否则,Rmail 会提示你输入密码。
有些邮件服务器的用户名中包含域名信息,即用户名里会有 '@' 字符。而收件箱指定字符串用 '@' 表示服务器名的开始,这会让 movemail 产生混淆。如果你的用户名包含 '@' ,且使用 Mailutils 版 movemail,可以这样解决:将用户名中的 '@' 替换为 URL 编码形式 '%40' 。
如果你需要向 movemail 传递额外的命令行参数,可以将变量 rmail-movemail-flags 设置为你要使用的参数列表。 不要 用这个变量传递 '-p' 参数来保留收件箱内容,应使用 rmail-preserve-inbox 。
你所在环境安装的 movemail 可能支持 Kerberos 认证 。如果支持,当 rmail-remote-password 和 rmail-remote-password-required 均未设置时,在收取 POP3 邮件时会默认使用 Kerberos。
部分 POP3 服务器会以 逆序 存放邮件。如果你希望按邮件接收顺序阅读,可以在 rmail-movemail-flags 中加入 '-r' 参数,让 movemail 反转下载后的邮件顺序。
Mailutils 版 movemail 支持 TLS 加密。如果你想启用,可以在 rmail-movemail-flags 中加入 '--tls' 参数。
35.20. 从多种格式的本地邮箱获取邮件
如果你的收件邮件保存在本地机器上,但格式并非 Unix 邮箱格式,那么你需要使用 Mailutils 版的 movemail 来收取邮件。关于 movemail 不同版本的详细说明,请参见 movemail 程序一节。
例如,要访问位于 /var/spool/mail/in 目录下的 maildir 格式收件箱,你需要在 Rmail 收件箱列表中加入如下内容:
maildir:///var/spool/mail/in
36. 使用 Gnus 处理电子邮件与新闻组
Gnus 是一个 Emacs 包,最初主要用于阅读和发布 Usenet 新闻。它也可以用来阅读和回复来自其他多种来源的消息 —— 电子邮件、远程目录、合集邮件等。本章将对 Gnus 进行介绍,并描述几项基础功能。完整细节请参阅《Gnus 手册》中的 Gnus 相关内容。
36.1. Gnus 缓冲区
Gnus 使用多个缓冲区来展示信息并接收命令。其中最常用的三个 Gnus 缓冲区分别是 group buffer组缓冲区 、 summary buffer摘要缓冲区 和 article buffer文章缓冲区 。
组缓冲区里列出了各类文章来源(例如新闻组、电子邮箱收件箱),这些统称为组(groups)。这是 Gnus 启动后第一个显示的缓冲区。默认情况下,它只显示你已订阅且包含未读文章的组。你可以在这个缓冲区中选择要阅读的组。
摘要缓冲区会列出单个组里的所有文章,每行显示一篇。默认会展示文章的作者、主题和行号,这些显示格式均可自定义,详见《Gnus 手册》中的「摘要缓冲区格式」部分。当你在组缓冲区选中一个组时,会创建对应的摘要缓冲区;退出该组时,缓冲区会被关闭。
在摘要缓冲区中,你可以选择要查看的文章,文章内容会在文章缓冲区中显示。日常使用 Gnus 时,你只需查看这个缓冲区,无需主动选中它 —— 所有常用的 Gnus 命令都可以在摘要缓冲区中直接执行。当然,如果你需要,也可以手动选中文章缓冲区,并在其中执行 Gnus 命令。
36.2. Gnus 启动流程
如果你的系统已经配置好用于阅读 Usenet 新闻,那么启动 Gnus 非常简单 —— 只需输入 M-x gnus 。
启动时,Gnus 会读取你的新闻初始化文件:位于家目录下、名为 .newsrc 的文件,该文件记录了你订阅的 Usenet 新闻组(此文件并非 Gnus 独有,许多其他新闻阅读器也会使用)。随后,它会尝试连接系统默认的新闻服务器,该服务器通常由环境变量 NNTPSERVER 指定。
如果你的系统没有默认新闻服务器,或者你希望用 Gnus 来阅读邮件,那么在执行 M-x gnus 之前,需要先告诉 Gnus 从哪里获取新闻和 / 或邮件。为此,你需要自定义变量 gnus-select-method 或 gnus-secondary-select-methods 。详见《Gnus 手册》中的「查找新闻服务器」部分。
Gnus 启动完成后,会显示组缓冲区。默认情况下,组缓冲区只显示少量已订阅的组。其他状态的组 —— 未订阅、已屏蔽或僵尸状态 —— 都会被隐藏。首次启动 Gnus 时,所有你未订阅的组都会被设为 已屏蔽组 ;之后新闻服务器上新出现的组则会成为 僵尸组 。
要继续操作,你必须在组缓冲区中选中一个组,以打开该组的摘要缓冲区;然后在摘要缓冲区中选择一篇文章,即可在单独窗口中查看对应的文章缓冲区。后续章节会介绍如何使用组缓冲区和摘要缓冲区完成这些操作。
退出 Gnus 的方法是:在组缓冲区中按 q 。该操作会自动将你的组状态记录到 .newsrc 和 .newsrc.eld 文件中,以便在后续的 Gnus 会话中生效。
36.3. 使用 Gnus 组缓冲区
在 Gnus 组缓冲区中可使用以下命令:
SPC- 切换到当前行所在组的摘要缓冲区 (
gnus-group-read-group)。 lA s- 在组缓冲区中,只显示你已订阅且包含未读文章的组 (
gnus-group-list-groups;这是默认列表)。 LA u- 列出所有已订阅和未订阅的组,但不显示已屏蔽(killed)或僵尸(zombie)组 (
gnus-group-list-all-groups)。 A k- 列出已屏蔽组 (
gnus-group-list-killed)。 A z- 列出僵尸组 (
gnus-group-list-zombies)。 u- 切换当前行所在组的订阅状态 (
gnus-group-toggle-subscription-at-point)。对已屏蔽或僵尸组执行此操作会将其转为未订阅组。 C-k- 屏蔽当前行所在的组 (
gnus-group-kill-group)。被屏蔽的组不会记录在.newsrc文件中,也不会在l或L列表中显示。 DEL- 将光标移到上一个包含未读文章的组 (
gnus-group-prev-unread-group)。 n- 将光标移到下一个未读组 (
gnus-group-next-unread-group)。 p- 将光标移到上一个未读组 (
gnus-group-prev-unread-group)。 q- 保存 Gnus 设置并退出 Gnus (
gnus-group-exit)。
36.4. 使用 Gnus 摘要缓冲区
在 Gnus 摘要缓冲区中可使用以下命令:
SPC- 若尚未选中文章,则选中当前行的文章并显示其文章缓冲区。若已选中文章,则在对应窗口中滚动该文章缓冲区;滚动到缓冲区末尾时,自动选中下一篇未读文章 (
gnus-summary-next-page)。 因此,反复按 SPC 即可连续阅读所有文章。 DEL- 向上滚动文章内容 (
gnus-summary-prev-page)。 n- 选中下一篇未读文章 (
gnus-summary-next-unread-article)。 p- 选中上一篇未读文章 (
gnus-summary-prev-unread-article)。 s- 在当前选中的文章缓冲区中执行增量搜索 (
gnus-summary-isearch-article),效果等同于切换到该缓冲区并按C-s(参见增量搜索)。 M-s M-s regexp RET- 向前搜索匹配该正则表达式的文章 (
gnus-summary-search-article-forward)。 M-r regexp RET- 向后搜索匹配该正则表达式的文章 (
gnus-summary-search-article-backward)。 q- 退出摘要缓冲区,回到组缓冲区 (
gnus-summary-exit)。
37. 主机安全
Emacs 运行在 GNU/Linux 等操作系统之上,依赖操作系统来校验文件访问等安全限制。Emacs 的默认配置面向常规使用场景设计;在安全要求高于或低于普通环境的场景中,可能需要进行相应调整。
例如,文件局部变量存在一定风险,你可以将变量 enable-local-variables 设置为 :safe ,或(更保守地)设置为 nil ;反之,如果你的所有文件都可信任,而默认的变量校验机制令人困扰,也可将其设为 :all 。详见《文件变量的安全性》。
使用 load-file 或 load-library 加载 Emacs Lisp 代码文件(参见《Emacs Lisp 代码库》)会执行文件中的部分 Lisp 代码,因此你只应加载来源可信的 Lisp 文件。不过,Emacs 的某些功能在特定情况下,即使没有你显式执行命令,也可能自动运行 Lisp 代码。例如,Emacs 的实时语法检查工具 Flymake(参见 GNU Flymake),若已启用,会在语法检查过程中自动执行你打开的 Lisp 文件中的部分代码。类似地,在编辑 Lisp 文件的缓冲区中,某些补全命令(参见《补全》)有时需要展开 Lisp 宏以获得最佳效果。
在这些情况下,仅仅打开一个 Lisp 文件并进行编辑,就可能触发其中代码的执行。如果该文件来自不可信来源,就可能包含危险甚至恶意代码,并被 Emacs 在上述场景中执行。
为防范此类风险,除非打开的文件被标记为可信,否则 Emacs 会禁止 Flymake、补全及其他相关功能执行 Lisp 代码。你需要通过自定义用户选项 trusted-content ,来指定系统中哪些文件或目录应被视为可信。
- 用户选项:
trusted-content 该选项的默认值为
nil,表示不信任任何文件。你可以将其自定义为一个或多个可信文件与目录的名称列表。以斜杠/结尾的文件名会被识别为目录,表示该目录下的所有文件与子目录均被信任。特殊值
:all表示信任系统上的所有文件与目录; 不推荐使用此值 ,因为它会留下巨大的安全漏洞。
有关将 Emacs 作为大型应用一部分使用时的安全注意事项,详见《Emacs Lisp 参考手册》中的《安全考量》。
38. 网络安全
每当 Emacs 建立网络连接时,都会将已建立的连接交给 Network Security Manager 网络安全管理器 (NSM)。NSM 负责在你的控制下强制执行网络安全策略。目前,该功能通过 Transport Layer Security 传输层安全(TLS) 特性实现。
network-security-level 变量决定 NSM 强制执行的安全级别。如果其值为 low (低),则不执行任何安全检查。 不推荐使用该值 ,因为这基本意味着你的网络连接不可信。不过,在有限场景下(例如排查网络问题时)该设置可能有用。
如果该变量为 medium (中,默认值),则会执行多项检查。若 NSM 判断该网络连接可能不可信,会向你发出提示,并询问如何处理该连接。
你可以选择为未验证的连接 添加永久安全例外、临时例外,或完全拒绝该连接 。
除了基本的证书合法性检查外,NSM 还支持多项 TLS 算法检查。一些过去被认为安全的加密技术现已被证明存在缺陷,因此 Emacs(默认情况下)会对其中部分问题发出警告。
协议网络检查由变量 network-security-protocol-checks 控制。它是一个关联列表(alist),每个关联的第一个元素是检查项名称,第二个元素是应启用该检查的安全级别。
例如 (rc4 medium) 会导致函数 nsm-protocol-check--rc4 以如下方式调用: (nsm-protocol-check--rc4 host port status settings) 。如果函数返回非空,表示允许继续连接;返回空则拒绝。
下面是默认 medium 级别下执行的检查项列表:
- 无法验证 TLS 证书
如果是 TLS、SSL 或 STARTTLS 连接,NSM 会检查用于确认服务器身份的证书是否可验证。
虽然无效证书通常值得警惕(可能存在中间人劫持连接并窃取密码),但有时也有合理原因继续使用该连接。例如服务器使用自签名证书,或证书已过期。是否继续连接由你自行决定。
- 自签名证书发生变更
- 如果你之前接受过某个自签名证书,但该证书现在发生变化,这可能只是服务器更换了证书,也可能意味着网络连接已被劫持。
- 此前加密的连接现在变为未加密
如果当前连接未加密,但之前会话是加密的,可能表示你与服务器之间存在代理,剥离了 STARTTLS 声明,导致连接明文传输。这通常非常可疑。
- 发送密码时使用未加密服务
- 连接 IMAP 或 POP3 服务器时通常应加密,因为这类连接常会传输密码。同样,通过需要密码验证的 SMTP 发送邮件时,通常也希望连接加密。如果连接未加密,NSM 会发出警告。
- Diffie‑Hellman 素数位数过低
- 在公钥交换过程中,素数位数应足够高,以防止信道被第三方窃听。如果该数值过低,Emacs 会发出警告。(对应
network-security-protocol-checks中的diffie-hellman-prime-bits检查) - RC4 流密码
- RC4 流密码被认为安全性较低,可能允许第三方窃听。(对应
network-security-protocol-checks中的 rc4 检查) - 主机证书或中间证书使用 SHA1
- 如果中间证书使用 SHA1 哈希算法,第三方可能伪造证书冒充该签发机构。这类连接易受中间人攻击。(对应
network-security-protocol-checks中的signature-sha1和intermediate-sha1检查) - SSL1、SSL2、SSL3
- 早于 TLS1.0 的协议被认为存在多种安全漏洞,若你的场景需要较高安全性,应避免使用。(对应
network-security-protocol-checks中的version检查) - 三重 DES(3DES)密码
- 3DES 流密码的有效安全强度最多只有 112 位,且 2016 年已披露重大安全漏洞(CVE-2016-2183)。NIST 已从 2023 年底起在所有应用中弃用该算法。(对应
network-security-protocol-checks中的 3des-cipher 检查)
如果 network-security-level 设为 high (高),除上述检查外,还会额外执行:
- 已验证证书的公钥发生变更
- 服务器偶尔会更换密钥,这通常无需担心。但如果你担心网络连接被某些机构劫持 —— 这些机构可利用受控的证书颁发机构为第三方服务签发新证书 —— 你可能需要跟踪这类变更。
最后,如果 network-security-level 设为 paranoid (最高),则 NSM 首次见到任何新证书时都会通知你。这让你可以检查 Emacs 发起的所有连接的全部证书。
以下附加变量可用于控制 NSM 的具体行为:
nsm-settings-file- NSM 存储连接详细信息的文件。默认为:
~/.emacs.d/network-security.data。 nsm-save-host-names- 默认情况下,非 STARTTLS 连接不会保存主机名,而是使用 主机/端口 哈希值标识连接。这可以防止他人随意读取配置文件,获知用户连接过哪些服务器。如果该变量设为
t,NSM 会在nsm-settings-file中同时保存主机名。
39. 文档查看
DocView 模式是用于查看 DVI、PostScript(PS)、PDF、OpenDocument、Microsoft Office、EPUB、CBZ、FB2、XPS 和 OXPS 文档的主模式。它提供切片、缩放、文档内搜索等功能。其工作原理是:使用 gs (GhostScript)或 pdfdraw / mutool draw (MuPDF)命令及其他外部工具,将文档转换为一组图片,然后显示这些转换后的图片。
当你打开可由 DocView 模式显示的文档文件时,Emacs 会自动启用该模式21。唯一例外是:打开 PostScript 文件时,Emacs 会切换到 PS 模式(一种将 PostScript 文件作为文本编辑的主模式),但同时也会启用 DocView 次模式,因此你可以按 C-c C-c 用 DocView 查看文档。在 DocView 主模式或次模式下,重复按 C-c C-c (doc-view-toggle-display) 可在 DocView 显示与原始文件内容之间切换。
若你打开的文件本应由 DocView 处理,但某些条件未满足(例如在终端框架中运行,或 Emacs 不支持 PNG),Emacs 会询问是否以纯文本查看文档内容。确认后,缓冲区将进入文本模式并激活 DocView 次模式。此时按 C-c C-c 可切换到回退模式,再按一次 C-c C-c 回到 DocView 模式。在 DocView 模式中,也可通过 C-c C-t (doc-view-open-text) 直接显示纯文本内容。
你可以通过命令 M-x doc-view-mode 显式启用 DocView 模式,通过 M-x doc-view-minor-mode 切换 DocView 次模式。
DocView 模式启动时会显示欢迎界面,并开始逐页排版文件。第一页排版完成后便会显示。
关闭 DocView 缓冲区:按 k (doc-view-kill-proc-and-buffer) 。隐藏该窗口:按 q (quit-window)。
39.2. DocView 搜索
在文档视图(DocView)模式下,你可以基于正则表达式搜索文件文本(参见正则表达式语法)。该搜索界面的设计参考了增量搜索 isearch (参见增量搜索)。
开始搜索:
- 输入
C-s(doc-view-search) :正向搜索 - 输入
C-r(doc-view-search-backward) :反向搜索
这会通过小缓冲区读取一个正则表达式,然后在回显区显示文档中匹配到的数量。你可以通过再次输入 C-s 和 C-r 在匹配项之间前后跳转。
文档视图模式无法在页面图片中直接高亮显示匹配内容;它会在鼠标位置弹出提示框(tooltip),列出当前页面内所有匹配的行。如需强制显示该提示框,输入 C-t (doc-view-show-tooltip)。
39.3. DocView 切片
文档在打印时通常会留有较宽的边距。但在屏幕上阅读时,这些边距会很碍事,因为它们会占用屏幕空间,并可能导致不便的滚动操作。
在 DocView 中,你可以通过选择页面的一个切片区域来隐藏这些边距。切片是页面区域内的一个矩形;一旦在 DocView 中指定了切片,它将应用到你查看的任意页面。
要通过数值方式指定切片,按 C-s (doc-view-set-slice) ;然后输入左上角像素坐标,以及切片的宽度和高度。
更便捷的图形化指定切片方式是使用 C-m (doc-view-set-slice-using-mouse) ,通过鼠标来选择切片。只需在想要保留区域的左上角按住 mouse-1 鼠标左键,然后将鼠标指针拖动到右下角并松开按键即可。
最便捷的方式是利用从文档自动获取的边界框信息来设置最优切片,按 C-b (doc-view-set-slice-from-bounding-box)。
要取消已选中的切片,按 C-r (doc-view-reset-slice) 。之后 DocView 将显示完整页面,包括全部边距。
39.4. DocView 格式转换
为提升效率,DocView 会缓存由 gs (Ghostscript)生成的图片。用于缓存图片的目录名称由变量 doc-view-cache-directory 指定。你可以输入 M-x doc-view-clear-cache 清空缓存目录。
若要强制重新转换当前查看的文档,输入 r 或 g (revert-buffer) 。若要终止与当前缓冲区关联的转换进程,输入 K (doc-view-kill-proc) 。命令 k (doc-view-kill-proc-and-buffer) 会同时终止转换进程并关闭 DocView 缓冲区。
40. 从 Emacs 运行 Shell 命令
Emacs 提供了相关命令,可将单行命令传递给 Shell 子进程、在 Emacs 缓冲区中交互式运行 Shell(输入输出均在缓冲区),以及在终端仿真窗口中运行 Shell。
M-! cmd RET- 执行 Shell 命令 cmd 并显示输出结果 (
shell-command)。 M-| cmd RET- 以选中区域的内容作为输入,执行 Shell 命令 cmd ;可选择用输出结果替换选中区域 (
shell-command-on-region)。 M-& cmd RET- 异步执行 Shell 命令 cmd ,并显示输出结果 (
async-shell-command)。 M-x shell- 通过 Emacs 缓冲区进行输入输出,启动一个子 Shell。之后可交互式输入命令。
M-x term- 通过 Emacs 缓冲区进行输入输出,启动一个子 Shell。之后可交互式输入命令,支持完整的终端仿真。
当你为可执行程序指定相对路径文件名时(无论是在上述命令的 cmd 参数中,还是在其他场景下),Emacs 会在变量 exec-path 指定的目录中搜索该程序。此变量的值必须是一个目录列表;Emacs 启动时,会从环境变量 PATH 初始化该变量的默认值(参见通用变量)。
M-x eshell 启动一个完全由 Emacs 自身实现的 Shell。其用法在独立手册中有详细说明,参见《Eshell:Emacs 内置 Shell》中的 Eshell 章节。
40.1. 单个 Shell 命令
M-! (shell-command) 通过迷你缓冲(minibuffer)读取一行文本,并在专门为此命令创建的子 Shell 中作为 Shell 命令执行。命令的标准输入来自空设备。如果 Shell 命令产生输出,输出会显示在回显区(内容较短时),或显示在名为 *Shell Command Output* (由变量 shell-command-buffer-name 指定)的缓冲区中(内容较长时)。变量 resize-mini-windows 和 max-mini-window-height (见 “在小缓冲中编辑”)控制 Emacs 何时认为输出过长而不适合显示在回显区。注意:自定义下文介绍的 shell-command-dont-erase-buffer 可能会影响回显区的显示内容。
例如,解压名为 foo.gz 的文件可以输入: M-! gunzip foo.gz RET 该 Shell 命令通常会生成文件 foo,且不会产生终端输出。
给 shell-command 传递数字前缀参数(如 M-1 M-! ),会让命令将终端输出 插入到当前缓冲区 ,而不是单独的输出缓冲区。默认情况下,光标(point)放在输出内容之前,标记(mark)放在输出内容之后(但非默认值的 shell-command-dont-erase-buffer 会改变这一行为,见下文)。例如: M-1 M-! gunzip < foo.gz RET 会将 foo.gz 解压后的内容插入到当前缓冲区。
如果指定的 Shell 命令不以 '&' 结尾,它会同步执行,你必须等待命令退出才能继续使用 Emacs。要停止等待,按 C-g 退出;这会发送 SIGINT 信号终止 Shell 命令(与 Shell 中 C-c 的作用相同)。Emacs 会等待命令真正终止。如果 Shell 命令没有停止(因为它忽略了 SIGINT ),再次按 C-g ;这会发送 SIGKILL 信号,该信号无法被忽略。
以 '&' 结尾的 Shell 命令会 asynchronously异步执行 ,命令运行时你可以继续使用 Emacs。你也可以输入 M-& (async-shell-command) 来异步执行 Shell 命令;效果与在 M-! 命令末尾加 '&' 完全一样,只是不需要手动输入 '&' 。异步 Shell 命令的输出默认进入 *Async Shell Command* 缓冲区(由变量 shell-command-buffer-name-async 指定)。无论该缓冲区是否在窗口中可见,Emacs 都会实时将输出写入其中。
如果你希望 同时运行多个异步 Shell 命令 ,它们可能会竞争同一个输出缓冲区。选项 async-shell-command-buffer 用于控制这种情况:例如是重命名已存在的输出缓冲区,还是为新命令使用不同缓冲区。更多行为请查阅该变量的文档。
如果你希望异步命令的输出缓冲区 只在有输出时才显示 ,将 async-shell-command-display-buffer 设为 nil 。
选项 async-shell-command-width 定义异步 Shell 命令输出可用的显示列数。正整数表示指定列数;默认值 nil 表示使用 Shell 提供的列数。
要让上述命令在提示符中显示 当前目录 ,将变量 shell-command-prompt-show-cwd 设为非 nil 值。
M-| (shell-command-on-region) 与 M-! 类似,但会将 选中区域内容 作为标准输入传给 Shell 命令,而不是空输入。如果使用数字前缀参数,它会删除原有区域,并用 Shell 命令的输出替换该区域。
例如,你可以用 M-| 配合 gpg 程序查看缓冲区中的密钥。如果缓冲区包含 GnuPG 密钥,输入: C-x h M-| gpg RET 将整个缓冲区内容传给 gpg ,密钥列表会输出到 shell-command-buffer-name 命名的缓冲区。
上述命令使用由变量 shell-file-name 指定的 Shell。其默认值在 Emacs 启动时由环境变量 SHELL 决定。如果文件名是相对路径,Emacs 会在 exec-path 列出的目录中搜索(见 “在 Emacs 中运行 Shell 命令”)。
如果当前默认目录是远程目录(见 “远程文件”), shell-file-name 默认值为 /bin/sh 。可以通过将 shell-file-name 声明为连接本地变量来修改(见 “每个连接的本地变量”)。
要为 M-! 或 M-| 指定编码系统,在执行前先使用命令 C-x RET c 。见 “进程间通信的编码系统”。
默认情况下,错误输出与普通输出混合在输出缓冲区中。但如果你将变量 shell-command-default-error-buffer 设为字符串,错误输出会插入到该字符串命名的缓冲区中。
默认情况下,输出缓冲区在不同 Shell 命令之间会被清空,除非输出直接写入当前缓冲区。如果你将选项 shell-command-dont-erase-buffer 设为 erase ,输出缓冲区 总是会被清空 。其他非 nil 值则 禁止清空 输出缓冲区;并且 —— 如果输出缓冲区不是当前缓冲区 —— 还会控制插入 Shell 命令输出后光标的位置:
beg-last-out- 光标放在上一次 Shell 命令输出的开头。
end-last-out- 光标放在上一次 Shell 命令输出的末尾,即输出缓冲区的末尾。
save-point- 恢复插入 Shell 命令输出前的光标位置。
注意:如果该选项为非 nil,回显区显示的内容可能不只来自最后一条命令,因为回显区只显示输出缓冲区的一部分内容。
如果输出缓冲区不是当前缓冲区,Shell 命令的输出会追加到该缓冲区末尾
40.2. 交互式子 shell
要交互式运行子 Shell,输入 M-x shell 。这会创建(或复用)一个名为 *shell* 的缓冲区,并在其中运行一个子 Shell 进程,输入与输出都通过该缓冲区进行。也就是说,子 Shell 的所有终端输出都会写入缓冲区,并移动光标;子 Shell 的所有终端输入都来自缓冲区中的文本。要向子 Shell 输入内容,只需跳到缓冲区末尾,输入内容并按 RET 即可。
默认情况下,当以交互方式启动子 Shell 时, *shell* 缓冲区会在新窗口中显示,除非当前窗口已经显示该缓冲区。此行为可通过 display-buffer-alist 自定义(参见 display-buffer 的工作方式)。
当子 Shell 处于等待或运行命令状态时,你可以切换窗口或缓冲区,在 Emacs 中进行其他编辑操作。Emacs 会在有空时(例如等待键盘输入时)将子 Shell 的输出插入到 Shell 缓冲区。
在 Shell 缓冲区中,提示符使用 comint-highlight-prompt 样式显示,提交的输入行使用 comint-highlight-input 样式显示。这便于区分输入行与 Shell 输出。参见文本外观。
要创建多个子 Shell,带前缀参数调用 M-x shell (例如 C-u M-x shell )。命令会让你输入一个缓冲区名称,并在该缓冲区中创建(或复用)子 Shell。你也可以用 M-x rename-uniquely 重命名现有的 *shell* 缓冲区,然后直接用 M-x shell 新建一个 *shell* 缓冲区。不同缓冲区中的子 Shell 彼此独立、并行运行。
Emacs 会尝试通过解析你输入的命令(如 cd 等)来跟踪当前目录。这种方法容易出错,因为修改当前目录的方式很多,因此 Emacs 还会识别专门用于传递目录信息的 OSC(Operating System Commands操作系统命令)转义码,这种方式更可靠。你可以配置 Shell 在每个提示符处输出合适的转义序列,例如使用以下命令:
printf "\e]7;file://%s%s\e\\" "$HOSTNAME" "$PWD"
然后告诉 Emacs 处理这些转义码:
(add-hook 'comint-output-filter-functions #'comint-osc-process-output)
要指定 M-x shell 使用的 Shell 文件名,自定义变量 explicit-shell-file-name 。如果该变量为 nil (默认),Emacs 会优先使用环境变量 ESHELL (如果存在)。否则通常使用变量 shell-file-name (参见单行 Shell 命令);但如果默认目录是远程目录(参见远程文件),Emacs 会提示你输入 Shell 文件名。关于如何高效输入远程文件名,参见文件名的迷你缓冲。
如果存在文件 ~/.emacs_shellname (其中 shellname 是所加载 Shell 的文件名),Emacs 会将其内容作为输入发送给新启动的 Shell。例如,使用 bash 时,该文件为 ~/.emacs_bash 。如果找不到该文件,Emacs 会尝试 ~/.emacs.d/init_shellname.sh 。
要为 Shell 指定编码系统,可在 M-x shell 之前使用 C-x RET c 。也可以在 Shell 缓冲区中输入 C-x RET p 来修改正在运行的子 Shell 的编码系统。参见进程间通信的编码系统。
Emacs 会在子 Shell 中将环境变量 INSIDE_EMACS 设置为 'version,comint',其中 version 是 Emacs 版本号(如 28.1)。程序可以检查此变量,以判断是否运行在 Emacs 子 Shell 中。
40.3. Shell 模式
Shell 缓冲区使用的主模式是 Shell mode。它的许多专用快捷键都以 C-c 为前缀,用法与普通 Shell 里常见的编辑和任务控制键类似,只是需要先按 C-c 。下面是 Shell 模式的命令列表:
RET- 将当前行作为输入发送给子 Shell (
comint-send-input)。行首的 Shell 提示符会被忽略(见 Shell 提示符)。如果光标在缓冲区末尾,这就和在普通交互式 Shell 里提交命令行一样。不过,你也可以在 Shell 缓冲区的任意位置按RET,将当前行作为输入提交。 TAB- 对 Shell 缓冲区中光标前的命令名或文件名进行补全 (
completion-at-point)。它使用 Emacs 常规的补全规则(见补全),补全候选项包括文件名、环境变量名、Shell 命令历史以及历史引用(见 Shell 历史引用)。控制补全行为的选项见 Shell 模式选项。 M-?- 临时显示光标前文件名的所有可能补全列表 (
comint-dynamic-list-filename-completions)。 C-d- 要么删除一个字符,要么发送 EOF 结束信号 (
comint-delchar-or-maybe-eof)。在 Shell 缓冲区末尾按C-d会向子 Shell 发送 EOF;在缓冲区其他位置则照常删除字符。 C-c C-a- 跳转到行首,但如果有提示符则跳到提示符之后 (
comint-bol-or-process-mark)。如果连续按两次,第二次会跳回到进程标记(process mark),也就是你尚未发送给子 Shell 的输入的起始位置。(通常就是本行提示符的末尾,但在使用C-c SPC后,进程标记可能出现在上一行。) C-c SPC- 累积多行输入,之后一起发送 (
comint-accumulate)。该命令会在光标前插入换行,但不会把前面的文本发送给子 Shell—— 至少暂时不会。当你按RET时,换行前后的两行(以及分隔它们的换行符)会一起发送。 C-c C-u- 删除缓冲区末尾待发送的所有输入文本 (
comint-kill-input)。如果光标不在缓冲区末尾,只删除光标之前的那部分待输入文本。 C-c C-w- 删除光标前的一个单词 (
backward-kill-word)。 C-c C-c- 中断 Shell 或其当前子任务(如果有) (
comint-interrupt-subjob) 。该命令同时会清空 Shell 缓冲区中尚未发送的所有输入。 C-c C-z- 暂停 Shell 或其当前子任务(如果有) (
comint-stop-subjob)。该命令同时会清空 Shell 缓冲区中尚未发送的所有输入。 C-c C-\- 向 Shell 或其当前子任务(如果有)发送退出信号 (
comint-quit-subjob) 。该命令同时会清空 Shell 缓冲区中尚未发送的所有输入。 C-c C-o- 删除上一条 Shell 命令的最后一批输出 (
comint-delete-output)。当某条命令输出大量无用信息时很有用。带前缀参数使用时,会把删除的文本保存到剪切环 (kill-ring),之后可以在别处粘贴。 C-c C-s- 将上一条 Shell 命令的最后一批输出写入文件 (
comint-write-output)。带前缀参数时为追加写入。输出末尾的提示符不会被写入。 C-c C-rC-M-l- 滚动窗口,使上一批输出的开头显示在窗口顶部,并将光标移到那里 (
comint-show-output)。 C-c C-e- 滚动窗口,使缓冲区最后一行显示在窗口底部 (
comint-show-maximum-output)。 C-c C-f- 向后跳过一条 Shell 命令,但不超出当前行 (
shell-forward-command)。变量shell-command-regexp用于定义如何识别命令结尾。 C-c C-b- 向前跳过一条 Shell 命令,但不超出当前行 (
shell-backward-command)。 M-x dirs- 向 Shell 查询当前工作目录,并更新 Shell 缓冲区的默认目录。见目录跟踪。
M-x comint-send-invisible RET 文本 RET以不回显的方式读取 text文本 ,然后发送给 Shell。当 Shell 命令运行需要输入密码的程序时,这个命令很有用。
注意:Emacs 默认不会回显密码。如果你确实需要回显密码,请执行下面的 Elisp 表达式:
(remove-hook 'comint-output-filter-functions 'comint-watch-for-password-prompt)M-x comint-continue-subjob- 继续 Shell 进程。如果你意外挂起了 Shell 进程,这个命令很有用22。
M-x comint-strip-ctrl-m从当前一组 Shell 输出中清除所有控制字符
^M(回车符)。最方便的用法是让它在收到子 Shell 输出时自动运行,执行以下代码:(add-hook 'comint-output-filter-functions 'comint-strip-ctrl-m)M-x comint-truncate-buffer将 Shell 缓冲区截断到指定的最大行数,行数由变量
comint-buffer-maximum-size决定。若要每次收到输出时都自动截断,执行:(add-hook 'comint-output-filter-functions 'comint-truncate-buffer)
默认情况下,Shell 模式会处理常见的 ANSI 转义码(例如修改文字颜色)。Emacs 还可选支持一些扩展转义码,比如部分 OSC(操作系统命令)码,只需在配置文件中加入:
(add-hook 'comint-output-filter-functions #'comint-osc-process-output)
启用后,例如 ls --hyperlink 的输出会在 Shell 模式缓冲区中变成可点击的链接按钮。
Shell 模式继承自 Comint 模式,后者是用于与交互式子进程通信的通用模式。Shell 模式的大部分功能实际来自 Comint 模式,从命令名前缀就能看出来。Shell 模式的特有功能包括目录跟踪和少量用户命令。
其他使用 Comint 变体的 Emacs 功能包括 GUD(见调试器)和 M-x run-lisp (见运行外部 Lisp 解释)等。
你可以用 M-x comint-run 以未经定制的原始 Comint 模式运行任意程序 —— 不带 Shell 模式的那些特殊功能。要给程序传递参数,使用 C-u M-x comint-run 。
40.4. Shell 提示符
提示符是程序输出的一段文本,用于表明它已准备好接收用户新的输入。通常情况下,Comint 模式(以及由此衍生的 Shell 模式)会根据子进程的输出,自动识别缓冲区中的哪一部分是提示符。(具体来说,它会将所有不以换行符结尾的输出行视作提示符。)
Comint 模式会将缓冲区分为两类 fields区域 :
- 输入区域:用户输入命令的位置
- 输出区域:除此之外的所有内容
提示符属于输出区域。大多数 Emacs 光标移动命令 不会跨越区域边界 ,除非命令本身需要跨多行移动。例如,当光标位于某行 Shell 命令的输入区域时,按下 C-a 会将光标定位到 输入区域的开头 ,也就是提示符之后。在内部实现中,这些区域是通过文本区域属性(field text property) 来实现的(详见《Emacs Lisp 参考手册》中的文本属性章节)。
如果你将变量 comint-use-prompt-regexp 设置为非空值,Comint 模式就会 使用正则表达式 来识别提示符(详见正则表达式语法)。在 Shell 模式下,该正则表达式由变量 shell-prompt-pattern 指定。
comint-use-prompt-regexp 的默认值为 nil ,因为这种识别提示符的方式并不可靠;但在一些特殊场景下,你可以将其设为非空值。此时,Emacs 不会再把 Comint 缓冲区划分成不同区域,因此常规的光标移动命令会和在普通无特殊属性的缓冲区中行为一致。不过,你仍然可以使用 段落移动命令 来方便地浏览缓冲区(详见段落相关章节);在 Shell 模式下,Emacs 会把 shell-prompt-pattern 匹配的内容当作段落边界。
40.5. Shell 命令历史
Shell 缓冲区支持三种重复执行之前命令的方式:你可以使用类似迷你缓冲区(minibuffer)历史记录的按键;其工作方式与迷你缓冲区中基本相同,会从之前的命令中插入文本,同时光标始终保持在缓冲区末尾。你可以在缓冲区中移动到之前输入命令的原始位置,然后重新提交或将它们复制到缓冲区末尾。或者,你也可以使用以 '!' 开头的历史引用方式。
40.5.1. Shell 历史环
M-pC-UP- 取出上一条更早的 Shell 命令 (
comint-previous-input)。 M-nC-DOWN- 取出下一条更近的 Shell 命令 (
comint-next-input)。 M-r- 开始对过往 Shell 命令进行增量正则表达式搜索 (
comint-history-isearch-backward-regexp)。 C-c C-x- 从历史记录中取出下一条连续命令 (
comint-get-next-from-history)。 C-c .- 从旧 Shell 命令中取出一个参数 (
comint-input-previous-argument)。 C-c C-l- 在另一个窗口中显示缓冲区的 Shell 命令历史 (
comint-dynamic-list-input-ring)。
Shell 缓冲区会保存之前输入过的命令历史。要复用历史中的命令,可使用编辑命令 M-p 、 M-n 和 M-r 。它们的行为与迷你缓冲区(minibuffer)的历史命令类似(参见 “迷你缓冲区历史”),区别在于这些命令作用于 Shell 缓冲区而非迷你缓冲区,并且 Shell 缓冲区中的 M-r 会对 Shell 命令历史执行增量搜索。
M-p 会将更早的一条 Shell 命令取出到 Shell 缓冲区末尾。连续使用 M-p 会依次取出更早的命令,每条命令都会替换掉当前作为待输入的文本。 M-n 作用类似,只是会依次取出更近的命令。 C-UP 等同于 M-p , C-DOWN 等同于 M-n 。
历史搜索命令 M-r 会启动对过往 Shell 命令的增量正则表达式搜索。按下 M-r 后,输入你想匹配的字符串或正则表达式,最后一条匹配的 Shell 命令会显示在当前行。增量搜索命令保持原有行为 —— 例如 C-s 和 C-r 分别向前、向后查找下一个匹配项(参见 “增量搜索”)。找到想要的输入后,按 RET 结束搜索,该命令会被放到命令行中。在浏览历史列表前你正在输入的内容,会在回到历史环开头或末尾时自动恢复。
有时需要重新执行一连串连续的 Shell 命令。做法是:先找到并重新执行序列中的第一条命令,然后按 C-c C-x ,它会取出紧跟在刚才重复命令之后的下一条命令,再按 RET 执行。反复使用 C-c C-x RET 即可连续重执行多条命令。
命令 C-c . (comint-insert-previous-argument) 用于从之前的命令中复制单个参数,类似 Bash 和 zsh 中的 ESC . 。最简单的用法是复制上一条命令的最后一个参数。带前缀参数 n 时,会复制第 n 个参数。重复按 C-c . 会从更早的命令中复制同一位置的参数(重复时不要再加前缀参数)。
如果将变量 comint-insert-previous-argument-from-end 设为非空值, C-c . 会改为从末尾往前数第 n 个参数,这模拟了 zsh 中的 ESC . 行为。
这些命令从专门的历史列表中获取旧命令文本,而非直接从 Shell 缓冲区读取。因此,编辑 Shell 缓冲区甚至删除其中大部分内容,都不会影响这些命令访问的历史记录。
部分 Shell 会将命令历史保存到文件中,以便在不同 Shell 会话间复用。Emacs 会读取你所用 Shell 的历史文件来初始化自身的命令历史。历史文件名由变量 shell-history-file-name 指定:
- 若该选项为
t,则不读取命令历史; - 若为
nil,则从环境变量HISTFILE指定的文件读取; - Bash:
~/.bash_history - ksh:
~/.sh_history - zsh:
~/.zsh_history - 其他 Shell:
~/.history
40.5.2. Shell 历史复制
C-c C-p- 将光标移到上一个提示符处 (
comint-previous-prompt)。 C-c C-n- 将光标移到下一个提示符处 (
comint-next-prompt)。 C-c RET- 复制光标所在位置的输入命令,并将副本插入到缓冲区末尾 (
comint-copy-old-input)。当你把光标移回到之前的命令时,这个命令非常有用。复制命令后,你可以按RET将复制内容作为新输入提交。如果需要,你可以在重新提交前编辑复制的内容。如果你在输出行上使用此命令,它会将该行复制到缓冲区末尾。 mouse-2- 如果
comint-use-prompt-regexp为nil(默认值),则复制你点击的旧输入命令,并将副本插入缓冲区末尾 (comint-insert-input)。如果comint-use-prompt-regexp为非空值,或者点击位置不在旧输入上,则执行普通的粘贴操作。
先移到之前的输入,再用 C-c RET 或 mouse-2 复制,得到的缓冲区内容,与你多次按 M-p 从历史记录中取出该旧输入的效果相同。但是, C-c RET 是从缓冲区中复制文本;如果你在命令发送后编辑过缓冲区里的输入内容,缓冲区内容可能与历史记录中的内容不一致。
40.5.3. Shell 历史引用
包括 csh 和 bash 在内的多种 Shell,都支持以 ! 和 ^ 开头的历史引用。Shell 模式能够识别这些语法结构,并为你执行历史替换。
如果你输入一条历史引用并按下 TAB ,Emacs 会在输入历史中搜索匹配的命令,按需执行替换,并将结果替换掉原来的历史引用插入到缓冲区中。例如,输入 !mv 再按 TAB ,即可取出最近一条以 'mv' 开头的命令。你可以按需编辑该命令,然后按下 RET 重新提交给 Shell。
Shell 模式可以在你将命令发送给 Shell 时,自动展开缓冲区中的历史引用。要启用此功能,可将变量 comint-input-autoexpand 设置为 input 。你还可以将 SPC 键绑定到命令 comint-magic-space ,让 SPC 空格键也执行历史展开(参见交互式修改按键绑定)。
Shell 模式只识别出现在提示符之后的历史引用。关于 Shell 模式如何识别提示符,可参见「Shell 提示符」一节。
40.6. 目录跟踪
Shell 模式会跟踪发送给子 Shell 的 cd、pushd 和 popd 命令,以便让 Shell 缓冲区的 默认目录 (见文件名)与 Shell 的工作目录保持一致。它通过检查你发送的输入行来识别这些命令。
如果你为这些命令设置了别名,可以通过将变量 shell-cd-regexp 、 shell-pushd-regexp 、 shell-popd-regexp 设置为对应的正则表达式,来让 Emacs 也识别这些别名(见正则表达式语法)。例如,如果 shell-pushd-regexp 与某条 Shell 命令行的开头匹配,该行就会被视为 pushd 命令。这些命令 只在命令行开头 才会被识别。
如果 Emacs 对子 Shell 的工作目录变化判断出错,可以输入 M-x dirs 。该命令会向 Shell 查询当前工作目录,并据此更新 Emacs 里的默认目录。它适用于支持最常用命令语法的 Shell,但在一些特殊 Shell 中可能无效。
你还可以使用 Dirtrack 模式 —— 一种缓冲区本地的次要模式,它提供另一种跟踪 Shell 工作目录的方式。使用该方式时,你的 Shell 提示符必须 始终包含工作目录 ,并且你必须提供一个正则表达式,用于识别提示符中哪一部分是工作目录;详情见变量 dirtrack-list 的文档。要启用 Dirtrack 模式,可以在 Shell 缓冲区中输入 M-x dirtrack-mode ,或将 dirtrack-mode 添加到 shell-mode-hook 中(见钩子)。
40.7. Shell 模式选项
如果变量 comint-scroll-to-bottom-on-input 为非空值,那么在插入和粘贴操作前,当前窗口会自动滚动到缓冲区底部。默认值为 nil 。
如果 comint-scroll-show-maximum-output 为非空值,当光标位于缓冲区末尾时,新输出到来时会尽量把最后一行显示在窗口最底部,以展示尽可能多的有效内容。(这模仿了大多数终端的滚动行为。)默认值为 t 。
通过设置 comint-move-point-for-output ,你可以让 每当有新输出时,光标自动跳转到缓冲区末尾 ,无论光标之前在何处:
- 值为 this:仅在选中窗口中跳转光标
- 值为 all:在所有显示该 Comint 缓冲区的窗口中跳转
- 值为 other:在所有非选中但显示该缓冲区的窗口中跳转
- 默认值为 nil,表示光标不自动跳转。
如果设置 comint-prompt-read-only ,Comint 缓冲区中的提示符会变为 只读 。
变量 comint-input-ignoredups 控制是否在输入历史中保存连续重复的命令。非空值表示忽略与上一条相同的输入;默认值 nil 表示保存所有输入,即使重复。
有三个变量用于自定义文件名补全:
comint-completion-addsuffix:控制补全后是否自动添加空格或斜杠来标识已完整匹配的文件或目录(非空表示添加)。comint-completion-recexact:若为非空,当常规补全无法再增加字符时,TAB会选择最短的可能匹配项。comint-completion-autolist:若为非空,当无法精确补全时,自动列出所有可能结果。
命令补全默认只考虑 可执行文件 。如果将 shell-completion-execonly 设为 nil ,则也会包含不可执行文件。
变量 shell-completion-fignore 指定在 Shell 模式补全中要忽略的文件后缀列表。默认值为 nil ;很多用户会设为 ("" "#" "%"),忽略以 ~、#、% 结尾的文件。其他相关的 Comint 模式则使用变量 ~comint-completion-fignore 。
Shell 命令补全的部分实现细节,可参考 shell-dynamic-complete-command 函数的 Lisp 文档。
你可以配置 'pushd' 的行为:
shell-pushd-tohome:无参数时是否像 cd 一样回到家目录shell-pushd-dextract:使用数字参数时是否直接弹出而非旋转shell-pushd-dunique:仅当目录不在目录栈中时才加入
这些设置应与底层 Shell 的行为保持一致。
Comint 模式会将环境变量 TERM 设置为安全的默认值,但这会禁用部分有用功能,例如很多程序会根据 TERM 判断是否支持颜色,此时颜色会被关闭。因此 Emacs 提供了 comint-terminfo-terminal 选项,让你指定系统 terminfo 数据库中功能更完整的终端类型。只要 system-uses-terminfo 为非空,Emacs 就会用该选项作为 TERM 的值。
comint-terminfo-terminal 和 system-uses-terminfo 都可以声明为 连接本地变量 ,以便适配不同远程主机的需求(参见 “按连接本地变量”)。
40.8. Emacs 终端模拟器
要在文本终端模拟器中运行子 Shell,使用 M-x term 。该命令会创建(或复用)名为 *terminal* 的缓冲区,并运行一个子 Shell:输入来自你的键盘,输出则发送到该缓冲区。
终端模拟器使用 Term 模式,它包含两种输入模式:
- 行模式(line mode):Term 模式的行为基本与 Shell 模式一致(见 Shell 模式)。
字符模式(char mode):每个字符会直接作为终端输入发送给子 Shell;唯一的例外是 终端转义字符 ,默认为
C-c(见 Term 模式)。输入的回显由子 Shell 负责处理;子 Shell 输出的任何终端内容都会进入缓冲区,并移动光标。
有些程序(如 Emacs 本身)需要精细控制终端屏幕的显示效果,它们会通过发送特殊的控制码来实现。Term 模式能够识别并处理 ANSI 标准 VT100 风格转义序列 ,这是包括 xterm 在内的大多数现代终端都支持的格式。(因此,你甚至可以在 Emacs 的 Term 窗口中再运行一个 Emacs。)
term 外观指定了终端模拟器中文本的默认外观(默认与默认外观相同)。当使用终端控制码改变文本外观时,终端模拟器会通过以下外观来表示:term-color-black、term-color-red、term-color-green、term-color-yellow、term-color-blue、term-color-magenta、term-color-cyan、term-color-white、term-color-underline、term-color-bold。详见「文本面孔」。
你也可以使用 Term 模式与连接到串口的设备进行通信。详见「串口终端」。
用于加载子 Shell 的文件名,其确定方式与 Shell 模式相同。若要创建多个终端模拟器,可以使用 M-x rename-uniquely 将 *terminal* 缓冲区重命名为其他名称,用法与 Shell 模式一致。
与 Shell 模式不同,Term 模式 不会通过解析输入来跟踪当前目录 。但部分 Shell 可以主动告知 Term 当前目录,bash 1.15 及以上版本会自动实现这一点。
40.9. 终端模式
在 Term 模式中,在行模式和字符模式之间切换,使用以下命令:
C-c C-j- 切换到行模式 (
term-line-mode)。如果已经在行模式,则不执行任何操作。 C-c C-k- 切换到字符模式 (
term-char-mode)。如果已经在字符模式,则不执行任何操作。
以下命令仅在字符模式下可用:
C-c C-c- 向子 Shell 发送一个原始的
C-c(term-interrupt-subjob)。 C-c 字符- 等价于普通 Emacs 中的
C-x 字符。例如,C-c o会触发C-x o的全局绑定,该命令通常是「other-window」。
Term 模式有分页显示功能。启用后,输出会在每屏内容结束时暂停:
C-c C-q- 切换分页显示功能 (
term-pager-toggle)。该命令在行模式和字符模式下均有效。启用该功能后,模式行上会显示单词 'page';每当 Term 接收到超过一屏的输出时,就会暂停,并在模式行显示 '**MORE**' 。按SPC显示下一屏内容,或按?查看其他选项。界面与more程序类似。
40.10. 远程主机 Shell
你可以在 Term 窗口中登录到远程计算机,使用与普通终端相同的命令(例如 ssh 命令)。
需要输入密码的程序通常会禁止密码回显,因此密码不会显示在缓冲区中。如果缓冲区处于字符模式,效果与使用真实终端完全一致。如果处于行模式,密码会暂时可见,但按下 RET 回车键后会自动清除。(这一过程是自动的,无需特殊的密码处理。)
登录到其他机器时,你需要通过在远程登录命令的环境中设置环境变量 TERM ,来指定你所使用的终端类型。(如果使用 bash,可在远程登录命令之前直接赋值变量,中间无需逗号。)终端类型 'ansi' 或 'vt100' 在大多数系统上都能正常工作。
40.11. 串行终端
如果你的计算机上有设备连接到串口,可以通过输入 M-x serial-term 与其通信。该命令会询问串口名称和波特率,然后切换到一个新的 Term 模式缓冲区。Emacs 通过该缓冲区与串口设备交互,方式与普通 Term 模式下的终端一致。
串口的速率以 比特/秒 为单位,最常用的速率是 9600 比特/秒。你可以通过点击模式行来交互式修改速率。
点击模式行中的 '8N1' 还可以对串口进行更多配置。串口默认配置为 '8N1',含义是:每个字节包含 8 个数据位、无校验位、1 个停止位。
如果速率或配置错误,将无法与设备正常通信,窗口中很可能只会显示乱码。
41. 将 Emacs 用作服务器
很多程序可以调用你指定的编辑器来编辑某段文本。例如,版本控制程序会调用编辑器来填写版本控制日志(见版本控制),Unix 的邮件工具会调用编辑器来编写要发送的邮件。按照惯例,你选择的编辑器由环境变量 EDITOR 指定。如果把 EDITOR 设为 emacs ,Emacs 会被调用,但方式并不方便 —— 它会 启动一个全新的 Emacs 进程 。这很不方便,因为新的 Emacs 进程不会与已有的 Emacs 进程共享缓冲区、命令历史或其他信息。
你可以通过将 Emacs 设置为 edit server编辑服务器 来解决这个问题,让它 “监听” 外部的编辑请求并做出响应。启动 Emacs 服务器有多种方式:
- 在已有的 Emacs 进程中运行命令
server-start:可以输入M-x server-start,或者在你的初始化文件中加入表达式(server-start)(见 Emacs 初始化文件)。已有的 Emacs 进程就是服务器;当你退出 Emacs 时,服务器会随 Emacs 进程一同关闭。 - 以守护进程(daemon)方式运行 Emacs,使用 '
--daemon' 命令行选项之一。见初始化选项。以这种方式启动时,Emacs 会在初始化后调用server-start,且不会打开初始窗口,然后等待来自客户端的编辑请求。 - 运行
emacsclient命令并带上 '--alternate-editor=""' 命令行选项。这会在没有 Emacs 守护进程运行时才启动一个 Emacs 守护进程。 如果你的操作系统使用
systemd管理启动项,可以在登录时通过提供的 systemd unit file 自动以守护进程模式启动 Emacs。启用方法:systemctl --user enable emacs
(如果你的 Emacs 安装在非标准位置,可能需要将
emacs.service文件复制到标准目录,如~/.config/systemd/user/。)外部进程可以在指定套接字上发生连接事件时启动 Emacs 服务器,并将套接字传递给新的 Emacs 服务器进程。
systemd的套接字功能就是一个例子:systemd服务创建一个套接字并监听连接;当emacsclient首次连接时,systemd 会启动 Emacs 服务器,并将套接字交给它处理后续连接。使用该功能的配置示例:~/.config/systemd/user/emacs.socket:
[Socket] ListenStream=/path/to/.emacs.socket DirectoryMode=0700 [Install] WantedBy=sockets.target
(同时必须安装前面提到的
emacs.service文件。)ListenStream中的路径就是 Emacs 用来监听emacsclient连接的路径,你可以自行指定。
Emacs 服务器启动后,你可以使用名为 emacsclient 的 Shell 命令连接到 Emacs 进程并让它打开文件。然后你可以将环境变量 EDITOR 设置为 emacsclient ,这样外部程序就会使用 已有的 Emacs 进程 进行编辑23。
你可以在同一台机器上运行多个 Emacs 服务器,通过变量 server-name 给每个服务器设置唯一名称。例如: M-x set-variable RET server-name RET "foo" RET 会将服务器名称设为 foo 。 emacsclient 程序可以通过 '-s' 或 '-f' 选项按名称指定服务器(见 emacsclient 选项),具体取决于服务器是否使用 TCP 套接字(见 TCP Emacs 服务器)。
如果要运行多个 Emacs 守护进程(见初始化选项),可以这样为每个守护进程指定服务器名:
emacs --daemon=foo
Emacs 服务器可以在满足特定条件时 自动停止 。要启用此功能,将选项 server-stop-automatically 设置为以下值之一:
empty- 当服务器没有客户端、没有未保存的文件缓冲区、也没有正在运行的进程时,自动停止服务器。
delete-frame- 当最后一个客户端窗口被关闭时,会询问你是否保存所有未保存的文件缓冲区、是否停止所有未完成的进程;如果确认,服务器会停止。
kill-terminal- 当用
C-x C-c(save-buffers-kill-terminal) 关闭最后一个客户端窗口时,会询问你是否保存所有未保存的文件缓冲区、是否停止所有未完成的进程;如果确认,服务器会停止。
如果你用唯一名称定义了服务器,可以从另一个 Emacs 实例连接到该服务器,并使用 server-eval-at 函数在服务器上执行 Lisp 表达式。例如: (server-eval-at "foo" '(+ 1 2)) 会在名为 'foo' 的服务器上执行表达式 (+ 1 2) 并返回 3 。(如果不存在该名称的服务器,会抛出错误。)目前该功能主要对开发者有用。
如果你的操作系统桌面环境符合 freedesktop.org 标准 (大多数 GNU/Linux 及较新的类 Unix 图形界面都是如此),你可以使用菜单条目 'Emacs (Client)' 通过 emacsclient 连接到 Emacs 服务器。如果守护进程尚未运行,会自动启动。
41.1. TCP Emacs 服务器
Emacs 服务器通常在本地 Unix 域套接字 上监听连接。部分操作系统(如 MS-Windows)不支持本地套接字,此时服务器会改用 TCP 套接字 。即使系统支持本地套接字,某些场景下使用 TCP 套接字也更实用,例如需要 从远程机器连接 Emacs 服务器 时。你可以将变量 server-use-tcp 设为非空值,让 Emacs 监听 TCP 套接字而非本地套接字。如果你的操作系统不支持本地套接字,该选项默认为启用。
如果 Emacs 服务器设为使用 TCP,默认会在 localhost 接口 的随机端口上监听。你可以通过变量 server-host 和 server-port ,改为监听其他网络接口和 / 或固定端口。
TCP 套接字 不受文件系统权限控制 。为了对能通过 TCP 访问 Emacs 服务器的用户进行限制, emacsclient 必须向服务器发送 授权密钥 。该密钥通常由 Emacs 服务器 随机生成 ,这是推荐的使用方式。
如有需要,你可以通过设置变量 server-auth-key ,将授权密钥设为固定值。密钥必须由 64 个可打印 ASCII 字符 组成(不含空格),即从 '!' 到 '~' (十进制编码 33 到 126)之间的字符。你可以使用 M-x server-generate-key 生成随机密钥。
启动 TCP 版 Emacs 服务器时,Emacs 会创建一个 服务器文件 ,其中包含 TCP 连接信息,供 emacsclient 连接服务器使用。变量 server-auth-dir 指定存放该服务器文件的默认目录,默认为: ~/.emacs.d/server/ 。在没有带文件权限的本地套接字时,该目录的权限决定了哪些用户的 emacsclient 进程可以与 Emacs 服务器通信。如果 server-name 是一个 绝对路径 ,服务器文件会创建在该路径指定的位置。
若要让 emacsclient 通过 TCP 并使用指定服务器文件连接服务器,可以使用选项:'-f' 或 '--server-file' 或设置环境变量 EMACS_SERVER_FILE (见 emacsclient 选项)。如果 server-auth-dir 设为非标准路径,或 server-name 设为绝对路径, emacsclient 需要使用 服务器文件的绝对路径 。因为 emacsclient 内部硬编码了默认的 server-auth-dir ,用于解析相对文件名。
41.2. 调用 emacsclient
使用 emacsclient 最简单的方式是在 Shell 中运行命令: 'emacsclient file' 其中 file 是文件名。它会连接到 Emacs 服务器,并让该 Emacs 进程在已有的某个窗口中打开文件 —— 可以是图形窗口,也可以是文本终端窗口(见窗口与图形显示)。之后你可以切换到该窗口开始编辑。
如果没有 Emacs 服务器在运行, emacsclient 会报错并停止(你可以通过给 emacsclient 指定 '--alternate-editor=""' 选项避免这种情况,详见 emacsclientho 选项)。如果 Emacs 进程没有已打开的框架(以守护进程方式启动时就会出现这种情况,详见「将 Emacs 用作服务器」),那么 Emacs 会在你调用 emacsclient 的终端上打开一个新窗口。
你也可以强制让 emacsclient 新建窗口:
- 使用 '-c' 选项在图形显示器上新建图形窗口。
- 使用 '-t' 选项在文本终端上新建终端窗口。详见 emacsclient 选项。
如果你只在单个文本终端下操作,可以通过以下两种方法在 emacsclient 所在的 Shell 和 Emacs 服务器之间切换:(i) 在不同的虚拟终端上分别运行 Emacs 服务器和 emacsclient ,调用 emacsclient 后切换到 Emacs 服务器所在的虚拟终端;或者(ii) 在 Emacs 服务器内部,通过 Shell 模式(见交互式子 Shell)或 Term 模式(见 Term 模式)调用 emacsclient ;此时 emacsclient 只会阻塞 Emacs 内部的子 Shell,你仍然可以正常使用 Emacs 编辑文件。
当你在 Emacs 服务器中编辑完文件后,在对应缓冲区里按 C-x # (server-edit) 。这会保存文件,并向 emacsclient 发送消息让其退出。使用 EDITOR 环境变量的程序通常会等待编辑器(这里是 emacsclient)退出后再继续执行。
如果你想 放弃编辑 ,可以使用 M-x server-edit-abort 。它会通知 emacsclient 以异常状态退出,且不保存任何缓冲区。
你也可以给 emacsclient 传入多个文件名: 'emacsclient file1 file2 …' 它会让 Emacs 服务器依次打开这些文件。Emacs 会选中 file1 对应的缓冲区,并把其他缓冲区埋到缓冲区列表底部(见使用多个缓冲区)。 emacsclient 会在 所有指定文件都处理完毕 (即每个服务器缓冲区都按过 C-x # )后才退出。
完成一个服务器缓冲区的编辑后,该缓冲区会被关闭,除非它在服务器被要求打开之前就已经存在。但如果你把 server-kill-new-buffers 设为 nil ,则会使用另一套规则:只有当文件名匹配 server-temp-file-regexp 正则表达式时,编辑完成才会关闭缓冲区。这一配置用于区分某些临时文件。
每次按 C-x # 都会检查是否有其他等待处理的外部编辑请求,并自动切换到下一个待编辑文件。你也可以手动切换到服务器缓冲区,不一定要靠 C-x # 跳过去。但 C-x # 是告诉 emacsclient 你已编辑完成的标准方式。
如果把变量 server-window 设置为某个窗口或窗口框架, C-x # 就会始终在该窗口 / 框架中显示下一个服务器缓冲区。
当 emacsclient 连接时,服务器通常会输出一段提示,告诉你如何退出客户端窗口。如果把 server-client-instructions 设为 nil ,这段提示就会被关闭。
41.3. emacsclient 选项
你可以向 emacsclient 程序传递一些可选参数,例如:
emacsclient -c +12 file1 +4:3 file2
如 '+行号' 或 '+行号:列号' 的参数,用于为下一个文件参数指定行号(或行号与列号)。这些参数的行为与 Emacs 自身的命令行参数一致。参见「动作参数」。
emacsclient 支持的其他可选参数如下:
- '
-a command' - '
--alternate-editor=command' 如果
emacsclient无法连接到 Emacs,则执行指定的 Shell 命令。这在脚本中运行emacsclient时非常有用。命令中可以包含参数,参数可以像这样 "用引号括起来"。目前不支持引号转义。一个特殊例外:如果命令为空字符串,
emacsclient会以守护进程模式启动 Emacs(即 'emacs --daemon'),然后再次尝试连接。环境变量
ALTERNATE_EDITOR与 '-a' 选项作用相同。如果两者同时存在,命令行选项优先。- '
-c' - '
--create-frame' 新建一个图形 client frame客户端框架 ,而不是使用已有的 Emacs 框架。关于客户端框架中
C-x C-c的特殊行为,见下文。如果 Emacs 无法新建图形框架(例如无法连接 X 服务器),会尝试创建文本终端客户端框架,效果等同于使用 '-t' 选项。在 MS-Windows 上,单个 Emacs 会话不能同时在图形终端和文本终端上显示框架,也不能在多个文本终端上显示。因此,如果 Emacs 服务器运行在文本终端上, '
-c' 选项与 '-t' 选项一样,会在服务器当前文本终端中新建框架。如果使用 '
-c' 选项但不提供文件名,新框架默认显示*scratch*缓冲区。你可以通过变量initial-buffer-choice自定义这一行为(见「启动 Emacs」)。- '
-r' - '
--reuse-frame' - 如果没有已存在的框架,则新建图形客户端框架;否则使用已有框架。
- '-F alist'
- '
--frame-parameters=alist' - 为新建的图形框架设置参数(见「框架参数」)。
- '-d display'
- '
--display=display' - 告诉 Emacs 在指定的 X 显示端打开文件(假设存在多个 X 显示端)。
- '
-e' - '
--eval' 让 Emacs 执行一段 Emacs Lisp 代码,而不是打开文件。使用该选项时,
emacsclient的后续参数会被当作待执行的表达式列表,而非文件列表。通过
--eval传递复杂 Lisp 表达式有时需要对 Shell 特殊字符做繁琐转义。为避免这一点,你可以把表达式中 Lisp 函数所需的参数作为emacsclient的独立附加参数传入,并在表达式中用server-eval-args-left访问这些参数。注意:无论代码是否执行成功,你的表达式都必须从server-eval-args-left中移除已处理的参数(例如用pop),否则 Emacs 会尝试把这些参数当作独立 Lisp 表达式再次执行。- '-f server-file'
- '
--server-file=server-file' 指定用于通过 TCP 连接 Emacs 服务器的服务器文件(见 TCP 版 Emacs 服务器)。你也可以设置环境变量
EMACS_SERVER_FILE指向该服务器文件。(命令行选项优先级高于环境变量。)Emacs 服务器通常使用本地套接字监听连接,但也支持 TCP 连接。要连接 TCP 版 Emacs 服务器,
emacsclient需要读取包含服务器连接信息的服务器文件。该文件的名称由此选项指定,可以是相对于~/.emacs.d/server的文件名,或绝对路径。- '
-n' - '
--no-wait' - 让
emacsclient立即退出,而不是等待所有服务器缓冲区编辑完成。你可以在 Emacs 内任意时间编辑服务器缓冲区,并且在按C-x #时不会杀死这些缓冲区。 - '
-w' - '
--timeout=N' - 等待 Emacs 响应最多 N 秒,超时则放弃。若在指定时间内无响应,
emacsclient会显示警告并退出。默认值为 0,表示无限等待。 - '
--parent-id=id' - 通过 XEmbed 协议,在指定 ID 的父 X 窗口中打开
emacsclient窗口作为子窗口。目前该选项主要对开发者有用。 - '
-q' - '
--quiet' - 不显示关于等待 Emacs 或连接远程服务器套接字的提示信息。
- '
-u' - '
--suppress-output' - 不显示从服务器返回的结果。主要与 '
-e' 配合使用,当执行表达式仅为副作用而非获取返回值时。 - '-s server-name'
- '
--socket-name=server-name' 连接到指定 server-name名称 的 Emacs 服务器。(MS-Windows 不支持此选项。)服务器名称由 Emacs 端的变量
server-name指定。若省略此选项,emacsclient连接默认套接字。如果你将 Emacs 服务器的server-name设置为绝对路径,则在此选项中传入相同绝对路径,以让emacsclient连接到对应服务器。当你以守护进程方式启动 Emacs 并指定了服务器名称时,需要使用此选项。你也可以设置环境变量
EMACS_SOCKET_NAME指向服务器套接字。(命令行选项优先级更高。)- '
-t' - '
--tty' - '
-nw' - '
--no-window-system' 在当前文本终端上新建客户端框架,而不是使用已有 Emacs 框架。行为与 '
-c' 类似,区别在于它创建文本终端框架(见文本终端)。在 MS-Windows 上,如果 Emacs 服务器使用图形显示, '
-t' 与 '-c' 行为相同;如果服务器运行在文本终端上,则在当前文本终端新建框架。- '-T tramp-prefix'
- '
--tramp=tramp-prefix' 设置文件名前缀,让 Emacs 通过 TRAMP 访问远程机器上的文件(见「远程文件」与 TRAMP 手册)。这在从远程主机使用 Emacs 服务器时非常有用。通过 SSH 转发监听套接字,或 SSH 转发监听端口(参见 TCP 版 Emacs 服务器),并让 server-file服务器文件 在远程机器上可用,远程机器上的程序就可以将
EDITOR变的值设为emacsclient等,但文件并不会连接到远程机器上的 Emacs 服务器,而是会借助 TRAMP 在本地 Emacs 会话中打开。设置环境变量
EMACSCLIENT_TRAMP与 '-T' 选项效果相同,命令行选项优先。示例(本地主机 local、远程主机 remote):
local$ ssh -R "/home/%r/.emacs.socket":"${XDG_RUNTIME_DIR:-${TMPDIR:-/tmp}/emacs%i}${XDG_RUNTIME_DIR:+/emacs}/server" remote remote$ export EMACS_SOCKET_NAME=$HOME/.emacs.socket remote$ export EMACSCLIENT_TRAMP=/ssh:remote: remote$ export EDITOR=emacsclient remote$ $EDITOR /tmp/foo.txt # 会在本地 Emacs 中打开
如果你所使用的平台中
emacsclient不采用 Unix 域套接字(例如 Windows 系统),或者你的 SSH 实现不支持套接字转发(比如低于 6.7 版本的 OpenSSH),可以改用 TCP 端口转发。在本示例中,假设本地 Emacs 监听 TCP 端口 12345。进一步假设/home位于共享文件系统上,因此服务器文件~/.emacs.d/server/server在两台主机上均可读取。local$ ssh -R12345:localhost:12345 remote remote$ export EMACS_SERVER_FILE=server remote$ export EMACSCLIENT_TRAMP=/ssh:remote: remote$ export EDITOR=emacsclient remote$ $EDITOR /tmp/foo.txt # 会在本地 Emacs 中打开
- '
-V' - '
--version' - 打印版本信息并退出。
- '
-H' - '
--help' - 打印使用信息并退出。
由 '-c' 或 '-t' 创建的新图形 / 文本终端窗口被视为 client frames客户端框架 。从客户端框架新建的任何框架也属于客户端框架。
如果你在客户端框架中按 C-x C-c (save-buffers-kill-terminal),该命令不会像平常一样杀死整个 Emacs 会话,而是只删除当前客户端框架。此外,如果该客户端框架有 emacsclient 正在等待(即未使用 -n ),Emacs 会删除同一客户端的所有其他窗口,并将该客户端的所有服务器缓冲区标记为已完成,如同在每个缓冲区都按了 C-x # 。如果删除客户端框架后已无任何框架剩余,Emacs 会话才会退出。
一个例外:当 Emacs 以守护进程启动时,所有框架都被视为客户端框架, C-x C-c 永远不会杀死 Emacs。要关闭守护进程会话,请使用 M-x kill-emacs 。
注意:'-t' 与 '-n' 选项相互矛盾: '-t 表示接管当前文本终端并新建客户端窗口; '-n' 表示不接管文本终端。如果同时使用这两个选项,Emacs 会在已有窗口中打开文件,而不是新建客户端窗口,这会抵消 '-t' 的效果。
42. 打印硬拷贝
Emacs 提供了用于打印整个缓冲区或部分缓冲区内容的命令。你可以直接调用这些打印命令(具体说明如下),也可以通过菜单栏中的「File文件」菜单使用。
除本节介绍的命令外,你还可以在 Dired(参见文件操作)和日程记录(参见显示日程)中打印纸质副本。你也可以使用命令 M-x htmlfontify-buffer 将 Emacs 缓冲区 “打印” 为 HTML 格式,该命令会将当前缓冲区转换为 HTML 文件,并使用基于 CSS 的标记替代 Emacs 面(字体样式)。此外,Org 模式支持将 Org 文件打印为多种格式(例如 PDF),参见 Org 模式。
M-x print-buffer- 打印当前缓冲区的纸质副本,并添加包含文件名与页码的页标题。
M-x lpr-buffer- 打印当前缓冲区的纸质副本,不添加页标题。
M-x print-region- 功能与
print-buffer类似,但仅打印当前选中区域。 M-x lpr-region- 功能与
lpr-buffer类似,但仅打印当前选中区域。
在大多数操作系统中,上述打印命令会通过调用 lpr 程序提交打印任务。若要更换打印程序,可以自定义变量 lpr-command 。若要为打印程序指定额外参数,可以自定义列表变量 lpr-switches ,其值应为一组选项字符串,每个字符串以 '-' 开头(例如字符串 '-w80' 表示设置行宽为 80 列),默认值为空列表 nil 。
若要指定使用的打印机,可以设置变量 printer-name 。默认值 nil 表示使用系统默认打印机;将其设为打印机名称(字符串)后,该名称会通过 '-P' 参数传递给 lpr 。如果你不使用 lpr ,则应通过 lpr-printer-switch 指定对应的参数。
变量 lpr-headers-switches 同样用于指定生成页标题时的额外参数。变量 lpr-add-switches 用于控制是否向打印程序提供 '-T ' 和 '-J' 选项(适用于 lpr ):设为 nil 表示不添加这些选项(如果你的打印程序与 lpr 不兼容,应使用该值)。
42.1. PostScript 纸质打开
这些命令将缓冲区内容转换为 PostScript 格式,既可直接打印,也可输出到另一个 Emacs 缓冲区。
M-x ps-print-buffer- 以 PostScript 格式打印当前缓冲区。
M-x ps-print-region- 以 PostScript 格式打印当前选中区域。
M-x ps-print-buffer-with-faces- 以 PostScript 格式打印当前缓冲区,并利用 PostScript 特性显示文本所使用的面(字体、颜色等样式)。
M-x ps-print-region-with-faces- 以 PostScript 格式打印当前选中区域,并显示文本所使用的样式。
M-x ps-spool-buffer- 为当前缓冲区生成并暂存 PostScript 图像。
M-x ps-spool-region- 为当前选中区域生成并暂存 PostScript 图像。
M-x ps-spool-buffer-with-faces- 为当前缓冲区生成并暂存 PostScript 图像,同时显示所使用的样式。
M-x ps-spool-region-with-faces- 为当前选中区域生成并暂存 PostScript 图像,同时显示所使用的样式。
M-x ps-despool- 将暂存的 PostScript 内容发送到打印机。
M-x handwrite- 为当前缓冲区生成 / 打印手写风格的 PostScript 效果。
ps-print-buffer 和 ps-print-region 命令以 PostScript 格式打印缓冲区内容,一个打印整个缓冲区,另一个只打印选中区域。~ps-print-buffer-with-faces~ 和 ps-print-region-with-faces 命令作用类似,但会利用 PostScript 特性显示缓冲区文本的外观(字体与颜色)。
交互使用时,若带上前缀参数( C‑u ),这些命令会提示输入文件名,并将 PostScript 图像保存到该文件,而不是发送到打印机。
名称中包含 'spool' (暂存)而非 'print' (打印)的命令,会在 Emacs 缓冲区中生成 PostScript 输出,而不是直接发送到打印机。
使用命令 ps-despool 可将暂存的图像发送到打印机。该命令会把由 '‑spool‑' 系列命令生成的 PostScript 内容发送至打印机;若带上前缀参数( C‑u ),则会提示输入文件名,并将暂存的 PostScript 图像保存到文件,而非发送到打印机。
M-x handwrite 更偏向趣味用途。它会将当前缓冲区生成类似手写草书的 PostScript 效果,可在 handwrite 组中进行自定义。该函数仅支持 ISO 8859‑1 字符。
42.2. PostScript 纸质打开相关变量
所有 PostScript 打印命令都使用变量 ps-lpr-command 和 ps-lpr-switches 来指定输出方式。 ps-lpr-command 用于指定要运行的命令名称, ps-lpr-switches 用于指定命令行参数, ps-printer-name 用于指定打印机。如果你没有手动设置前两个变量,它们会从 lpr-command 和 lpr-switches 继承初始值。如果 ps-printer-name 为 nil ,则使用 printer-name 。
变量 ps-print-header 控制这些命令是否为每页添加页眉行:将其设为 nil 即可关闭页眉。
如果你的打印机不支持彩色,应将 ps-print-color-p 设为 nil 以关闭彩色处理。默认情况下,如果显示器支持彩色,Emacs 会生成带色彩信息的打印输出;在黑白打印机上,颜色会用灰度级模拟。即使屏幕颜色只使用灰度,这也可能导致输出难以阅读甚至无法辨认。
你也可以将 ps-print-color-p 设置为 black-white ,让颜色在黑白打印机上显示效果更好。其原理是利用 ps-black-white-faces 中的信息,通过可自定义的灰度列表,并结合粗体、斜体等样式属性来表现颜色。
默认情况下,PostScript 打印会忽略样式(face)的背景色,除非变量 ps-use-face-background 不为 nil 。这样做是为了避免与斑马条纹、背景图片或文字产生不必要的干扰。
变量 ps-paper-type 指定要使用的纸张尺寸;合法值包括:a4、a3、a4small、b4、b5、executive、ledger、legal、letter、letter-small、statement、tabloid。默认为 letter。你可以通过修改变量 ps-page-dimensions-database 定义更多纸张尺寸。
变量 ps-landscape-mode 指定页面打印方向。默认值为 nil ,表示纵向(portrait);任何非 nil 值都表示横向(landscape)。
变量 ps-number-of-columns 指定打印的列数,在横向和纵向模式下均生效。默认为 1。
变量 ps-font-family 指定打印普通文本所使用的字体族。合法值包括:Courier、Helvetica、NewCenturySchlbk、Palatino 和 Times。变量 ps-font-size 指定普通文本的字号,默认为 8.5 磅。 ps-font-size 的值也可以是两个浮点数组成的 cons 对:一个用于横向模式,另一个用于纵向模式。
Emacs 支持的文字书写体系与字符,比普通的 PostScript 打印机更为丰富。因此,缓冲区中的部分字符,可能无法使用打印机内置字体正常打印。你可以用 GNU Intlfonts 字体包 来补充打印机自带的字体,也可以让 Emacs 只使用 Intlfonts 字体。变量 ps-multibyte-buffer 用于控制这一行为:
- 默认值
nil:适合打印 ASCII 和 Latin‑1 字符; - 值为
non‑latin‑printer:适用于内置了 ASCII、Latin‑1、日文和韩文字体的打印机; - 值为
bdf‑font:对所有字符统一使用 Intlfonts 字体包中的 BDF 字体; - 值为
bdf‑font‑except‑latin:对 ASCII 和 Latin‑1 字符使用打印机内置字体,其余字符使用 Intlfonts 的 BDF 字体。
要使用 BDF 字体,Emacs 需要知道字体的存放位置。变量 bdf-directory-list 用于保存 Emacs 搜索字体的目录列表;其默认值只包含一个目录: /usr/local/share/emacs/fonts/bdf 。
这些命令的许多其他自定义变量,都在 Lisp 文件 ps-print.el 和 ps-mule.el 中定义并说明。
42.3. 打印功能包
Emacs 基础的纸质打印功能可以通过 Printing 包 进行扩展。该包提供了易用的界面,用于选择打印内容、在打印前预览 PostScript 文件,以及设置各类打印选项,例如打印页眉、横向 / 纵向模式、双面打印模式等。在 GNU/Linux 或 Unix 系统中,Printing 包依赖 gs 和 gv 工具,这两个工具随 GhostScript 程序一同发布。在 MS-Windows 系统中,可以使用 Ghostscript 的 gstools 移植版。
使用方法
要使用 Printing 包,需在你的初始化文件(参见《Emacs 初始化文件》)中添加:
(require 'printing) (pr-update-menus)
pr-update-menus 函数会将菜单栏中默认的打印命令替换为包含多种打印选项的 'Printing' 子菜单。
你也可以执行命令: M-x pr-interface RET 该命令会创建一个名为 *Printing Interface* 的缓冲区(类似自定义设置缓冲区),在其中可以配置打印选项。选定打印内容与方式后,点击 Print 按钮即可启动打印任务(点击 mouse-2 鼠标中键,或将光标移至按钮上按 RET 回车)。如需了解更多选项的详细说明,可使用 'Interface Help' 按钮。
43. 文本排序
Emacs 提供多条用于对缓冲区中文本进行排序的命令。所有命令均作用于区域(region)内的内容。它们会将区域内的文本划分成多条 sort records排序记录 ,为每条记录确定一个 sort key排序键 ,然后按照排序键决定的顺序重新排列这些记录。记录会按其键的字母顺序排列;若为数值排序,则按数值顺序排列。在字母排序中,按照 ASCII 字符序列,所有大写字母「A」到「Z」排在小写字母「a」之前(不过下文介绍的 sort-fold-case 可以改变这一行为)。
各种排序命令的区别在于,它们 如何将文本划分成排序记录 ,以及 使用每条记录的哪一部分作为排序键 。大多数命令会把 每一行 当作一条独立的排序记录,但也有一些命令以 段落 或 页 作为排序记录。大多数排序命令会把 整条排序记录 本身作为排序键,但也有一些命令只使用记录的一 部分 作为排序键。
M-x sort-lines- 将区域按行划分,并通过比较整行文本进行排序。传入数值前缀参数表示按降序排序。
M-x sort-paragraphs- 将区域按段落划分,并通过比较整个段落(忽略开头空行)进行排序。传入数值前缀参数表示按降序排序。
M-x sort-pages- 将区域按页划分,并通过比较整页文本(忽略开头空行)进行排序。传入数值前缀参数表示按降序排序。
M-x sort-fields将区域按行划分,并通过比较每行中的某个字段进行排序。字段由空白字符分隔:一行中第一组连续的非空白字符为字段 1,第二组为字段 2,依此类推。
使用数值前缀参数指定按第几个字段排序:1 表示按第 1 个字段,以此类推;默认为 1。负数参数表示从右往左数字段:例如 -1 表示按最后一个字段排序。如果多行在用于排序的字段内容相同,它们会保持在原缓冲区中的相对顺序。
M-x sort-numeric-fields- 用法与
M-x sort-fields类似,区别在于:会将指定字段转换为整数后再比较。作为文本时, '10' 排在 '2' 前面;作为数字时, '10' 排在 '2' 后面。默认情况下,数字按sort-numeric-base解释;以 '0x' 开头的数字会被解释为十六进制,以 '0' 开头的数字会被解释为八进制。 M-x sort-columns- 用法与
M-x sort-fields类似,区别在于:用于比较的文本来自每行中固定的列范围。带前缀参数时按逆序排序。此命令的更多细节见下文。 M-x reverse-region- 将区域内的行顺序反转。这在需要按字段降序排序时很有用,因为相关排序命令本身不直接支持该功能。
示例,如果缓冲区包含如下内容:
On systems where clash detection (locking of files being edited) is implemented, Emacs also checks the first time you modify a buffer whether the file has changed on disk since it was last visited or saved. If it has, you are asked to confirm that you want to change the buffer.
对整个缓冲区执行 M-x sort-lines 后结果如下:
On systems where clash detection (locking of files being edited) is implemented, Emacs also checks the first time you modify a buffer saved. If it has, you are asked to confirm that you want to change the buffer. whether the file has changed on disk since it was last visited or
可以看到大写字母 'O' 排在所有小写字母之前。如果改用 C-u 2 M-x sort-fields ,结果如下:
implemented, Emacs also checks the first time you modify a buffer saved. If it has, you are asked to confirm that you want to change the buffer. On systems where clash detection (locking of files being edited) is whether the file has changed on disk since it was last visited or
此时排序键分别为:Emacs、If、buffer、systems 和 the。
M-x sort-columns 需要更多解释。你可以将光标置于某一列,标记置于另一列,以此指定列范围。由于这意味着你无法将光标或标记放在待排序文本首行的开头,该命令对「region区域」采用了特殊定义:光标所在行、标记所在行,以及二者之间的所有行,都被视为区域的一部分。
例如,要按第 10–15 列的内容对一个表格排序:可以将标记放在表格首行的第 10 列,光标放在表格末行的第 15 列,然后执行 sort-columns 。反之,标记放在首行第 15 列、光标放在末行第 10 列也可以。
可以将其理解为:对光标与标记围成的 矩形区域排序 ,不同之处在于:矩形左侧和右侧的文本会随矩形内的文本一起移动。详见「矩形操作(Rectangles)」。
如果 sort-fold-case 不为 nil ,多数排序命令在比较时会忽略大小写差异。
44. 图片编辑
要编辑由文本字符构成的图片(例如,以注释形式出现在程序中、将寄存器划分为多个字段的示意图),可使用命令 M-x picture-mode 进入图片模式(Picture mode)。
在图片模式中,编辑基于文本的 quarter-plane model 四分之一平面模型 :文本字符分布在向右、向下无限延伸的区域中。该模型中不存在 “行尾” 的概念,最多只能确定行中最后一个非空白字符的位置。
当然,Emacs 本质上始终将文本视为字符序列,行也确实存在行尾。但图片模式会用变体命令替换最常用的编辑命令,以此模拟四分之一平面模型。这些变体通过插入空格或将制表符转换为空格来实现效果。
图片模式重新定义了 Emacs 大部分基础编辑命令,使其功能基本不变,但按照四分之一平面模型的方式工作。此外,图片模式还定义了一系列以 C-c 开头的按键,用于执行专用的图片编辑命令。
其中 C-c C-c 尤为重要。图片通常是某个文件的一部分,而该文件一般在其他主模式下编辑。图片模式会记录之前的主模式名称,之后你可以用 C-c C-c 命令 (picture-mode-exit) 返回原模式。 C-c C-c 还会删除行尾空格,除非提供数值前缀参数。
图片模式的专用命令在其他模式中同样可用(前提是已加载 picture 库),但仅在图片模式下绑定了按键。下面的说明会提到 “移动一列” 等操作,但所有图片模式命令对数值参数的处理方式与普通命令一致。
开启图片模式时会运行钩子 picture-mode-hook 。图片模式的更多扩展功能可在 artist.el 中找到。
44.1. 图片模式基本编辑
在图片模式中,大部分按键功能与常规模式一致,但采用四象限平面的操作逻辑。例如, C-f 被重新绑定为 picture-forward-column 命令,将光标向右移动一列,必要时插入空格,确保不受行尾实际位置影响。 C-b 被重新绑定为 picture-backward-column ,始终将光标左移一列,必要时将制表符转换为多个空格。 C-n 和 C-p 分别被绑定为 picture-move-down 和 picture-move-up ,会自动插入空格或转换制表符,保证光标严格停在同一列。 C-e 执行 picture-end-of-line ,移动到行内最后一个非空白字符之后。 C-a 执行 picture-beginning-of-line 。(屏幕模式的选择不影响行首定位;该命令唯一额外作用是将当前图片列位置重置为 0。)
文本插入会通过 Overwrite mode覆盖模式 适配四象限屏幕模型(详见次要模式)。自插入字符会逐列覆盖已有文本,而非将文本向右挤开。 RET 执行 picture-newline ,仅跳至下一行行首,使新输入内容覆盖该行。
在图片模式中,原本用于删除或剪切文本的命令,会改为 erase擦除 文本(用空格替代)。
DEL(picture-backward-clear-column):用空格替换前一个字符,而非删除,光标同时左移。C-d(picture-clear-column):用空格替换后续一个或多个字符,但光标不移动。(若想擦除为空格并跳过这些字符,可使用空格SPC。)C-k(picture-clear-line):会真正清除行内容,但不会删除缓冲区中的换行符。
若要执行 真正的插入操作 ,需使用专用命令:
C-o(picture-open-line):在当前行下方新建空白行,不会拆分现有行。C-M-o(split-line):在图片模式中保持原有含义,未被修改。C-j(picture-duplicate-line):在当前行下方插入一行内容相同的副本。
若要在图片模式中真正删除文本,可使用: C-w 、 C-c C-d (功能同普通模式下的 C-d ,即 delete-char ),或图片模式矩形操作命令(详见图片模式矩形命令)。
44.2. 插入后移动控制
由于图片模式下输入字符会覆盖原有内容并移动光标,因此对光标移动方式没有本质限制。默认情况下光标向右移动,但你可以为输入字符后指定8个方向(水平、垂直与对角线)的移动规则。这在缓冲区中绘制线条时非常实用。
方向控制快捷键
C-c <C-c LEFT- 插入后向左移动 (
picture-movement-left)。 C-c >C-c RIGHT- 插入后向右移动 (
picture-movement-right)。 C-c ^C-c UP- 插入后向上移动 (
picture-movement-up)。 C-c .C-c DOWN- 插入后向下移动 (
picture-movement-down)。 C-c `C-c Home- 插入后向左上方移动(西北方向,
picture-movement-nw)。 C-c 'C-c PageUpC-c prior- 插入后向右上方移动(东北方向,
picture-movement-ne)。 C-c /C-c End- 插入后向左下方移动(西南方向,
picture-movement-sw)。 C-c \C-c PageDownC-c next- 插入后向右下方移动(东南方向,
picture-movement-se)。
相对方向移动
有两个命令会根据当前设定的插入方向来移动光标:
C-c C-f(picture-motion):向当前设定的插入方向移动。C-c C-b(picture-motion-reverse):向当前设定方向的反方向移动。
44.3. 图片模式制表符
图片模式提供两种类制表符的操作方式。
使用 M-TAB (picture-tab-search) 进行 基于上下文的跳格 。
- 不带参数时:光标跳至上一个非空行中,位于当前光标右侧、下一个 “有效字符” 的正下方。 这里的 “下一个” 指 水平位置大于当前光标起始位置 。
- 带前缀参数(如
C-u M-TAB)时:跳至当前行中下一个这样的 “有效字符” 处。
M-TAB 不会修改文本,仅移动光标。“有效字符” 由变量 picture-tab-chars 定义,该变量用于指定一组字符。其语法与正则表达式中 '[…]' 内部的语法一致,但不包含方括号 '[' 和 ']' 。默认值为 "!-~" (表示所有可打印 ASCII 字符)。
TAB 键本身执行 picture-tab ,基于 当前制表位设置 工作,相当于图片模式版的 tab-to-tab-stop 。
- 通常仅移动光标;
- 若带数字参数,则会清空移动路径上的文本。
C-c TAB (picture-set-tab-stops) 将上下文跳格与制表位跳格结合起来。该命令会把当前行中 M-TAB 识别为有效字符的位置,设为制表位。配合 TAB 使用,可达到基于上下文跳格的效果;但在适用场景下,直接用 M-TAB 更方便。
为避免图片中出现真实制表符导致错乱(例如防止 C-x TAB 破坏画面),可以将变量 indent-tabs-mode 设为 nil 。
44.4. 图片模式矩形命令
图片模式定义了一批针对文本矩形区域操作的命令,其行为符合四象限平面模型。标准的矩形命令同样可以使用,详见 “矩形操作”(Rectangles)章节。
命令一览
C-c C-k- 用空格清空选中的矩形区域 (
picture-clear-rectangle)。带前缀参数时,则直接删除该区域文本。 C-c C-w r- 功能类似,但会先将矩形内容保存到寄存器 r 中 (
picture-clear-rectangle-to-register)。详见 “寄存器”(Registers)章节。 C-c C-y- 将上一次删除的矩形以覆盖方式粘贴到缓冲区,左上角对齐当前光标 (
picture-yank-rectangle)。带参数时,则改为插入而非覆盖。 C-c C-x r- 功能类似,但使用寄存器 r 中保存的矩形 (
picture-yank-rectangle-from-register)。
与标准矩形命令的区别
图片模式的矩形命令 C-c C-k (picture-clear-rectangle) 和 C-c C-w (picture-clear-rectangle-to-register) 与标准矩形命令不同:它们 默认清空矩形(用空格填充) ,而不是直接删除;这与图片模式中 C-d 的修改逻辑类似。
不过,在图片模式中有时也需要真正删除矩形,因此这些命令在带数字参数时会执行删除操作。无论是否带参数, C-c C-k 都会保存矩形内容,供 C-c C-y 使用。
图片模式的矩形粘贴命令与标准命令的区别:它们默认 覆盖 而非插入。这和图片模式中其他文本的插入逻辑一致。 C-c C-y (picture-yank-rectangle) 粘贴最近删除的矩形(以覆盖方式), C-c C-x (picture-yank-rectangle-from-register) 则对指定寄存器中的矩形做同样操作。
45. 二进制文件编辑
Emacs 提供一个用于编辑二进制文件的专用主模式:Hexl mode。使用方法:用 M-x hexl-find-file 代替 C-x C-f 打开文件。该命令会将文件内容转换成十六进制形式供你编辑;保存文件时,会自动转换回二进制。
你也可以使用 M-x hexl-mode 将当前已打开的缓冲区转换成十六进制格式。如果你先用普通方式打开文件,之后才发现它是二进制文件,这个功能就会很方便。
在 Hexl 模式下,插入文本始终是覆盖模式,目的是降低意外破坏文件数据对齐的风险。普通文本字符会直接覆盖自身(即用相同字符覆盖)。另有一些命令可通过编码插入特殊字符。大多数光标移动键以及 C-x C-s 在 Hexl 模式下效果与普通模式一致。下面列出 Hexl 模式其他重要的专用命令:
C-M-d- 输入十进制编码,插入一个字节。
C-M-o- 输入八进制编码,插入一个字节。
C-M-x- 输入十六进制编码,插入一个字节。
C-M-a- 跳转到 512 字节页的起始位置。
C-M-e- 跳转到 512 字节页的末尾。
C-x [- 跳转到 1K 字节页的起始位置。
C-x ]- 跳转到 1K 字节页的末尾。
M-g- 跳转到十六进制指定的地址。
M-j- 跳转到十进制指定的地址。
C-c C-c- 退出 Hexl 模式,回到进入该模式之前缓冲区所在的主模式。
其他 Hexl 命令还支持插入二进制字节串、按短整型 / 整型移动等;输入 C-h a hexl- TAB 可查看详情。
Hexl 模式也可用于编辑文本文件。当文本文件包含特殊字符或使用不常见编码时(见编码系统),这会很方便。为此,Hexl 中插入字节的命令同样可插入 ASCII 与非 ASCII 字符(包括多字节字符)。要用 Hexl 编辑文本文件,先正常打开文件,再执行 M-x hexl-mode RET 切换模式即可。之后你可以直接输入文本字符。但插入 多字节字符 需要特别小心,避免生成非法多字节序列:应当在光标位于文件中多字节序列的第一个字节时开始输入。
46. 保存 Emacs 会话
你可以使用 desktop 库在不同会话之间保存 Emacs 的状态。保存的 Emacs desktop configuration 桌面配置 包括:缓冲区、对应的文件名、主模式、缓冲区位置、窗口与框架配置,以及一些重要的全局变量。
要启用该功能,可以通过自定义缓冲区(参见简易自定义界面)将 desktop-save-mode 设置为 t ,以便在后续会话中生效;或者在你的初始化文件(参见 Emacs 初始化文件)中添加这一行:
(desktop-save-mode 1)
如果你在初始化文件中开启了 desktop-save-mode ,那么 Emacs 启动时会在 desktop-path (默认为用户 Emacs 目录,然后是家目录)中查找已保存的桌面,并使用找到的第一个桌面。当 Emacs 在开启 desktop-save-mode 的状态下运行时,默认会在桌面配置发生变化时自动保存桌面。变量 desktop-auto-save-timeout 决定 Emacs 检查桌面修改的频率。当你退出 Emacs 时,桌面也会被保存。
如果你不希望 Emacs 加载任何已保存的桌面配置,可以在命令行启动时指定选项 '--no-desktop' 。这会在当前会话中关闭 desktop-save-mode 。使用 '--no-init-file' 选项启动 Emacs 同样会禁止加载桌面,因为它会跳过通常用来开启 desktop-save-mode 的初始化文件。
你可以在 不同目录 下保存各自独立的桌面配置;只要将 desktop-path 自定义为把 . (当前目录)放在其他目录前面,那么从存有桌面配置的目录启动 Emacs,就会恢复对应目录的桌面。你可以使用 M-x desktop-change-dir 保存当前桌面,并重新加载另一个目录下保存的桌面。输入 M-x desktop-revert 可回到上一次加载的桌面。
Emacs 保存桌面的文件在会话运行期间会被 加锁 ,避免被另一个 Emacs 会话意外覆盖。正常退出 Emacs 时该锁会被移除,但如果 Emacs 或系统崩溃,锁文件会保留。重启 Emacs 时,默认会询问你是否使用这个被锁定的桌面文件。你可以通过自定义变量 desktop-load-locked-desktop 来避免这个询问:
- 设置为
nil:表示这种情况下永远不加载桌面 - 设置为
t:表示不询问直接加载 - 你也可以将其设为特殊值
check-pid:表示只有当锁定该桌面的 Emacs 进程不在本机运行时才加载。在多用户环境中(例如家目录通过 NFS 等远程挂载),锁定的 Emacs 可能仍在另一台机器运行,这种情况下 不建议 使用check-pid。
当 Emacs 以守护进程(daemon)模式启动时,无法向你提问,因此如果发现桌面文件被锁定,将不会加载,除非 desktop-load-locked-desktop 设为 t 。注意:在守护进程模式下恢复桌面还存在其他问题:例如守护进程无法使用图形界面功能,因此框架位置、大小、装饰等参数无法恢复。出于这个原因,你可能希望将守护进程下的桌面恢复延迟到 第一个客户端连接时 再执行:可以在 server-after-make-frame-hook 钩子中添加一个函数(见Creating Frames),调用 desktop-read (见下文)。
你可以随时使用命令 M-x desktop-save 强制立即保存当前桌面。这在以下情况很有用:
- 你不想使用自动恢复,因此没有开启
desktop-save-mode - 或你对桌面做了大量修改,希望确保在 Emacs 或系统崩溃时配置不会丢失
如果当前 Emacs 会话尚未加载任何桌面,可以使用 M-x desktop-read 恢复之前保存的桌面。
默认情况下,桌面会尝试保存并恢复 框架与窗口配置 。要禁用此功能,将 desktop-restore-frames 设置为 nil 。(可查看该变量的文档,了解相关选项以精细调整行为。)
当桌面恢复框架和窗口配置时,它会使用已记录的框架参数值, 忽略 你在初始化文件(参见《Emacs 初始化文件》)中对这些参数的任何设置。这意味着,恢复后的框架所使用的字体、外观等参数,将来自你上一次退出 Emacs 会话时保存在桌面文件里的数据;初始化文件中对这些参数的配置会被忽略。若要禁用此行为,可以自定义变量 frameset-filter-alist ,过滤掉你不希望被恢复的框架参数;之后这些参数就会按照你在初始化文件中的自定义设置来生效。
默认 不保存 访问远程文件的缓冲区信息。如需改变这一行为,可自定义变量 desktop-files-not-to-save 。这种情况下,你可能还需要自定义 remote-file-name-access-timeout ,它表示停止恢复远程文件缓冲区的超时秒数,避免 Emacs 在恢复包含远程文件的会话时被阻塞。
默认会一次性恢复桌面中的所有缓冲区。但如果缓冲区数量很多,速度可能较慢。你可以通过变量 desktop-restore-eager 指定 立即恢复的最大缓冲区数量 ,剩下的缓冲区会在 Emacs 空闲 时延迟恢复 。
输入 M-x desktop-clear 清空 Emacs 桌面;例如,当你想接下来用 M-x desktop-read 切换到另一个桌面时很有用。 desktop-clear 命令会关闭除内部缓冲区外的所有缓冲区,并清空 desktop-globals-to-clear 中列出的全局变量。如果你希望保留某些缓冲区,可以自定义变量 desktop-clear-preserve-buffers ,它的值是一个正则表达式列表,匹配的缓冲区名不会被关闭。
如果你希望在会话间保存 小缓冲区历史记录 ,可以使用 savehist 库。你也可以将选定的小缓冲区历史变量添加到 desktop-globals-to-save 中,让它们随 desktop-save-mode 一起保存。
47. 递归编辑层级
recursive edit递归编辑 指的是这样一种场景:你正在执行某个 Emacs 命令的过程中,又使用 Emacs 命令进行任意编辑操作。例如,在查询替换( query-replace )过程中按下 C-r ,就会进入递归编辑,此时你可以修改当前缓冲区。退出递归编辑后,会回到之前的 query-replace 操作。详见查询替换。
退出递归编辑,意味着返回到未执行完的命令中,让其继续执行。退出命令是: C-M-c (exit-recursive-edit)。
你也可以 abort中止 递归编辑。这与退出类似,但会 直接终止 未执行完的命令。使用命令 C-] (abort-recursive-edit) 。详见退出与中止。
当你处于递归编辑时,模式行会在主模式和次要模式名称的外层括号外,再显示一对方括号 '[]' 来提示。所有窗口的模式行都会这样显示,因为递归编辑是整个 Emacs 的全局状态,而非某个窗口或缓冲区独有。
递归编辑可以 嵌套 (递归中再进入递归)。例如,在查询替换中按 C-r 进入递归编辑后,你又执行了某个会进入调试器的命令,这就会在 C-r 的递归编辑层内,再开启一层调试器的递归编辑。模式行会为 每一层 正在进行的递归编辑显示一对方括号。
退出内层递归编辑(例如使用调试器的 c 命令)会 恢复执行上一层的命令 。当上一层命令执行完毕后,你可以再用 C-M-c 退出上一层递归编辑,依此类推。 退出只作用于最内层 。中止也只会退出一层递归编辑,立即回到上一层命令。如有需要,你可以继续中止上一层。
另外,命令 M-x top-level 会中止所有层级的递归编辑,直接回到最顶层命令读取状态。如果迷你缓冲区处于激活状态,它也会一并退出。
在递归编辑中编辑的文本,不一定与顶层编辑的是同一个。这取决于触发递归编辑的命令。如果调用递归编辑的命令先切换到了另一个缓冲区,你在递归编辑中操作的就是那个缓冲区。无论如何,在递归编辑中你仍可以 照常切换缓冲区 (只要切换按键没有被重新绑定)。理论上你甚至可以在递归编辑里完成之后所有的编辑、打开文件等操作,但这偶尔会带来意外后果(如栈溢出)。因此记得在不需要时及时退出或中止递归编辑。
总体而言,GNU Emacs 尽量减少递归编辑层级的使用。因为它强制你必须按特定顺序返回 —— 从最内层一层层回到顶层。在可行的情况下,我们会将不同操作放在 独立的缓冲区 中,让你可以自由切换。有些命令会切换到新的主模式,并提供返回命令。这些方式让你能按自己选择的顺序回到未完成的任务,更灵活。
48. 超链接与网页导航功能
以下小节将介绍用于处理 Emacs 缓冲区文本中 URL 及其他类型链接的便捷功能。
48.1. 使用 EWW 网页浏览
48.2. 嵌入式 WebKit 组件
如果 Emacs 在编译时启用了对应的支持包,就可以在缓冲区中显示浏览器组件。命令 M-x xwidget-webkit-browse-url 会要求输入一个 URL,并在浏览器组件中显示。默认情况下,该 URL 取自光标所在或光标之前的位置;但如果存在激活的选区(参见标记与选区),则默认 URL 来自选区内容(会自动去除其中的空白字符)。
执行该命令后,Emacs 会新建一个缓冲区,在其中用嵌入式浏览器显示指定的 URL。该缓冲区会进入 Xwidget-WebKit 模式(与图片模式类似,参见查看图片文件),提供一键式命令用于滚动组件、调整大小和重新加载页面。在该缓冲区中按 C-h b 可查看所有按键绑定。
默认情况下,在 xwidget webkit 缓冲区中输入可自插入字符不会产生效果,或会触发某些特殊动作。若想让这些字符及常用编辑键正常输入,可以启用 xwidget-webkit-edit-mode ,该模式会重新定义按键,使其直接传递给 WebKit 组件。
你也可以在 xwidget webkit 缓冲区中按 e 来启用 xwidget-webkit-edit-mode 。
xwidget-webkit-isearch-mode 是一个次要模式,行为类似于增量搜索(参见增量搜索),但它作用于 WebKit 组件的内容,而非当前缓冲区。在 xwidget-webkit 缓冲区中,该模式绑定到 C-s 和 C-r 。当通过 C-r 启动时,初始搜索会按反向进行。
输入任意可自插入字符都会将该字符加入当前搜索串。按 C-s 会在 WebKit 组件中跳转到下一个搜索结果,按 C-r 则跳转到上一个结果。
按 C-g 可退出增量搜索。
命令 xwidget-webkit-browse-history 会打开一个缓冲区,列出当前 WebKit 缓冲区之前加载过的页面列表,按 RET 即可跳转到对应页面。该命令绑定到按键 H 。
WebKit 缓冲区默认启用 JavaScript,这可能不符合你的需求,因为网站常会用它来跟踪你的浏览行为。你可以通过将变量 xwidget-webkit-disable-javascript 设置为非 nil 值来禁用 JavaScript。修改后必须关闭所有 WebKit 缓冲区,该设置才会生效。
48.3. 跟随 URL 链接
M-x browse-url RET url RET- 将一个 URL 加载到网页浏览器中。
Browse-URL 包让你可以在 Emacs 内部方便地访问 URL。大多数 URL 会通过调用外部网页浏览器打开; ‘mailto:’ 类型的 URL 则会调用 Emacs 的 compose-mail 命令,向指定地址发送邮件(参见发送邮件)。
命令 M-x browse-url 会提示输入一个 URL 并访问它。如果光标位于一个合理的 URL 附近,该 URL 会作为默认值提供。Browse-URL 包还提供了其他你可能希望绑定快捷键的命令,例如 browse-url-at-point 和 browse-url-at-mouse 。
你可以通过 browse-url 定制组中的各种选项来调整 Browse-URL 的行为。其中,选项 browse-url-mailto-function 用于定义如何处理 ‘mailto:’ URL,而 browse-url-browser-function 用于指定默认浏览器。
你可以通过定制 browse-url-handlers 来让特定 URL 使用其他函数打开,它是一个由正则表达式或谓词与对应处理函数配对组成的关联列表。
更多信息可通过输入 C-h P browse-url RET 查看该包的注释说明。
Emacs 还提供一个次要模式,支持把 URL 当作文件来处理。url-handler-mode 是一个全局次要模式,会影响绝大多数处理文件名的 Emacs 命令与原语。开启该模式后,例如你可以使用 C-x C-f https://www.gnu.org/ RET 查看该网页的 HTML 内容,之后还能对其进行编辑并保存到本地文件。
48.4. 激活 URL 链接
M-x goto-address-mode- 在当前缓冲区中激活 URL 和电子邮件地址识别。
M-x global-goto-address-mode- 在所有缓冲区中启用
goto-address-mode。
通过输入 M-x goto-address-mode ,你可以让 Emacs 在当前缓冲区里对 URL 进行特殊标记。启用这个缓冲区本地的次要模式后,它会找出缓冲区中所有的 URL,对其高亮显示,并将它们变成可点击的按钮。当光标位于 URL 文本上时,你可以按 C-c RET (goto-address-at-point) 访问该 URL;也可以使用 鼠标中键( mouse-2 ) 点击,或快速单击鼠标左键( mouse-1 )打开(参见使用鼠标访问引用)。访问 URL 的操作是通过调用 browse-url 子程序完成的(参见访问 URL)。
将 goto-address-mode 添加到模式钩子以及显示新消息的钩子中(例如给 Rmail 使用的 rmail-show-message-hook )会很实用。Gnus 或 MH-E 无需如此,因为它们自身已具备类似功能。
48.5. 查找光标位置的文件与 URL
FFAP 包会替换部分用于查找文件的按键绑定(例如 C-x C-f ),替换为能提供更合理默认值的命令。当带有前缀参数时,这些命令的行为与普通命令一致;否则,它们会从光标周围的文本中获取默认文件名或 URL。如果在缓冲区中识别到的内容是 URL 而非文件名,命令会使用 browse-url 来打开它(参见访问 URL)。
该功能在访问邮件、新闻缓冲区、README 文件、MANIFEST 文件等中的引用时非常实用。更多信息可输入 C-h P ffap RET 查看该包的注释说明。
输入 M-x ffap-bindings 即可启用 FFAP。这会设置以下按键绑定,并为 Rmail、Gnus 和 VM 文章缓冲区安装钩子以提供额外的 FFAP 功能。
C-x C-f filename RET- 打开 filename ,从光标周围文本猜测默认值 (
find-file-at-point)。 C-x C-r filename RETffap-read-only,功能类似于find-file-read-only。C-x C-v filename RETffap-alternate-file,功能类似于find-alternate-file。C-x d directory RET- 在指定 directory目录 打开 Dired,默认使用光标所在目录 (
dired-at-point)。 C-x C-d directory RETffap-list-directory,功能类似于list-directory。C-x 4 f filename RETffap-other-window,功能类似于find-file-other-window。C-x 4 r filename RETffap-read-only-other-window,功能类似于find-file-read-only-other-window。C-x 4 d directory RETffap-dired-other-window,功能类似于dired-other-window。C-x 5 f filename RETffap-other-frame,功能类似于find-file-other-frame。C-x 5 r filename RETffap-read-only-other-frame,功能类似于find-file-read-only-other-frame。C-x 5 d directory RETffap-dired-other-frame,功能类似于dired-other-frame。C-x t C-f filename RETffap-other-tab,功能类似于find-file-other-tab。C-x t C-r filename RETffap-read-only-other-tab,功能类似于find-file-read-only-other-tab。M-x ffap-next- 在缓冲区中搜索下一个文件名或 URL,然后打开该文件或 URL。
S-mouse-3- ffap-at-mouse,从鼠标点击位置周围的文本猜测并打开文件。
C-S-mouse-3- 显示当前缓冲区中提到的文件与 URL 菜单,选择后打开 (
ffap-menu)。
49. 游戏与其他娱乐功能
animate 包可以让文本 “动起来”(例如 M-x animate-birthday-present )。
M-x blackbox 、 M-x mpuz 和 M-x 5x5 是解谜游戏。
blackbox:通过层析成像的方式推断盒子内部物体的位置。mpuz:展示一个字母替代数字的乘法谜题,你需要猜出密码 —— 输入一个字母,再输入你认为它代表的数字。5x5:目标是填满所有方格。
M-x bubbles 是一款消泡泡游戏,目标是以最少步数消除尽可能多的泡泡。
M-x decipher 可以帮助你分析经过简单单字母替换加密的缓冲区内容。
M-x dissociated-press 会将当前缓冲区的文本逐词或逐字符打乱,并输出到名为 *Dissociation* 的缓冲区。正参数表示逐字符处理,并指定重叠字符数;负参数表示逐词处理,并指定重叠单词数。该功能生成的效果与马尔可夫链类似,但它是独立原创的算法;它会在随机跳转之间从原文中连续复制若干字符,而不像马尔可夫链那样每词或每字符都随机跳转。如果你希望文档通顺规范、易于理解,就别把这些错乱内容写进正式文档里。
M-x dunnet 运行一款纯文字冒险游戏。
如果你想多一点互动,可以试试 M-x gomoku ,和 Emacs 对战五子棋。
有点无聊可以试试 M-x hanoi (汉诺塔)。非常无聊可以给它一个数字参数。超级、超级无聊就用参数 9,静静看着就行。
M-x life 运行康威生命游戏(元胞自动机)。
M-x morse-region :将选中区域文本转为摩尔斯电码; M-x unmorse-region :将摩尔斯电码还原为文本; M-x nato-region :将区域文本转为北约音标字母; M-x denato-region :将北约音标字母还原。
M-x pong 、 M-x snake 和 M-x tetris 分别实现了经典的乒乓球、贪吃蛇、俄罗斯方块游戏。
M-x solitaire 是一款跳棋式单人纸牌游戏。
命令 M-x zone 会在 Emacs 空闲时,在屏幕上播放各种视觉特效小游戏。
“真正的程序员” 会用 M-x butterfly ,它用蝴蝶效应翻转磁盘上的一个比特,详见 https://xkcd.com/378。
最后,如果你感到沮丧,可以向著名的心理治疗师 Eliza 倾诉。只需执行 M-x doctor ,每次输入完毕后连按两次回车即可。
50. Emacs Lisp 软件包
Emacs 通过 包(package) 来扩展功能,包本质是 Emacs Lisp 库。这些包可以由你自己编写,也可以由他人提供。如果你希望安装某个包,并在后续的 Emacs 会话中都能使用,需要将其编译,并放到 Emacs 搜索 Lisp 库的目录中。关于这种手动安装方式的更多细节,参见《Emacs 的 Lisp 代码库》。很多包会在 Lisp 文件开头的长篇注释中,提供安装与使用说明;你可以按照这些说明来安装和微调该包的使用。
功能包也可以由 软件包仓库(package archives) 提供,这类仓库是 Emacs Lisp 包的大型集合。每个包都是独立的 Emacs Lisp 程序,有时还会附带 Info 手册等其他组件。Emacs 内置了一套机制,可以让你方便地从这类仓库下载并安装包。本章后续内容会介绍这套机制。
输入 M-x list-packages RET 可以列出从软件包仓库中可安装的所有包。该命令会打开一个名为 *Packages* 的缓冲区,展示所有包的列表,你可以通过这个缓冲区安装或卸载包。参见《软件包菜单缓冲区》。
命令 C-h P (describe-package) 会提示输入包名,并打开一个帮助缓冲区,描述该包的属性与实现的功能。
默认情况下,Emacs 从两个仓库下载包:GNU ELPA 和 NonGNU ELPA。它们由 Emacs 开发者维护、GNU 项目托管。你也可以选择从第三方仓库下载包。参见《软件包安装》。
关于如何把一个 Emacs Lisp 程序制作成可安装的包,参见《Emacs Lisp 参考手册》中的 “打包” 章节。
50.1. 软件包菜单缓冲区
执行命令 M-x list-packages 会打开软件包菜单。这是一个列出 Emacs 已知所有软件包的缓冲区,每个软件包占一行,包含以下信息:
- 软件包名称(如 'auctex')
- 软件包版本号(如 '11.86')
- 软件包状态 —— 通常为 'available' (可从软件包仓库下载)、 'installed' (已安装)或 'built-in' (Emacs 默认内置)。详见「软件包状态」。
- 若启用了多个软件包仓库,会显示该软件包所属仓库。
- 软件包的简短描述。
list-packages 命令会访问网络,从软件包仓库服务器获取可用软件包列表。若网络不可用,则使用最近一次获取的列表。
软件包列表缓冲区的核心命令是 x :
- 若光标所在软件包未安装,执行安装;
- 若已安装,执行删除。
软件包菜单可用命令
h- 显示一条简短信息,总结软件包菜单的使用方法 (
package-menu-quick-help)。 ?RET- 显示当前行软件包的帮助缓冲区 (
package-menu-describe-package),与C-h P命令显示的帮助窗口类似(参见 Emacs Lisp 软件包)。 i- 标记当前行的软件包以待安装 (
package-menu-mark-install)。如果软件包状态为 “available”(可用),会在行首添加一个 'I' 字符;按下x(见下文)将下载并安装该软件包。 d- 标记当前行的软件包以待删除 (
package-menu-mark-delete)。如果软件包状态为 “installed”(已安装),会在行首添加一个 'D' 字符;按下x(见下文)将删除该软件包。关于删除软件包会涉及哪些操作,参见 “软件包文件与目录结构”。 w- 在浏览器中打开当前行软件包的官方网站 (
package-browse-url)。该功能通过browse-url调用浏览器。 ~- 将所有已废弃(obsolete)的软件包标记为待删除 (
package-menu-mark-obsolete-for-deletion)。此命令会将所有状态为 “obsolete” 的软件包标记为待删除。 uDEL- 取消之前通过
i或d命令为当前行添加的安装或删除标记 (package-menu-mark-unmark)。 U- 将所有有可用新版本的软件包标记为待升级 (
package-menu-mark-upgrades)。此命令会为新版本添加安装标记,并为旧的已安装版本(状态为 “obsolete”)添加删除标记。默认情况下,不会对有新版本的内置软件包进行标记,但可以通过自定义package-install-upgrade-built-in改变这一行为。参见 “软件包安装”。如果你将package-install-upgrade-built-in设置为非 nil 值,请务必检查U命令标记的所有内置软件包,避免意外覆盖不想更新的内置包。 x- 下载并安装所有标记为
i的软件包及其依赖项;同时删除所有标记为d的软件包 (package-menu-execute)。执行后会清除所有标记。如果没有任何软件包被标记,此命令将直接安装光标下的软件包(若尚未安装),或删除光标下的软件包(若已安装)。 gr- 刷新软件包列表 (
revert-buffer)。此操作会重新从软件包仓库获取可用软件包列表,并重新显示。 H- 隐藏名称匹配正则表达式的软件包 (
package-menu-hide-package)。命令会提示输入正则表达式,然后隐藏名称匹配的软件包。正则表达式的默认值仅隐藏光标所在的软件包,因此在提示时直接按RET即可隐藏当前软件包。 (- 切换是否显示软件包的旧版本以及来自低优先级仓库的版本 (
package-menu-toggle-hiding)。 / a- 按仓库来源过滤软件包列表 (
package-menu-filter-by-archive)。命令会提示输入仓库名称(如 'gnu'),然后只显示来自该仓库的软件包。可以输入多个仓库名称,用逗号分隔。 / d- 按描述信息过滤软件包列表 (
package-menu-filter-by-description)。命令会提示输入正则表达式,只显示描述信息匹配的软件包。 / k- 按关键词过滤软件包列表 (
package-menu-filter-by-keyword)。命令会提示输入关键词(如 'games'),只显示带有该关键词的软件包。可以输入多个关键词,用逗号分隔。 / N- 按名称或描述过滤软件包列表 (
package-menu-filter-by-name-or-description)。命令会提示输入正则表达式,只显示名称或描述匹配的软件包。 / n- 按名称过滤软件包列表 (
package-menu-filter-by-name)。命令会提示输入正则表达式,只显示名称匹配的软件包。 / s- 按状态过滤软件包列表 (
package-menu-filter-by-status)。命令会提示输入一个或多个状态(如 'available' ,参见 “软件包状态”),只显示状态匹配的软件包。可以输入多个状态值,用逗号分隔。 / v- 按版本过滤软件包列表 (
package-menu-filter-by-version)。命令会先提示输入比较符号 '<' 、 '>' 或 '=',再输入版本字符串,然后只显示版本满足条件的软件包。 / m- 只显示已被标记(待安装或待删除)的软件包 (
package-menu-filter-marked)。 / u- 只显示有可用升级的软件包 (
package-menu-filter-upgradable)。默认情况下,此过滤会排除有新版本的内置软件包,但可以通过自定义package-install-upgrade-built-in改变这一行为。参见 “软件包安装”。 / /- 清除当前应用到软件包列表的所有过滤条件 (
package-menu-filter-clear)。
示例:在目标软件包行按 i 标记,再按 x 执行安装即可。
50.2. 软件包状态
一个软件包可以具有以下状态之一:
- '
available' - 该软件包尚未安装,但可以从软件包仓库下载并安装。
- '
avail-obso' - 该软件包可用于安装,但同时存在更新的版本。具有此状态的软件包默认会被隐藏。
- '
built-in' - 该软件包是 Emacs 默认内置的。无法通过软件包菜单删除,并且默认不参与升级(但你可以通过自定义
package-install-upgrade-built-in改变这一点,参见 “软件包安装”)。 - '
dependency' - 该软件包是为满足其他软件包的依赖而自动安装的。
- '
disabled' - 该软件包已通过
package-load-list变量被禁用。 - '
external' - 该软件包既不是内置包,也不来自
package-user-dir指定的目录(参见 “软件包文件与目录结构”)。外部包的处理方式与内置包类似,无法删除。 - '
held' - 该软件包被保留(held)。参见 “软件包安装”。
- '
incompat' - 该软件包因某些原因无法安装,例如依赖无法安装的软件包。
- '
installed' - 该软件包已安装。
- '
new' - 与 'available' 等价,区别在于:该软件包是你上一次执行
M-x list-packages之后才加入仓库的。 - '
obsolete' - 该软件包是已安装的旧版本;除该版本外,已安装更新版本。
50.3. 软件包安装
最方便的安装方式是使用软件包菜单(参见《软件包菜单缓冲区》),你也可以使用命令 M-x package-install 。该命令会提示输入状态为 'available' 的软件包名称,然后下载并安装。同理,若要升级某个软件包,可使用 M-x package-upgrade ;若要升级所有软件包,可使用 M-x package-upgrade-all 。
默认情况下, package-install 不会考虑那些内置软件包(已随 Emacs 发行版自带)在软件仓库中有新版本的情况。具体来说,在命令提示时,补全候选列表中不会显示内置软件包。但如果你带前缀参数调用 package-install ,它就会同时考虑可升级的内置软件包。你可以通过自定义变量 package-install-upgrade-built-in 将此行为设为默认:若其值为非 nil,即使不带前缀参数, package-install 也会考虑内置软件包。注意:软件包菜单相关命令(参见《软件包菜单缓冲区》)同样受此变量影响。
与之相反, package-upgrade 和 package-upgrade-all 永远不会处理内置软件包。如果你想用这些命令升级某些内置软件包,需要先对每个目标软件包执行一次升级:要么通过 C-u M-x package-install RET ,要么先将 package-install-upgrade-built-in 设为非 nil,再通过软件包菜单或 package-install 升级一次。
如果你将 package-install-upgrade-built-in 设为非 nil,在使用批量更新命令(如 package-upgrade-all 和软件包菜单中的 U )时务必小心:这些命令可能会覆盖你本不想用仓库新版本替换的内置软件包。如果你只想更新少量内置软件包,就不要使用这些批量命令。
一个软件包可能依赖其他某些软件包,因为它需要用到后者提供的功能。当 Emacs 安装这类软件包时,会自动下载并安装所有尚未安装的依赖软件包。(若某个依赖包不可用,Emacs 会报错并中止安装。)软件包的依赖列表会显示在其帮助缓冲区中。
默认情况下,Emacs 从两个仓库下载软件包:GNU ELPA 和 NonGNU ELPA。它们由 Emacs 开发者维护、GNU 项目托管。GNU ELPA 包含我们视作 GNU Emacs 组成部分、但与 Emacs 核心分开发布的 GNU 软件包。NonGNU ELPA 包含版权未转让给自由软件基金会的第三方软件包。24
这由变量 package-archives 控制,其值是 Emacs 已知的软件包仓库列表。每个列表元素格式为 (id . location) ,其中 id 是仓库名称, location 是仓库目录的 URL 或名称。你可以修改此列表为第三方软件包仓库 —— 但风险自负,且只应使用你信任的第三方!
- 用户选项:
package-archives 该变量的值是一个关联列表,记录 Emacs 包管理器识别的软件包仓库。
每个元素对应一个仓库,格式为
(id . location): id 为仓库名称(字符串), location 为仓库基础路径(字符串)若基础路径以 ‘http:’ 或 ‘https:’ 开头,则被当作 HTTP (S) URL,Emacs 会通过 HTTP (S) 从该仓库下载软件包(默认 GNU 仓库就是如此)。
否则,基础路径应为目录名。此时 Emacs 通过普通文件访问从该仓库获取软件包。这类本地仓库主要用于测试。
软件包仓库维护者可以对软件包进行签名,以提高可信度。他们生成一对非对称加密密钥,用私钥为每个软件包生成 signature file签名文件 。你可以用公钥和签名文件验证软件包来源,并确保软件包未被篡改。签名验证通过 EasyPG 接口使用 GnuPG 软件包完成(参见《Emacs EasyPG 助手手册》中的 EasyPG)。有效签名并非软件包绝对安全的绝对保证,你仍应保持谨慎。
软件包仓库应提供获取其公钥的说明。一种方式是从密钥服务器(如 https://pgp.mit.edu/)下载。使用 M-x package-import-keyring 将公钥导入 Emacs。Emacs 将软件包密钥存放在变量 package-gnupghome-dir 指定的目录中,默认为 package-user-dir 下的 'gnupg' 子目录。这会让 Emacs 在验证签名时给 GnuPG 传入 '--homedir' 选项。若 package-gnupghome-dir 为 nil ,则不使用该选项。GNU 软件包仓库的公钥随 Emacs 一同分发,位于 etc/package-keyring.gpg ,Emacs 会自动使用。
若用户选项 package-check-signature 为非 nil,Emacs 会在安装软件包时尝试验证签名。若值为 allow-unsigned 且存在可用的 OpenPGP 配置:已签名的包会被检查,但你仍可安装未签名包。如果你使用某些不签名的仓库,可以将它们加入 package-unsigned-archives 列表。(若值为 allow-unsigned 但无可用 OpenPGP,则按 nil 处理。)
- 若值为
t:必须至少有一个有效签名。 - 若值为
all:所有签名都必须有效。
关于加密密钥与签名的更多信息,参见《GNU 隐私卫士手册》中的 GnuPG。Emacs 自带 GNU Privacy Guard 接口,参见《Emacs EasyPG 助手手册》中的 EasyPG。
如果你启用了多个软件包仓库,且其中某些仓库提供同一软件包的不同版本,选项 package-pinned-packages 会很有用。你可以在该列表中添加「软件包 / 仓库」对,确保指定软件包只从指定仓库下载。
另一个在多仓库下有用的选项是 package-archive-priorities 。它指定每个仓库的优先级(数字越大优先级越高)。默认优先级为 0,除非本选项另行指定。如果高优先级仓库中存在某个软件包,低优先级仓库中的同名包将不会在菜单中显示。(这由 package-menu-hide-low-priority 控制。)
软件包下载、字节编译并安装后,会在当前 Emacs 会话中生效。生效过程会将其目录加入 load-path 并加载其自动加载项。不同软件包的自动加载效果各不相同:多数只是提供新命令,有些则对 Emacs 会话产生更广泛的影响。相关信息请查阅该软件包的帮助缓冲区。
已安装的软件包会在后续所有会话中由 Emacs 自动生效。该过程发生在启动时:处理 early init file早期初始化文件 之后、处理常规 init file 初始化文件 之前(参见《早期初始化文件》)。一个例外:如果 Emacs 以 '-q' 或 '--no-init-file' 选项启动(参见《初始选项》),则不会在启动时自动加载软件包。
若要禁止 Emacs 在启动时自动加载软件包,将变量 package-enable-at-startup 设为 nil 。你必须在 early init file早期初始化文件 中设置,因为该变量在加载常规 init file初始化文件 之前就会被读取。因此,如果你通过 Customize 自定义该变量,应将设置保存到早期初始化文件中。方法是:在保存 package-enable-at-startup 之前,将变量 custom-file (参见《保存自定义设置》)设为指向你的早期初始化文件。
如果你安装了大量软件包,将用户选项 package-quickstart 设为 t 可以提升启动速度。启用后,Emacs 会预先计算很多内容,而不是每次启动都重新计算。但这样做之后,当激活方式需要改变时(例如修改 package-load-list ),你必须手动运行命令 package-quickstart-refresh 。
即使你将 package-enable-at-startup 设为 nil ,仍可在启动中或启动后让软件包生效:
- 要在启动时生效:在初始化文件中调用函数
package-activate-all。 - 要在启动后生效:执行
M-: (package-activate-all) RET。
若要对启动时哪些软件包生效进行更精细的控制,可以使用变量 package-load-list 。它的值应当是一个列表。格式为 (name version) 的列表元素用于告知 Emacs:使名为 name 的软件包的 version 版本生效。
这里的 version 可以是:
- 一个版本字符串(对应软件包的某个具体版本),
- 或
t(表示使用任意已安装的版本), - 或
nil(表示不使用任何版本;这会禁用该软件包,阻止其生效)。
列表元素也可以是符号 all ,表示:对列表中其他元素未指定的所有软件包,使用其最新已安装版本。该变量的默认值为 '(all) 。
例如,若将 package-load-list 设为 '((muse "3.20") all) : Emacs 只会让 'muse' 包的 3.20 版生效,其他所有非 muse 软件包使用任意已安装版本。已安装的其他 'muse' 版本会被忽略。 'muse' 包在软件包菜单中将显示为「held」(保留)状态。
Emacs 字节码相当稳定,但仍可能出现字节码过时、或编译文件依赖的宏在新版 Emacs 中已变更的情况。你可以使用: M-x package-recompile 重新编译某个软件包,或 M-x package-recompile-all 重新编译所有软件包。(若安装包很多,后者可能需要较长时间。)
50.4. 软件包文件与目录结构
每个软件包都以 单个软件包文件 的形式从软件仓库下载:可以是一个 Emacs Lisp 源码文件,或是一个包含多个 Emacs Lisp 源码及其他文件的 tar 包。
软件包文件由负责安装软件包的 Emacs 命令自动获取、处理与清理。通常情况下,你无需直接操作这些文件,除非你正在 制作软件包 (参见《Emacs Lisp 参考手册》中的 “打包”)。如果你需要直接从软件包文件安装软件包,可使用命令 M-x package-install-file 。
软件包安装后,其内容会被放置在 ~/.emacs.d/elpa/ 的子目录中。(你可以通过自定义变量 package-user-dir 修改该目录名称。)软件包子目录的命名格式为: name-version ,其中 name 是软件包名, version 是版本字符串。
除 package-user-dir 外,Emacs 还会在 package-directory-list 列出的目录中查找已安装的软件包。这些目录供系统管理员使用,用于提供 系统级全局可用 的 Emacs 软件包;Emacs 自身永远不会将软件包安装到这些位置。 package-directory-list 中的软件包子目录结构与 package-user-dir 保持一致。
删除软件包(参见《软件包菜单缓冲区》)需要删除对应的软件包子目录。该操作仅对安装在 package-user-dir 中的软件包有效;如果对系统级目录中的软件包执行删除命令,Emacs 会报错。
50.5. 获取软件包源码
默认情况下, package-install 会从软件包仓库下载一个 Tarball 并安装其中的文件。如果你希望修改该软件包的源码并与他人共享你的改动,这种方式可能不够用。这种情况下,你可能更希望直接获取并使用上游源码。这通常会让开发补丁与提交 bug 变得更简单。
实现这一点的一种方式是使用 package-vc-install ,直接从源码仓库获取软件包的源代码。该命令同样会自动确保所有文件被字节编译并设置自动加载,就像普通软件包一样。以这种方式安装的软件包与其他软件包行为完全一致:你可以用 package-upgrade 或 package-upgrade-all 升级,用 package-delete 删除,它们甚至会显示在普通的软件包列表中。如果你只想克隆软件包源码,不把它加入软件包列表,可以使用 package-vc-checkout 。
注意:目前内置软件包无法使用 package-vc-install 进行升级。
在源码检出(checkout)后,你可能希望基于最新开发版复现某个 bug,或实现某个新功能。如果软件包元数据中注明了维护者联系方式,你可以使用命令 package-report-bug 通过邮件提交 bug。该报告将包含你自定义过的所有用户选项。如果你做了改动并希望分享给维护者,可以先提交改动,再使用命令 package-vc-prepare-patch 生成补丁。详见 “准备补丁”。
如果你维护自己的软件包,可能希望使用本地检出,而不是克隆远程仓库。可以使用 package-vc-install-from-checkout 实现:它会从软件包目录(见 “软件包文件与目录结构”)创建一个指向你本地检出目录的符号链接,并初始化代码。注意:你可能需要使用 package-vc-rebuild 重新执行初始化并更新自动加载信息。
50.5.1. 指定软件包源码
要从源码安装软件包,Emacs 必须知道从哪里获取该软件包的源码(如代码仓库),以及代码结构的基本信息(如多文件软件包的主文件)。软件包说明(package specification) 就是用来描述这些信息的。
当软件包仓库支持时(见《Emacs Lisp 参考手册》中的 “软件包仓库”),Emacs 可以自动从该仓库下载软件包说明。如果传给 package-vc-install 的第一个参数是一个表示软件包名的符号,Emacs 就会使用仓库为该软件包提供的说明。
;; Emacs 会从 GNU ELPA 下载 BBDB 的软件包说明: (package-vc-install 'bbdb)
传给 package-vc-install 的第一个参数也可以是一个软件包说明。这允许你从用户选项 package-archives 列出的已知仓库之外的其他位置安装源码包。软件包说明是一个形如 (name . spec) 的列表,其中 spec 是一个属性列表,可使用下表中的任意关键字。
关于代码仓库与版本控制系统的基本术语定义,见《GNU Emacs 手册》中的 “VCS 概念”。
:url- 字符串,指定获取软件包源码的仓库 URL。
:branch- 字符串,指定要安装的代码修订版本。不要与软件包的版本号混淆。
:lisp-dir- 字符串,相对于仓库根目录的 Lisp 源码加载目录,默认为仓库根目录。
:main-file- 字符串,项目的主文件,用于提取软件包元数据。如果未指定,默认使用软件包名加上 ".el" 后缀。
:doc- 字符串,相对于仓库根目录的文档文件,用于构建 Info 文件。可以是 Texinfo 文件或 Org 文件。
:make- 字符串或字符串列表,指定仓库中 Makefile 里需要在构建 Info 文件之前运行的目标。仅当
package-vc-allow-build-commands为非 nil 时生效。 :shell-command- 字符串,指定在构建 Info 文件之前要运行的 shell 命令。仅当
package-vc-allow-build-commands为非 nil 时生效。 :vc-backend- 符号,指定用于下载软件包仓库的 VC 后端(见《GNU Emacs 手册》中的 “版本控制系统”)。如果省略,Emacs 会根据提供的 URL 尝试自动推断;若推断失败,则回退到
package-vc-default-backend的值。
;; 手动指定信息示例: (package-vc-install '(bbdb :url "https://git.savannah.nongnu.org/git/bbdb.git" :lisp-dir "lisp" :doc "doc/bbdb.texi"))
51. 定制
本章介绍几种自定义 Emacs 行为的简单方法。
除本章所述方法外,有关使用 X 资源自定义 Emacs 的信息,参见《X 选项与资源》;有关录制和重放键盘宏的信息,参见《键盘宏》。如需进行更深入、更灵活的修改,则需要编写 Emacs Lisp 代码,参见《Emacs Lisp 参考手册》中的《Emacs Lisp》。
51.1. 简易自定义界面
Emacs 拥有大量可供修改的 settings(设置) 。绝大多数设置都是 customizable variables(可自定义变量) (参见 变量),也被称为 user options用户选项 。可自定义变量数量庞大,控制着 Emacs 行为的方方面面;本手册中记载的变量已收录在变量索引里。另一类独立的设置是 faces(外观) ,它们决定文本的字体、颜色及其他属性(参见 文本外观)。
若要浏览和修改设置(包括变量与外观),请输入 M-x customize 。该命令会创建一个 /customization buffer(自定义缓冲区)/,你可以在其中浏览按逻辑组织的设置列表,编辑并设置它们的值,还能永久保存修改。
51.1.1. 自定义组
自定义设置被组织到 customization groups自定义组 中。这些组又被归到更大的组里,一直向上汇总到一个名为 Emacs 的主组。
执行 M-x customize 会创建一个自定义缓冲区,显示顶层的 Emacs 组。它的部分内容大致如下:
For help using this buffer, see [Easy Customization] in the [Emacs manual]. ________________________________________ [ Search ] Operate on all settings in this buffer: [ Revert... ] [ Apply ] [ Apply and Save ] Emacs group: Customization of the One True Editor. [State]: visible group members are all at standard values. See also [Manual]. [Editing] Basic text editing facilities. [Convenience] Convenience features for faster editing. ...more second-level groups... ----- 如需了解此缓冲区的使用方法,请参阅《Emacs 手册》中的 [简易自定义] 章节。 ________________________________________ [搜索] 对本缓冲区中的所有设置执行操作: [恢复…] [ 应用 ] [ 应用并保存 ] Emacs 组:对唯一真正编辑器的自定义。 [状态]:可见的组成员均为标准值。 另见 [手册]。 [Editing] 基本文本编辑功能。[便捷] 用于更快编辑的便捷特性。 …… 更多二级组……
该缓冲区的主体部分显示 'Emacs' 自定义组,其中包含多个其他组(如 Editing「编辑」、Convenience「便捷」等)。这些组的内容不会在这里全部列出,只会显示一行说明文档。
组的 state状态 会表明该组内的设置是否已被编辑、设定或保存。参见 修改变量。
自定义缓冲区的大部分内容是 只读 的,但包含一些你可以编辑的 editable fields可编辑区域 。例如,自定义缓冲区顶部有一个用于搜索设置的可编辑区域(参见 浏览和搜索设置)。缓冲区中还有 button按钮 和 links链接 ,你可以通过鼠标点击,或将光标移到该处并按 RET 来激活。例如,像 '[Editing]' 这样的组名就是链接;激活其中一个链接会打开对应组的自定义缓冲区。
在自定义缓冲区中,你可以按 TAB (widget-forward) 向前跳到下一个按钮或可编辑区域。按 S-TAB (widget-backward) 向后跳回上一个按钮或可编辑区域。
51.1.2. 浏览与搜索设置
在由 M-x customize 创建的顶层自定义缓冲区中,你可以通过链接进入 'Emacs' 自定义组的子组。这些子组中可能包含可供你自定义的设置;它们也可能包含更深层的子组,用于处理 Emacs 中更专门的子系统。在浏览自定义组的层级结构时,你可以找到想要自定义的设置。
如果你想要自定义某个特定的设置或自定义组,可以直接使用以下命令直达目标: M-x customize-option 、 M-x customize-face 或 M-x customize-group 。参见「自定义特定项」。
如果你不确定要自定义哪些组或设置,可以在每个自定义缓冲区顶部的 可编辑搜索框 中进行搜索。你可以在其中输入搜索词 —— 可以是一个或多个用空格分隔的单词,也可以是正则表达式(参见「正则表达式语法」)。然后在搜索框中按 RET ,或点击旁边的「Search」(搜索)按钮,即可切换到一个新的自定义缓冲区,其中显示与这些关键词匹配的组和设置。但请注意:该功能只会搜索 当前 Emacs 会话中已加载 的组和设置。
如果你不希望自定义缓冲区显示搜索框,可将变量 custom-search-field 设为 nil 。
命令 M-x customize-apropos 与使用搜索框类似,区别在于它通过迷你缓冲区(minibuffer)读取搜索词。参见「自定义特定项」。
M-x customize-browse 是另一种浏览可用设置的方式。该命令会创建一个专用的自定义缓冲区,只以结构化布局显示组和设置的名称。你可以点击组名旁边的 '[+]' 按钮,在当前缓冲区中展开该组的内容;当内容展开后,按钮会变为 '[-]' ,再次点击即可隐藏内容。此缓冲区中的每个组或设置都带有 '[Group]' (组)、 '[Option]' (选项)或 '[Face]' (外观)链接。点击这些链接会创建一个普通的自定义缓冲区,专门显示该组、选项或外观 —— 这就是修改通过 M-x customize-browse 找到的设置的方法。
51.1.3. 修改变量
下面是变量(也叫用户选项)在自定义缓冲区中的显示示例:
[Hide] Kill Ring Max: Integer (positive or zero): 120
[State]: STANDARD.
Maximum length of kill ring before oldest elements are thrown away.
-----
[隐藏] Kill Ring Max(剪切环最大长度):整数(正数或零):120
[状态]:标准。
剪切环的最大长度,超出后最旧的元素会被丢弃。
第一行显示变量名为 kill-ring-max ,为便于阅读格式化为 'Kill Ring Max' ,同时标明其预期类型:正整数或零。默认值为 '120' 。标有 '[Hide]' (隐藏)的按钮激活后会隐藏变量的值与状态;这在避免因值过长而挤满自定义缓冲区时很有用(正因如此,值很长的变量默认可能是隐藏状态)。点击 '[Hide]' 后,按钮会变为 '[Show Value]' (显示值),再次激活可重新显示值与状态。在图形界面下, '[Hide]' 和 '[Show Value]' 会分别替换为向下和向右的小三角图标。
变量名下方一行是该变量的 customization state 自定义状态 :本例中 'STANDARD' (标准)表示你尚未修改过该变量,使用的是默认值。 '[State]' (状态)按钮会弹出一个菜单,提供用于自定义该变量的操作。
状态行下方是变量的 说明文档 ,与 C-h v 命令显示的内容一致(参见 “查看与设置变量”)。如果文档超过一行,可能只显示一行,该行末尾会有 '[More]' (更多)按钮;激活可查看完整文档。
要为 'Kill Ring Max' 输入新值,只需将光标移到值上直接编辑即可。例如,按 M-d 删除 120,再输入其他数字。当你开始修改文本时, '[State]' 行会变为:
[State]: EDITED, shown value does not take effect until you
set or save it.
-----
[状态]:已编辑,所显示的值需设置或保存后才会生效。
编辑值并 不会立即生效 。要使其生效,必须激活 '[State]' 按钮并选择 'Set for Current Session' 「为当前会话设置」。之后变量状态会变为:
[State]: SET for current session only. ----- [状态]:仅对当前会话已设置。
你不必担心输入无效值; 'Set for Current Session' 「为当前会话设置」 会自动检查合法性,不会接受不合法的值。
在编辑某些类型的值(如文件名、目录名、Emacs 命令名)时,可以用 C-M-i (widget-complete) 或等价的 M-TAB / ESC TAB 进行补全,用法与迷你缓冲区补全类似(参见 “补全”)。
在可编辑值区域按 RET ,会像 TAB 一样将光标跳到下一个区域或按钮。编辑完一个区域后按 RET 即可继续下一项。要在可编辑区域内插入换行,使用 C-o 或 C-q C-j 。
有些变量只允许 固定的合法值集合 ,不允许直接编辑。此时值前面会出现 '[Value Menu]' (值菜单)按钮,激活后可从列表中选择值。对于布尔 "on or off" 类型的值,按钮显示为 '[Toggle]' (切换),点击可翻转值。使用 '[Value Menu]' 或 '[Toggle]' 后,仍需再次设置变量才能让选中的值生效。
有些变量的值具有 复杂结构 。例如 minibuffer-frame-alist 的值是一个关联列表,它在自定义缓冲区中大致如下显示:
[Hide] Minibuffer Frame Alist:
[INS] [DEL] Parameter: width
Value: 80
[INS] [DEL] Parameter: height
Value: 2
[INS]
[ State ]: STANDARD.
Alist of parameters for the initial minibuffer frame. [Hide]
[…more lines of documentation…]
-----
[隐藏] 小缓冲区框架关联列表:
[插入] [删除] 参数:宽度
值:80
[插入] [删除] 参数:高度
值:2
[插入]
[状态]:标准。
初始小缓冲区框架的参数关联列表。[隐藏]
[…… 更多文档行……]
这种情况下,列表中的每一项关联都由 'Parameter' 「参数」和 'Value' 「值」两部分组成,两者都是可编辑区域。你可以用旁边的 '[DEL]' (删除)按钮删除某条关联;用对应位置的 '[INS]' (插入)按钮添加新关联,最后一个 '[INS]' 会在列表末尾插入。
设置一个变量后,新值 只在当前 Emacs 会话生效 。要将值保存以便未来会话使用,需通过 '[State]' 按钮选择 'Save for Future Sessions' 「为未来会话保存」。参见 “保存自定义设置”。
你也可以通过 '[State]' 按钮选择 'Erase Customization' 「清除自定义」,将变量恢复为标准值。实际上共有四种重置操作:
- '
Undo Edits' - 「撤销编辑」如果你修改了变量但尚未设置,此操作会将自定义缓冲区中的文本恢复为实际当前值。
- '
Revert This Session's Customizations' - 「恢复此会话的自定义」将变量值恢复到上一次保存的值(如果有),否则恢复为标准值,并同步更新缓冲区文本。
- '
Erase Customization' - 「清除自定义」将变量设为标准值,并 删除你之前保存过的值 。
- '
Set to Backup Value' - 「设为备份值」将变量恢复为本会话中在自定义缓冲区里 曾经设置过的旧值 。如果你自定义过一个变量又重置丢弃了它,可以用此操作找回。
有时需要为某条自定义 记录注释 。可从 '[State]' 菜单中选择 'Add Comment' 「添加注释」,创建一个用于输入注释的区域。
自定义缓冲区顶部附近有两行按钮:
Operate on all settings in this buffer: [Revert...] [Apply] [Apply and Save] ----- 对本缓冲区中所有设置执行操作: [恢复…] [应用] [应用并保存]
'[Revert...]' (恢复…)按钮会弹出菜单,包含上面提到的前三种重置操作。 '[Apply]' (应用)按钮为 当前会话应用 所有设置。 '[Apply and Save]' (应用并保存)按钮会应用设置并 保存到未来会话 ;如果 Emacs 以 '-q' 或 '-Q' 参数启动(参见 “初始选项”),则不会显示此按钮。
命令 C-c C-c (Custom-set) 等价于点击 '[Set for Current Session]' 「为当前会话设置」。命令 C-x C-s (Custom-save) 等价于点击 '[Save for Future Sessions]' 「为未来会话保存」。
'[Exit]' (退出)按钮会退出自定义缓冲区,并将缓冲区放到缓冲区列表底部。若想让它直接 杀死 自定义缓冲区,可将变量 custom-buffer-done-kill 设为 t 。
51.1.4. 保存自定义设置
在自定义缓冲区中,你可以通过变量的 '[State]' (状态)按钮选择 'Save for Future Sessions' 「为未来会话保存」 来保存该项自定义设置。命令 C-x C-s (Custom-save),或是自定义缓冲区顶部的 '[Apply and Save]' 「应用并保存」 按钮,会保存缓冲区中所有可生效的设置。
保存的原理是将配置代码写入一个文件,通常是你的 initialization file初始化文件 (参见《Emacs 初始化文件》)。之后的 Emacs 会话会在启动时自动读取该文件,重新应用这些自定义设置。
你可以选择将自定义设置保存在初始化文件以外的其他位置。要实现这一点,必须在初始化文件中添加几行代码:将变量 custom-file 设置为你想要的文件路径,并加载该文件。例如:
(setq custom-file "~/.config/emacs-custom.el") (load custom-file)
你甚至可以为不同版本的 Emacs 指定不同的自定义文件,示例如下:
(cond ((< emacs-major-version 28) ;; Emacs 27 版本的自定义文件 (setq custom-file "~/.config/custom-27.el")) ((and (= emacs-major-version 26) (< emacs-minor-version 3)) ;; Emacs 26 版本(26.3 之前)的自定义文件 (setq custom-file "~/.config/custom-26.el")) (t ;; Emacs 28.1 及更高版本 (setq custom-file "~/.config/emacs-custom.el"))) (load custom-file)
如果 Emacs 是以 '-q' 或 '--no-init-file' 参数启动的(参见《初始选项》),它将 不允许 你把自定义设置保存到初始化文件中。这是因为在这种会话下保存自定义设置,会覆盖掉你原本初始化文件里的其他所有配置。
请注意: 任何没有选择为未来会话保存的自定义设置,在退出 Emacs 后都会丢失 。如果你希望在退出时收到 “有未保存的自定义设置” 的提醒,可以在初始化文件中添加以下代码:
(add-hook 'kill-emacs-query-functions
'custom-prompt-customize-unsaved-options)
51.1.5. 自定义外观
你可以自定义外观(face)(见文本外观),它决定 Emacs 如何显示不同类型的文本。自定义组中可以同时包含变量和外观。
例如,在编程语言模式下,源代码注释使用 font-lock-comment-face 显示(见字体锁定模式)。在自定义缓冲区中,点击 '[Show All Attributes]' 「[显示全部属性]」链接后,该外观大致如下所示:
[Hide] Font Lock Comment Face:[sample]
[State] : STANDARD.
Font Lock mode face used to highlight comments.
[ ] Font Family: --
[ ] Font Foundry: --
[ ] Width: --
[ ] Height: --
[ ] Weight: --
[ ] Slant: --
[ ] Underline: --
[ ] Overline: --
[ ] Strike-through: --
[ ] Box around text: --
[ ] Inverse-video: --
[X] Foreground: Firebrick [Choose] (sample)
[ ] Background: --
[ ] Stipple: --
[ ] Inherit: --
[Hide Unused Attributes]
----
[隐藏] 字体锁定注释外观:[示例]
[状态]:标准。
字体锁定模式中用于高亮注释的外观。
[ ] 字体族:--
[ ] 字体铸造厂:--
[ ] 宽度:--
[ ] 高度:--
[ ] 粗细:--
[ ] 倾斜:--
[ ] 下划线:--
[ ] 上划线:--
[ ] 删除线:--
[ ] 文本外框:--
[ ] 反色:--
[X] 前景色:砖红色 [选择] (示例)
[ ] 背景色:--
[ ] 点画图案:--
[ ] 继承:--
[隐藏未使用属性]
前三行显示外观名称、'[State]' 「[状态]」按钮和外观说明。下方是 face attributes外观属性 列表。每个属性前面都有一个复选框:
- 勾选框 '
[X]' 表示该外观为此属性指定了值; - 空复选框 '
[ ]' 表示该外观未为此属性指定特殊值。你可以激活复选框来设定或取消设定对应属性。
一个外观不必指定每一个属性;事实上,大多数外观只指定少数几个属性。在上例中, font-lock-comment-face 只指定了前景色。任何未指定的属性都会从名为 default 的特殊外观中继承,而 default 外观的所有属性都是完整指定的。 default 外观用于显示没有显式指定外观的文本;此外,它的背景色属性同时作为框架的背景色。
属性列表末尾的 '[Hide Unused Attributes]' 「[隐藏未使用属性]」按钮会隐藏该外观未设定的属性。当属性被隐藏时,按钮会变为 '[Show All Attributes]' 「[显示全部属性]」,点击可重新显示完整属性列表。自定义缓冲区在初始时可能会隐藏未设定的属性,避免界面杂乱。
当某个属性被设定后,你可以用常规方式修改它的值。
Foreground 前景色 和 background colors 背景色 既可以用颜色名指定,也可以用 RGB 三元组指定(见外观颜色)。你也可以使用 '[Choose]' 「[选择]」按钮切换到颜色名列表;在该缓冲区中用回车选中一个颜色,颜色名就会填入值区域。
外观的设置、保存和重置操作与变量的对应操作完全相同(见修改变量)。
一个外观可以为不同类型的显示器指定不同的显示效果。例如,同一个外观可以在彩色显示器上让文字显示为红色,在单色显示器上使用粗体。要为一个外观指定多种显示效果,可以在 '[State]' 「[状态]」按钮的菜单中选择 'For All Kinds of Displays' 「适用于所有类型显示器」。
51.1.6. 自定义特定项
M-x customize-option RET option RETM-x customize-variable RET option RET- 只为单个 user option用户选项 创建自定义缓冲区。
M-x customize-face RET face RET- 只为单个 face外观 创建自定义缓冲区。
M-x customize-icon RET face RET- 只为单个 icon图标 创建自定义缓冲区。
M-x customize-group RET group RET- 只为单个自定义 group组 创建自定义缓冲区。
M-x customize-apropos RET regexp RET- 为所有匹配该 regexp正则表达式 的设置和组创建自定义缓冲区。
M-x customize-changed RET version RET- 创建自定义缓冲区,显示自指定 Emacs 版本以来 含义发生变化或新增 的所有用户选项、外观和组。
M-x customize-saved- 创建包含 所有已保存设置 的自定义缓冲区。
M-x customize-unsaved- 创建包含 所有已设置但未保存 的设置的自定义缓冲区。
如果你想自定义某个特定用户选项,输入 M-x customize-option 。该命令会读取变量名,并只为这一个用户选项创建自定义缓冲区。在迷你缓冲区中输入变量名时支持补全,但只对已加载到 Emacs 中的变量名生效。
同理,你可以用 M-x customize-face 自定义特定外观,用 M-x customize-group 为特定自定义组打开自定义缓冲区。
M-x customize-apropos 会提示输入搜索词(可以是空格分隔的单词,或正则表达式),并为所有 已加载 且名称匹配的设置和组创建自定义缓冲区。这与自定义缓冲区顶部的搜索框功能类似(见自定义组)。
当你升级到新的 Emacs 版本时,可能想查看新增的设置,或含义、默认值发生变化的设置。使用 M-x customize-changed 并在迷你缓冲区中输入旧版本号即可。它会创建自定义缓冲区,显示自该版本以来定义发生变化的所有设置和组,并在需要时自动加载它们。
如果你修改了设置后想重新查看或撤销,可以使用以下两个命令:
- 使用
M-x customize-saved查看并编辑已保存的设置。 - 使用
M-x customize-unsaved查看并编辑已设置但未保存的设置。
51.1.7. 自定义主题
Custom themes自定义主题 是可以 整体启用或禁用 的设置集合。你可以用自定义主题在多套配置间快速切换,也能把整套配置从一台电脑迁移到另一台。
一个自定义主题以一份 Emacs Lisp 源文件的形式存储。如果主题名为 name ,对应的主题文件就是 name-theme.el 。关于主题文件的格式与创建方法,参见「创建自定义主题」。
输入 M-x customize-themes 可进入名为 *Custom Themes* 的缓冲区,其中列出 Emacs 能识别的所有自定义主题。默认情况下,Emacs 会在两个位置查找主题文件:
- 变量
custom-theme-directory指定的目录(默认为~/.emacs.d/) - Emacs 安装目录下的
etc/themes目录(参见变量data-directory)
后者包含随 Emacs 分发的若干自带主题,主要用于修改外观以适配不同配色方案。(注意:自定义主题 不局限于 修改外观,也可以用来修改变量。)
如果你希望 Emacs 在其他目录查找自定义主题,把目录加入列表变量 custom-theme-load-path 即可。它的默认值是 (custom-theme-directory t) :符号 custom-theme-directory 代表变量 custom-theme-directory 的实际路径,符号 t 代表内置主题目录 etc/themes 。 *Custom Themes* 缓冲区里列出的,就是在 custom-theme-load-path 路径下找到的所有主题。
在 *Custom Themes* 缓冲区中,你可以勾选 / 取消勾选一个自定义主题旁边的复选框,为 当前 Emacs 会话 启用或禁用该主题。启用一个自定义主题后,它的所有设置(变量和外观)都会在会话中生效。要把选中的主题应用到 未来所有 Emacs 会话 ,输入 C-x C-s (custom-theme-save) 或点击 '[Save Theme Settings]' 「[保存主题设置]」按钮。
首次启用 一个自定义主题时,Emacs 会显示主题文件内容,并询问你是否确定加载。因为加载主题可能执行任意 Lisp 代码, 只应加载你确认安全的主题 。如果安全,Emacs 会提供 “以后记住该主题可信” 的选项(通过把文件的 SHA-256 哈希保存到变量 custom-safe-themes 实现)。如果你想把 所有主题 都视为安全,可将该变量设为 t 。Emacs 自带主题(位于 etc/themes 下)不受此检查限制,永远被视为安全。
设置或保存自定义主题,本质上是在修改变量 custom-enabled-themes 。该变量的值是一个 已启用主题名称列表 (以 Lisp 符号形式,如 tango )。你也可以不使用 *Custom Themes* 缓冲区,而是用普通自定义界面(如 M-x customize-option )直接修改这个变量。注意:自定义主题 不允许 自行设置 custom-enabled-themes 。
你通过自定义缓冲区做的任何修改, 优先级高于 主题设置。这让你可以轻松覆盖主题中不满意的个别配置。如果两个不同主题的设置冲突,在 custom-enabled-themes 列表中靠前的主题优先。在自定义缓冲区中,如果某项设置被主题从默认值修改过,它的 'State' 「状态」会显示为 'THEMED' (已应用主题),而不是 'STANDARD' (标准)。
你可以在当前 Emacs 会话中直接启用某个特定主题:
M-x load-theme:提示输入主题名,从文件加载并启用。M-x enable-theme:如果某主题文件已加载过,可直接启用而不重新加载文件。M-x disable-theme:禁用一个自定义主题。
要查看某个自定义主题的说明:
- 在
*Custom Themes*缓冲区中,将光标移到对应行按 =?=; - 或在任意位置输入
M-x describe-theme并输入主题名
部分主题提供变体(最常见是浅色、深色两种)。你可以用 M-x theme-choose-variant 切换到另一变体:
- 如果当前激活主题只有一个变体,会直接切换
- 如果有多个变体,命令会提示你选择哪一个
注意: theme-choose-variant 只在 同时只激活一个主题 时有效。
51.1.8. 创建自定义主题
你可以使用与自定义缓冲区类似的界面来定义自定义主题,输入 M-x customize-create-theme 。该命令会切换到名为 *Custom Theme* 的缓冲区,并会询问是否要在主题中插入一些常用的 Emacs 外观(face)(这是一个便捷选项,因为自定义主题常用来配置外观)。如果你选择否,主题初始将不包含任何设置。
在 *Custom Theme* 缓冲区顶部附近,有可编辑区域,你可以在这里输入 主题名称 和 主题描述 。名称可以是除 'user' 以外的任意名称。描述会在你对该主题执行 M-x describe-theme 时显示,其第一行应为简短的单句摘要;在 M-x customize-themes 打开的缓冲区中,这句话会显示在主题名称旁边。
要向主题添加新设置,可以使用 '[Insert Additional Face]' 「插入额外外观」 或 '[Insert Additional Variable]' 「插入额外变量」 按钮。每个按钮都会通过小缓冲区读取外观或变量名(支持补全),并插入对应外观或变量的自定义条目。你可以像在普通自定义缓冲区中一样修改变量值或外观属性。要从主题中移除某个外观或变量,取消其名称旁边的复选框即可。
指定好主题的外观和变量后,输入 C-x C-s (custom-theme-write) 或使用缓冲区中的 '[Save Theme]' 「保存主题」 按钮。这会将主题文件以 name-theme.el 的格式保存在 custom-theme-directory 指定的目录中。
在 *Custom Theme* 缓冲区中,你可以通过激活 '[Visit Theme]' 「访问主题」 按钮并指定主题名,来查看和编辑已有的自定义主题。你也可以使用 '[Merge Theme]' 「合并主题」 按钮,将另一个主题的设置导入当前缓冲区。通过 '[Merge Theme]' 「合并主题」 按钮并指定特殊主题 'user' ,可以将你非主题形式的自定义设置导入到一个自定义主题中。
主题文件本质上就是一个 Emacs Lisp 源文件,加载自定义主题就是加载该 Lisp 文件。因此,你也可以直接编辑主题文件,而不使用 *Custom Theme* 缓冲区。详情参见《Emacs Lisp 参考手册》中的 “自定义主题” 章节。
51.2. 变量
variable 变量 是一个拥有值的 Lisp 符号(symbol),该符号的名称也称为 variable name变量名 。变量名可以包含文件名中允许的任意字符,但绝大多数变量名由普通单词加连字符分隔构成。
变量名本身简要说明了它的作用。大多数变量还附带 documentation string文档字符串 ,用于说明:
- 变量的用途
- 应该接受什么类型的值
- 该值会如何被使用。你可以使用帮助命令
C-h v(describe-variable) 查看这份文档。参见「查看与设置变量」。
Emacs 内部使用大量 Lisp 变量来记录状态,但对普通用户最有意义的,是那些 允许用户修改 的变量 —— 它们被称为 customizable variables 可自定义变量 或 user options用户选项 (见简易自定义界面)。在接下来的小节中,我们会介绍 Emacs 变量的其他用法,例如如何在自定义界面之外设置变量。
Emacs Lisp 中,除少数例外, 任何变量都可以接受任意类型的值 。但很多变量只有在被赋予 特定类型 的值时才有意义。例如, kill-ring-max (用于指定剪切环最大长度)只接受数字;如果你给它赋一个字符串, C-y (yank) 等命令就会报错。
另一方面,有些变量 不关心类型 :比如某个变量在值为 nil 时有一种效果,非 nil 时有另一种效果。那么任何不是符号 nil 的值都会触发第二种效果,无论它本身是什么类型。按照惯例,我们通常用符号 t (表示 “true”)来表示非 nil 值。
如果你通过 自定义缓冲区 设置变量,就不用担心类型不合法:自定义界面通常只允许你输入有意义的值。如有疑问,可用 C-h v 查看变量的文档字符串,确认它期望的类型。
51.2.1. 查看与设置变量
C-h v var RET- 显示变量 var 的值与文档 (
describe-variable)。 M-x set-variable RET var RET value RET- 将变量 var 的值修改为 value新值 。
要查看一个变量的值,使用 C-h v (describe-variable)。它会通过迷你缓冲区读取变量名(支持补全),并显示该变量的值和文档。例如:
C-h v fill-column RET
会显示类似如下内容:
fill-column is a variable defined in ‘C source code’. Its value is 70 Automatically becomes buffer-local when set. This variable is safe as a file local variable if its value satisfies the predicate ‘integerp’. Probably introduced at or before Emacs version 18. Documentation: Column beyond which automatic line-wrapping should happen. Interactively, you can set the buffer local value using C-x f. You can customize this variable. ----- fill-column 是在‘C 源代码’中定义的变量。 它的值是 70 设置时自动变为缓冲区局部变量。 如果该变量的值满足谓词‘integerp’,则它可以安全地作为文件局部变量。 大概在 Emacs 18 版或更早引入。 文档: 超过该列时自动进行换行。 交互使用时,你可以用 C-x f 设置缓冲区局部值。 你可以自定义此变量。
显示 “You can customize the variable 这一行,说明该变量是 用户选项 。 C-h v 并不限于用户选项,也可以查看非自定义变量。
设置某个可自定义变量最方便的方式是使用 M-x set-variable 。它先通过迷你缓冲区读取变量名(支持补全),再通过迷你缓冲区第二次读取表示新值的 Lisp 表达式(你可以用 M-n 将旧值插入小缓冲区进行编辑)。例如:
M-x set-variable RET fill-column RET 75 RET
将 fill-column 设置为 75。
M-x set-variable 只适用于可自定义变量,但你可以用 Lisp 表达式设置任意变量,例如:
(setq fill-column 75)
要执行这样的表达式,输入 M-: (eval-expression),并在迷你缓冲区中输入表达式(见 “执行 Emacs Lisp 表达式”)。或者进入 *scratch* 缓冲区,输入表达式后按 C-j (见 “Lisp 交互缓冲区”)。
与所有自定义 Emacs 的方式一样(除非另有说明),设置变量 只影响当前 Emacs 会话 。要让变量在未来会话中生效,唯一的方法是把配置写到 初始化文件 中(见 “Emacs 初始化文件”)。
如果你要在初始化文件中设置可自定义变量,且不想使用 Customize 界面,可以使用 setopt 宏。例如:
(setopt fill-column 75)
它的效果和 setq 一样,但如果该变量有专门的设置函数,使用 setopt 时会 自动运行 这些函数。你也可以对非自定义变量使用 setopt ,但效率会比 setq 低。
51.2.2. 钩子
Hooks钩子 是自定义 Emacs 的重要机制。钩子是一个 Lisp 变量,里面保存着一组函数,会在某个预先定义好的时机被调用(这一过程称为 running the hook运行钩子 )。列表里的每个函数,都称为该钩子的 hook functions钩子函数 。例如,钩子 kill-emacs-hook 会在 即将退出 Emacs 时运行(参见 “退出 Emacs”)。
大多数钩子都是 normal hooks普通钩子 。这意味着 Emacs 运行钩子时,会 依次调用每个钩子函数,且不传递任何参数 。我们尽量让大部分钩子都是普通钩子,以便你能用统一的方式使用它们。所有以 '-hook' 结尾的变量都是普通钩子。
少数钩子是 非普通钩子 。它们的名字以 '-functions' 结尾,而不是 '-hook' (一些旧代码可能还在使用已废弃的后缀 '-hooks' )。这类钩子之所以 “非普通”,是因为函数的调用方式不同:可能会传入参数,或者返回值会被特殊处理。例如, find-file-not-found-functions 就是非普通钩子,因为只要有一个钩子函数返回非 nil 值,剩下的函数就 不再执行 (参见 “访问文件”)。每个非普通钩子变量的文档都会说明其函数的具体调用方式。
你可以像设置其他 Lisp 变量一样,用 setq 设置钩子变量,但 推荐 使用 add-hook 向钩子(无论普通或非普通)中添加函数,如下例所示。详情参见《Emacs Lisp 参考手册》中的 “Hooks” 章节。
大多数 主模式 都会在初始化的最后一步运行一个或多个 mode hooks模式钩子 。模式钩子是自定义单个模式行为的便捷方式,它们 永远是普通钩子 。例如,下面的代码可以在 Text 模式及其派生模式中自动开启自动换行模式:
(add-hook 'text-mode-hook 'auto-fill-mode)
它的原理是调用 auto-fill-mode ,不提供参数时会启用该次要模式(参见 “次要模式”)。接下来,假设你不希望在 LaTeX 模式(Text 模式的派生模式)中开启自动换行,可以再加一行:
(add-hook 'latex-mode-hook (lambda () (auto-fill-mode -1)))
这里我们用特殊宏 lambda 构造了一个 匿名函数 (参见《Emacs Lisp 参考手册》中的 “Lambda 表达式”),它调用 auto-fill-mode 并传入参数 -1 来关闭该次要模式。由于 LaTeX 模式会在运行 text-mode-hook 之后运行 latex-mode-hook ,最终效果就是关闭自动换行。
下面是一个更复杂的例子,展示如何用钩子自定义 C 语言代码的缩进风格:
(setq my-c-style '((c-comment-only-line-offset . 4) (c-cleanup-list . (scope-operator empty-defun-braces defun-close-semi)))) (add-hook 'c-mode-common-hook (lambda () (c-add-style "my-style" my-c-style t)))
主模式钩子也会作用于从它 derived派生 出来的其他主模式(参见《Emacs Lisp 参考手册》中的 “派生模式”)。例如,HTML 模式派生自 Text 模式(参见 “SGML 和 HTML 模式”);启用 HTML 模式时,会先运行 text-mode-hook ,再运行 html-mode-hook 。这让你可以用一个钩子同时影响多个相关模式。特别地,如果你想让某个钩子函数作用于 所有编程语言模式 ,可以把它加到 prog-mode-hook ;Prog 模式本身几乎不做任何事,就是为了让其他模式继承而设计的。
设计钩子函数时,最好让它们 不依赖执行顺序 。任何对顺序的依赖都容易出问题。不过顺序本身是可预测的:钩子函数会按照它们在钩子列表中的 先后顺序执行 。
如果你反复调用 add-hook 往同一个钩子添加不同版本的函数,要记住:所有添加过的版本都会一起留在钩子变量里。你可以用 remove-hook 删除单个函数,或者用 (setq hook-variable nil) 清空全部内容。
如果钩子变量是 buffer-local缓冲区局部 的,就会使用局部值而不是全局值。但是,如果局部变量里包含元素 t ,那么 全局钩子也会一并运行 。
51.2.3. 局部变量
M-x make-local-variable RET var RET- 让变量 var 在当前缓冲区中拥有局部值。
M-x kill-local-variable RET var RET- 让变量 var 在当前缓冲区中恢复使用全局值。
M-x make-variable-buffer-local RET var RET- 标记变量 var ,使其在被设置时自动变为当前缓冲区局部。
几乎所有变量都可以设为特定缓冲区本地。这意味着它在该缓冲区中的值,与其他缓冲区中的值相互独立。有少数变量在每个缓冲区中默认就是局部的。其余所有 Emacs 变量都有一个 global value全局值 ,在所有未设为局部的缓冲区中生效。
M-x make-local-variable 读取一个变量名,并将其设为当前缓冲区局部。之后在本缓冲区修改它的值,不会影响其他缓冲区;全局值的改动也不会影响本缓冲区。
M-x make-variable-buffer-local 会标记一个变量,使其一旦被设置,就自动变成局部变量。更准确地说:一个变量被这样标记后,所有常规的赋值方式都会先隐式执行 make-local-variable 。我们称这类变量为 按缓冲区变量 (per-buffer)。Emacs 中很多变量默认就是这类;变量的文档字符串会说明这一点。按缓冲区变量的全局值通常不在任何缓冲区直接生效,但它仍有意义:它是 每个新缓冲区创建时该变量的初始值 。
主模式 (见主模式)在设置变量前,总会先将变量设为缓冲区局部。这就是为什么在一个缓冲区切换主模式,不会影响其他缓冲区。 次要模式 也是通过设置变量工作:通常每个次要模式都有一个控制变量,模式启用时为非 nil(参见 “次要模式”)。很多次要模式的控制变量本身就是按缓冲区的,因此永远是缓冲区局部;否则你也可以像其他变量一样,手动将其设为特定缓冲区局部。
有少数变量 不能是缓冲区局部 ,因为它们默认是 每个显示设备局部 (参见 “多显示器”)。如果你试图将这类变量设为缓冲区局部,会收到错误信息。
M-x kill-local-variable 取消指定变量在当前缓冲区的局部状态,此后该变量的 全局值 在此缓冲区生效。切换主模式时,会清除缓冲区的所有局部变量,只有少数被标记为 永久局部 的变量例外。
无论当前缓冲区是否有局部值,你都可以用 Lisp 结构 setq-default 来设置变量的 全局值 。用法与 setq 一样,但它设置的是全局值而非局部值(如果存在局部值)。当当前缓冲区有局部值时,新的全局值要等到切换到其他缓冲区才会显现。示例:
(setq-default fill-column 75)
对于被 make-variable-buffer-local 标记过的变量, setq-default 是设置其全局值的 唯一方式 。
Lisp 程序可以用 default-value 查看变量的默认(全局)值。该函数接受一个符号作为参数,返回其默认值。参数需要被求值,通常要显式加引号。例如,获取 fill-column 的默认值:
(default-value 'fill-column)
51.2.4. 文件中的局部变量
在使用 Emacs 编辑文件时,文件本身可以指定要使用的本地变量值。打开文件或设置主模式时,Emacs 会检查文件中的本地变量声明;它会自动将这些变量设为缓冲区本地变量,并将其值设置为文件中指定的值。
如果文件所在目录已声明目录本地变量(参见 “按目录本地变量”),文件本地变量会覆盖目录本地变量。
51.2.4.1. 指定文件变量
指定文件本地变量值有两种方式:在文件 first line首行 ,或使用本地变量列表。以下是在首行指定的方法:
-*- mode: modename; var: value; … -*-
你可以用这种方式指定任意多组 “变量 / 值” 对,每组用冒号和分号分隔。特殊的 “变量 / 值” 对 mode: modename ;(如果存在)用于指定主模式(无需带 '-mode' 后缀)。值会被直接使用, 不会被求值 。
你可以使用 M-x add-file-local-variable-prop-line 代替手动添加条目。该命令会提示输入变量名和值,并以合适的格式将它们添加到首行。 M-x delete-file-local-variable-prop-line 会提示输入变量名,并从该行中删除对应的条目。命令 M-x copy-dir-locals-to-file-locals-prop-line 可将当前的目录本地变量复制到首行(参见 “按目录本地变量”)。
下面是一个首行示例,指定 Lisp 模式并设置两个数值型变量:
;; -*- mode: Lisp; fill-column: 75; comment-column: 50; -*-
除 mode 外,作为文件变量具有特殊含义的其他关键字还有: coding 、 unibyte 和 eval 。下文会介绍这些关键字。
在 Shell 脚本中,首行用于指定脚本解释器,因此不能在那里放置本地变量。为适配这种情况,如果首行指定了解释器,Emacs 会 检查 second line第二行 是否有本地变量声明。对于以特殊字符串 '\" 开头以指定 troff 预处理程序列表的 man 手册页(并非全部都如此),规则也是一样的。
除了使用 '-*-' 行之外,你还可以在文件末尾附近用 local variables list本地变量列表 定义文件本地变量。本地变量列表的起始位置距离文件末尾不应超过 3000 字符;如果文件分页,则必须位于最后一页。
如果一个文件同时存在本地变量列表和 '-*-' 行,
- Emacs 会先处理 '-*-' 行的所有内容,再处理本地变量列表。
- 主模式指定是个例外:无论出现在哪里,Emacs 都会 最先应用 主模式,因为大多数主模式在初始化时会清除所有本地变量。
本地变量列表以包含字符串 'Local Variables' : 的行开始,以包含 'End:' 的行结束。中间每行写一组变量名和值,示例如下:
/* Local Variables: */ /* mode: c */ /* comment-column: 0 */ /* End: */
本例中每行都以前缀 /* 开头、后缀 */ 结尾。Emacs 通过列表首行中包裹着 'Local Variables:' 的前缀和后缀来识别它们,然后自动在列表其他行中忽略这些前缀后缀。使用前缀 / 后缀的常见原因是:将本地变量列表 嵌入注释 ,避免干扰处理该文件的其他程序。上面的例子适用于 C 语言,其注释以 /* 开头、 */ 结尾。
如果某些无关文本可能被 Emacs 误判为本地变量列表,你可以在该文本后插入一个换页符(分页符,参见 Pages)来避免。Emacs 只在文件 最后一页 、最后一个分页符之后查找文件本地变量。
除了直接手写本地变量列表,你还可以使用命令 M-x add-file-local-variable 。该命令会提示输入变量名和值,并将它们加入列表,必要时自动添加 'Local Variables:' 字符串与起止标记。命令 M-x delete-file-local-variable 从列表中删除一个变量。 M-x copy-dir-locals-to-file-locals 将目录本地变量复制到该列表(参见 “按目录本地变量”)。
与 -*- 行一样,本地变量列表中的变量值会 直接使用,不会先被求值 。如果你想把一个长字符串值拆成多行书写,可以使用反斜杠 + 换行( \+ 换行 ),Lisp 字符串常量中会忽略这种写法。你应该在每一行都加上前缀和后缀,即使该行在字符串中间开始或结束 —— 处理列表时它们会被自动去掉。示例:
# Local Variables: # compile-command: "cc foo.c -Dfoo=bar -Dhack=whatever \ # -Dmumble=blaah" # End:
在本地变量列表中,部分名称具有特殊含义:
mode:启用指定的主模式。eval:对指定的 Lisp 表达式求值(表达式的返回值会被忽略)。coding:指定该文件字符编码转换所用的编码系统。参见 “编码系统”。unibyte:若值为t,则以单字节模式加载或编译该 Emacs Lisp 文件。参见《GNU Emacs Lisp 参考手册》中的 “禁用多字节字符”。
这四个关键字并非真正的变量;在其他上下文中设置它们没有特殊含义。
如果你在不同 Emacs 版本间编辑文件,新版 Emacs 引入了处理某类文件的新模式,你可以用多个 mode 条目,让新版 Emacs 使用新模式(如 my-new-mode ),旧版 Emacs 回退到旧模式(如 my-old-mode )。在文件首行中可以这样写:
-*- mode: my-old; mode: my-new -*-
Emacs 会使用 最后找到 的那个已定义模式:旧版 Emacs 会忽略 my-new-mode ,而支持 my-new-mode 的新版 Emacs 会忽略 my-old-mode 。同理,在文件末尾的本地变量块中:
Local Variables: mode: my-old mode: my-new End:
不要 将 mode 关键字用于次要模式。若要在本地变量列表中启用或禁用某个次要模式,请使用 eval 关键字,并搭配执行对应模式命令的 Lisp 表达式(详见「次要模式」)。例如,下面这段本地变量列表通过无参数调用 eldoc-mode 来启用 ElDoc 模式(见编程语言文档查阅)(传入参数 1 效果相同),并通过传入参数 -1 调用 font-lock-mode 来禁用语法高亮(Font Lock)模式。
;; Local Variables: ;; eval: (eldoc-mode) ;; eval: (font-lock-mode -1) ;; End:
但要注意:以这种方式指定次要模式通常 并不合适 。次要模式多是用户个人偏好,将你的偏好强加给其他可能编辑该文件的用户并不妥当。如果你希望根据场景自动启用 / 禁用次要模式,通常更适合在 主模式钩子 中实现(参见 “钩子”)。
使用命令 M-x normal-mode 可根据文件名与内容,重置缓冲区的本地变量与主模式(包括本地变量列表,如果有的话)。参见 “选择文件模式”
51.2.4.2. 文件变量的安全性
文件局部变量可能存在安全风险;当你打开他人编写的文件时,无法预知文件中的局部变量列表会对你的 Emacs 产生何种影响。~eval~ “变量” 以及 load-path 等其他变量若被设置不当,可能会执行你本不想运行的 Lisp 代码。
因此,每当 Emacs 遇到 无法确认安全 的文件局部变量取值时,都会显示该文件完整的局部变量列表,并在应用前向你请求确认。你可以按下 y 或 SPC 使局部变量列表生效,或按下 n 忽略这些变量。如果 Emacs 以批处理模式运行(参见「初始选项」),由于无法向你询问,会默认按 n 处理。
Emacs 通常会将某些变量 / 取值对认定为安全。例如, comment-column 或 fill-column 接受任意整数都是安全的。如果一个文件只指定了已知安全的变量 / 取值对,Emacs 会直接应用,不再请求确认。反之,你可以在确认提示时按下 ! ,让 Emacs 将当前文件里所有变量 / 取值对都记录为安全。之后 Emacs 再次遇到这些变量 / 取值(无论在同一文件还是其他文件),都会默认视为安全。
你也可以在确认提示时按下 i ,让 Emacs永久忽略该文件中的所有变量 / 取值对 —— 此后这些配置在本文件及所有其他文件中都会被忽略。
当 Emacs 对设置目录局部变量请求确认时(参见「按目录设置局部变量」),在提示处按下 + ,会一次性应用所有变量,并将该目录加入 safe-local-variable-directories 列表(下文说明)。此后 Emacs 会认为该目录是可信的,直接加载其中的所有目录局部变量。 + 操作只应在你完全信任该目录内容时使用。
有些变量(如 load-path )被视为高风险变量:几乎没有合理理由将它们设为局部变量,修改它们可能带来危险。如果一个文件只包含高风险局部变量,Emacs 在确认提示中不会提供、也不接受 ! 作为输入。如果文件中既有高风险变量,也有仅潜在不安全的变量,你仍可以在提示处输入 ! :它会应用所有变量,但只将非高风险的那些标记为未来可信任。如果你确实想为高风险变量记录安全取值,可直接通过自定义 safe-local-variable-values 实现(参见「简易自定义界面」)。同理,若想记录需要永久忽略的高风险变量取值,可自定义 ignored-local-variable-values 。
有时你希望始终信任某些目录中的目录变量,从这些目录加载局部变量时跳过确认提示,即便其中包含风险变量。变量 safe-local-variable-directories 就用于保存这类目录列表,列表中的目录名必须是完整绝对路径。如果变量 enable-remote-dir-locals 设为非空值,该列表还可包含远程目录(参见「远程文件」)。
变量 enable-local-variables 用于控制 Emacs 处理局部变量的方式,默认值为 t ,即采用上文描述的行为:
- 若设为
nil:Emacs 直接忽略所有文件局部变量。 - 若设为
:safe:只使用安全取值,忽略其余所有。 - 若设为
:all:Emacs 会应用所有文件局部变量,无论取值是否安全(不建议长期使用)。 - 其他取值:对每个含局部变量的文件都向你询问,不自动判断是否安全。
变量 enable-local-eval 控制 Emacs 是否处理 eval 变量,取值与 enable-local-variables 类似,可为 t 、 nil 或其他值。其默认值为 maybe (既非 t 也非 nil),因此 Emacs 通常会对处理 eval 变量请求确认。
作为例外:如果某个 eval 表达式出现在变量 safe-local-eval-forms 中,Emacs 执行时不会请求任何确认。
51.2.5. 目录级局部变量
有时,你可能希望对某个目录及其子目录下的 所有文件 定义同一套局部变量,例如大型软件项目的目录树。这可以通过目录局部变量来实现。 文件局部变量会覆盖目录局部变量 ,因此:如果目录中某些文件需要特殊设置,你可以先用目录变量为目录下大多数文件设置通用配置,再在少数需要覆盖的文件里单独定义文件局部变量。
定义目录局部变量的常规方式,是在目录中放置一个名为 .dir-locals.el 的文件25。每当 Emacs 打开该目录或其任意子目录中的文件时,都会应用 .dir-locals.el 里定义的目录局部变量,就像这些变量是该文件自身的文件局部变量一样(见「文件中的局部变量」)。Emacs 会从当前打开文件所在的目录开始,向上遍历目录树查找 .dir-locals.el 。为避免速度变慢,远程文件默认跳过此搜索。如果需要,可以将变量 enable-remote-dir-locals 设为 t ,从而对远程文件也启用搜索。
你还可以使用 .dir-locals-2.el 。如果它与 .dir-locals.el 位于同一目录,Emacs 会在加载 .dir-locals.el 之后额外加载它。这在 .dir-locals.el 处于共享仓库的版本控制中、无法用于个人自定义时非常有用。
.dir-locals.el 文件应存放一个特定结构的列表,它将主模式名(符号)映射到关联列表(alists)(见《Emacs Lisp 参考手册》中的关联列表)。每个关联列表项由一个变量名和该变量的目录局部值组成,在指定主模式启用时生效。你可以不写模式名,而是指定 nil ,表示该关联列表对所有模式生效;也可以指定一个子目录(字符串),此时该关联列表只对该子目录下的所有文件生效。
下面是一个 .dir-locals.el 文件示例:
((nil . ((indent-tabs-mode . t)
(fill-column . 80)
(mode . auto-fill)))
(c-mode . ((c-file-style . "BSD")
(subdirs . nil)))
("src/imported"
. ((nil . ((change-log-default-name
. "ChangeLog.local"))))))
它为目录树下任意文件设置了 indent-tabs-mode 和 fill-column ,并为所有 C 源码文件设置了缩进风格。特殊的 mode 项用于指定要启用的次要模式,因此 (mode . auto-fill) 表示需要启用 auto-fill-mode 。特殊的 subdirs 项不是变量,而是一个关键字,表示 C 模式的设置只应用于当前目录,不应用到子目录。最后,它为 src/imported 子目录下的所有文件指定了不同的 ChangeLog 文件名。
如果 .dir-locals.el 中,同一个变量在不同模式或不同目录下定义了多个值,Emacs 会按更具体优先的规则应用:更具体的模式优先于更通用的模式;目录指定的值优先级高于模式指定的值。例如:
((nil . ((fill-column . 40)))
(c-mode . ((fill-column . 50)))
(prog-mode . ((fill-column . 60)))
("narrow-files" . ((nil . ((fill-column . 20))))))
使用 c-mode 的文件同时匹配 prog-mode (因为前者继承自后者),但 C 文件中 fill-column 仍取 50,因为 c-mode 更具体。其他继承自 prog-mode 的模式会使用 60。 narrow-files 目录下的任何文件都会使用 20,即使它们使用 c-mode ,因为目录项优先级高于模式项。
你可以在 .dir-locals.el 中指定变量 mode、eval 和 unibyte,它们的含义与文件局部变量中一致。coding 不能作为目录局部变量指定。详见「文件中的局部变量」。
.dir-locals.el 中的特殊键 auto-mode-alist 可用于设置文件的主模式,用法与变量 auto-mode-alist 非常相似(见「选择文件模式」)。例如,你可以这样告诉 Emacs:本目录下的 .def 文件应使用 C 模式:
((auto-mode-alist . (("\\.def\\'" . c-mode))))
不必手动编辑 .dir-locals.el ,你可以使用命令: M-x add-dir-local-variable 提示输入模式或子目录、变量名与值,并添加目录局部变量定义项。 M-x delete-dir-local-variable 删除一项。 M-x copy-file-locals-to-dir-locals 将当前文件的文件局部变量复制到 .dir-locals.el ;如果同时存在 .dir-locals-2.el ,则复制到后者。
使用前缀参数时,这三个命令都会提示你选择要修改的文件。文件不必已存在,但必须输入合法文件名: .dir-locals.el 或 .dir-locals-2.el 。
还有一个命令可弹出简易自定义缓冲区编辑目录局部变量: customize-dirlocals (见「简易自定义界面」)。
另一种指定目录局部变量的方法是:使用函数 dir-locals-set-class-variables 在一个 directory class目录类 中定义一组变量 / 值对,再用函数 dir-locals-set-directory-class 告诉 Emacs 哪些目录对应这个类。这些函数调用通常写在你的初始化文件中(见「Emacs 初始化文件」)。当你因某些原因无法在目录中放置 .dir-locals.el 时,这种方法很有用。例如,你可以用这种方式对不可写目录应用设置:
(dir-locals-set-class-variables 'unwritable-directory
'((nil . ((some-useful-setting . value)))))
(dir-locals-set-directory-class
"/usr/include/" 'unwritable-directory)
如果一个变量同时指定了目录局部值和文件局部值,文件局部值优先。不安全的目录局部变量的处理方式,与不安全的文件局部变量完全相同(见「文件变量的安全性」)。
目录局部变量也会在某些不直接打开文件、但在目录内工作的缓冲区中生效,例如 Dired 缓冲区(见「Dired:目录编辑器」)。
51.2.5.1. 通过 EditorConfig 设置目录级变量
EditorConfig 标准是 .dir-locals.el 文件的一种替代方案,它虽然只能控制极少数变量,但优点是 与编辑器无关 ,并非 Emacs 专用。这些配置保存在名为 .editorconfig 的文件中,会作用于该目录及其子目录下的文件。
如果希望 Emacs 遵循 .editorconfig 文件中的配置,需要开启全局次要模式 editorconfig-mode 。通常只需这样启用即可:当该模式激活后,每当打开一个文件,Emacs 会在该文件所在目录及其上级目录中查找 .editorconfig ,就像查找 .dir-locals.el 一样。
如果同时找到 .editorconfig 和 .dir-locals.el ,两者的配置会合并;若出现配置冲突, 目录层级更近 的文件中的配置优先;如果层级相同,则 .dir-locals.el 优先。在安全性方面, .editorconfig 的配置会受到与 .dir-locals.el 和文件局部变量相同的安全检查(同样遵循 enable-local-variables )。详见 “文件变量的安全性”。
EditorConfig 标准中的 indent_size (缩进大小)在 Emacs 中没有固定对应的变量,而是需要根据主模式设置不同的变量。理想情况下,所有主模式都应设置对应的 editorconfig-indent-size-vars ;但如果你使用的某个主模式因尚未支持而导致 indent_size 不生效,可以通过自定义变量 editorconfig-indentation-alist ,告诉 Emacs 在该主模式下需要设置哪些变量。
类似地,删除行尾空白有多种不同实现方式。当使用 EditorConfig 的 trim_trailing_whitespace (删除行尾空白)配置时,默认情况下 editorconfig-mode 会在每次保存文件时直接调用 delete-trailing-whitespace (参见 “无用空白”)。如果你希望使用其他行为,可以将 editorconfig-trim-whitespaces-mode 自定义为你偏好的次要模式,例如 ws-butler-mode 。
51.2.6. 连接级局部变量
大部分变量反映的是本地机器上的状态。当你在使用远程默认目录的缓冲区中操作时,这些变量往往需要使用不同的值。例如调用 shell 时的行为:在本地机器上,你可能使用 /bin/bash 并依赖 termcap ;但在远程机器上,则可能是 /bin/ksh 与 terminfo 。
这类需求可以通过连接局部变量(connection-local variables)实现。目录局部变量与文件局部变量的优先级高于连接局部变量。不安全的连接局部变量,其处理方式与不安全的文件局部变量相同(参见 “文件变量的安全性”)。
连接局部变量通过 connection-local-set-profile-variables 函数,以 配置文件(profile) 的形式成组声明变量 / 值对。函数 connection-local-set-profiles 则根据指定的匹配条件(用于标识远程主机)来激活这些配置文件:
(connection-local-set-profile-variables 'remote-terminfo
'((system-uses-terminfo . t)
(comint-terminfo-terminal . "dumb-emacs-ansi")))
(connection-local-set-profile-variables 'remote-ksh
'((shell-file-name . "/bin/ksh")
(shell-command-switch . "-c")))
(connection-local-set-profile-variables 'remote-bash
'((shell-file-name . "/bin/bash")
(shell-command-switch . "-c")))
(connection-local-set-profiles
'(:application tramp :machine "remotemachine")
'remote-terminfo 'remote-ksh)
这段代码声明了三个不同的配置文件: remote-terminfo 、 remote-ksh 和 remote-bash 。其中 remote-terminfo 和 remote-ksh 会应用到所有远程默认目录的主机名匹配正则表达式 "remotemachine" 的缓冲区。
这类匹配条件还可以根据以下属性区分:
:protocol(即 Tramp 方法):user(远程用户名)
条件为 nil 时,匹配所有带有远程默认目录的缓冲区。
注意事项,当多个配置文件声明同一个变量,且这些配置文件的匹配条件可能同时生效时,最终使用哪个变量值是未定义的,需谨慎使用。
如果在某个缓冲区中设置了连接局部变量,之后又修改了该缓冲区的主模式,需要特别注意:切换主模式时,所有缓冲区局部变量都会被清除,连接局部变量的值也会丢失。可以通过将对应变量的 permanent-local 符号属性设为非 nil 来避免这一问题。
51.3. 按键绑定自定义
本节介绍 key bindings按键绑定 (将按键映射到命令)与 keymaps键映射表 (记录按键绑定),同时说明如何自定义按键绑定(通过编辑初始化文件实现,参见「在初始化文件中重新绑定按键」)。
由于多数模式都会定义自身的按键绑定,激活某个模式可能会覆盖你自定义的按键绑定。有少量按键是保留给用户自定义绑定的,模式不应占用,因此使用这些按键的绑定在这方面更安全。保留的按键序列包括:
C-c后接一个字母(大写或小写)- 不带修饰键的功能键 F5 至 F9(参见「修饰键」)
51.3.1. 键映射
正如 “按键与命令” 一节所述,每个 Emacs 命令都是一个 Lisp 函数,其定义支持交互式使用。与所有 Lisp 函数一样,命令拥有一个函数名,通常由小写字母和连字符组成。
key sequence按键序列 (简称 key按键 )是一组作为整体具有意义的输入事件序列。输入事件包括字符、功能键和鼠标按键 —— 所有你可以发送给计算机的输入。按键序列的含义来自它的绑定,即该按键序列所执行的命令。
按键序列与命令函数之间的绑定关系,存储在名为 键映射(keymap) 的数据结构中。Emacs 包含大量键映射,每种键映射只在特定场景下生效。
global全局 键映射是最重要的键映射,因为它始终生效。全局键映射为 基本模式(Fundamental mode) 定义按键(参见主模式);其中大部分定义对绝大多数或全部主模式通用。每个主模式或次要模式都可以拥有自己的键映射,用于覆盖全局键映射中部分按键的定义。
例如, g 这类自插入字符能够自动插入文本,是因为全局键映射将它绑定到 self-insert-command 命令。 C-a 这类标准 Emacs 编辑按键,其默认功能也来自全局键映射。用于重新绑定按键的命令(如 M-x keymap-global-set ),其工作方式就是将新绑定存入全局键映射的对应位置(参见交互式修改按键绑定)。使用命令 C-h b 可以查看当前所有按键绑定。
现代键盘通常同时拥有字符键和功能键。功能键与字符键一样会发送输入事件,键映射也可以为功能键设置绑定。按键序列可以混合功能键与字符。例如,如果你的键盘有 Home 功能键,Emacs 可以识别 C-x Home 这类按键序列。你甚至可以混合鼠标事件与键盘事件,例如 S-down-mouse-1 。
在文本终端下,按下一个功能键实际上会向计算机发送一串字符序列;该序列的具体格式取决于功能键与终端类型(通常以 ESC [ 开头)。如果 Emacs 能正确识别你的终端类型,会自动将这类序列当作单个输入事件处理。
以 C-c 后接一个字母(大写或小写,ASCII 或非 ASCII)组成的按键序列 保留给用户使用 。Emacs 自身永远不会绑定这些按键序列,Emacs 扩展也应避免占用它们。也就是说,用户可以放心绑定 C-c a 或 C-c ç 这类按键,不必担心被 Emacs 其他绑定覆盖。
51.3.2. 前缀键映射
在内部实现中,Emacs 在每个键映射里只记录 单个事件 。解析由多个事件组成的按键序列,需要通过 一连串键映 射来完成:第一个键映射给出第一个事件的定义,该定义又是另一个键映射,再用它去查找序列中的第二个事件,依此类推。因此,像 C-x 或 ESC 这样的前缀键都拥有自己的键映射,里面存放着紧跟在该前缀之后的事件定义。
前缀键的定义通常是一个键映射,用于查找后续的事件。该定义也可以是一个 Lisp 符号,其函数定义就是对应的后续键映射;效果完全相同,但这样会为前缀键提供一个命令名,可用来说明该前缀键的用途。例如, C-x 绑定到符号 Control-X-prefix ,它的函数定义就是存放 C-x 系列命令的键映射。 C-c 、 C-x 、 C-h 和 ESC 作为前缀键的定义出现在全局键映射中,因此这些前缀键始终可用。
除了普通前缀键,还有一个虚拟的 “前缀键” 用于表示菜单栏;关于菜单栏按键绑定的特殊信息,参见《Emacs Lisp 参考手册》中的 “菜单栏” 一节。触发弹出菜单的鼠标按键事件也属于前缀键;更多细节参见《Emacs Lisp 参考手册》中的 “菜单键映射” 一节。
部分前缀键映射存放在以特定名称命名的变量中:
ctl-x-map是C-x之后所跟字符对应的键映射变量名。help-map对应C-h之后的字符。esc-map对应ESC之后的字符。因此,所有 Meta 字符实际上都由该键映射定义。ctl-x-4-map对应C-x 4之后的字符。mode-specific-map对应C-c之后的字符。project-prefix-map对应C-x p之后的字符,用于项目相关命令(参见 “使用项目”)。
51.3.3. 局部键映射
到目前为止,我们已经介绍了全局键映射的相关细节。主模式通过提供自身的局部键映射来自定义 Emacs 行为。例如,C 语言模式会重新定义 TAB 键,使其为 C 代码对当前行进行缩进。次要模式也可以拥有局部键映射;只要某个次要模式处于启用状态,它的键映射定义就会同时覆盖主模式的局部键映射和全局键映射。此外,缓冲区中的部分文本也可以指定自身的键映射,覆盖所有其他键映射。
局部键映射可以将某个按键重新定义为前缀键,只需将其定义为一个前缀键映射即可。如果该按键在全局中也被定义为前缀,那么它的局部定义与全局定义(两者都是键映射)会有效合并:查找前缀键之后的事件时,两种定义都会被使用。例如,如果某个局部键映射将 C-c 定义为前缀键映射,并且该映射中把 C-z 绑定到某个命令,这就为 C-c C-z 提供了局部含义。这不会影响其他以 C-c 开头的序列;如果这些序列没有对应的局部绑定,它们的全局绑定依然生效。
另一种理解方式是:Emacs 处理多事件按键序列时,会依次在多个键映射中查找整个按键序列的绑定。它会先检查已启用的次要模式键映射,然后检查主模式键映射,最后检查全局键映射。这并非键查找的精确内部机制,但在通常情况下足以帮你理解最终效果。
51.3.4. 迷你缓冲区键映射
迷你缓冲拥有一套自身的局部键映射,其中包含各种补全与退出命令。
minibuffer-local-map用于普通输入(无补全)。minibuffer-local-ns-map与之类似,区别是空格SPC与回车RET一样会直接退出。minibuffer-local-completion-map用于宽松补全。minibuffer-local-must-match-map用于严格补全与谨慎补全。minibuffer-local-filename-completion-map和上面两个类似,但专门用于文件名补全,它不会绑定空格SPC。
默认情况下,在 minibuffer-local-completion-map 里, TAB 、 SPC 和 ? 用于触发补全。如果你经常需要对包含空格或问号的内容进行补全,可以把下面代码放到配置文件中,禁用这些按键的补全功能:
(keymap-set minibuffer-local-completion-map "SPC" 'self-insert-command) (keymap-set minibuffer-local-completion-map "?" 'self-insert-command)
51.3.5. 交互式修改键绑定
重新定义 Emacs 按键的方式,是修改它在键映射中的条目。你可以修改全局键映射,这样修改会在所有主模式中生效(那些对同一按键有覆盖性局部绑定的模式除外)。或者,你也可以修改局部键映射,这会影响所有使用同一主模式的缓冲区。
本节介绍如何为当前 Emacs 会话重新绑定按键。关于如何让按键绑定在后续 Emacs 会话中持续生效,参见「在初始化文件中重新绑定按键」。
M-x keymap-global-set RET key cmd RET- 全局定义 key按键 ,使其运行指定 cmd命令 。
M-x keymap-local-set RET key cmd RET- 在当前生效的主模式中局部定义 key按键 ,使其运行指定 cmd命令 。
M-x keymap-global-unset RET key- 在全局键映射中取消该 key按键 的定义。
M-x keymap-local-unset RET key- 在当前主模式中局部取消该 key按键 的定义。
例如,下面的操作将 C-z 绑定到 shell 命令(参见交互式子 Shell),替换掉 C-z 原本的全局定义:
M-x keymap-global-set RET C-z shell RET
keymap-global-set 命令会在按下按键后读取命令名。按下按键后,会出现类似下面的提示,让你确认正在绑定想要的按键:
Set key C-z to command:
你可以用同样方式重新定义功能键和鼠标事件;只需在指定要重新绑定的按键时,按下功能键或点击鼠标即可。
你也可以用同样方式重新绑定 包含多个事件 的按键。Emacs 会持续读取按键,直到它是一个完整按键(即不是前缀键)为止。例如,如果你输入 C-f 作为按键,到此结束;它会立刻进入迷你缓冲读取命令名。但如果你输入 C-x ,因为它是前缀键,Emacs 会继续读取下一个字符;如果下一个是 4 (也是前缀),它会再读取一个字符,依此类推。例如:
M-x keymap-global-set RET C-x 4 $ spell-other-window RET
会将 C-x 4 $ 重新定义为运行(虚构的) spell-other-window 命令。
你可以用 keymap-global-unset 移除一个按键的全局定义。这会让该按键 undefined未定义 ;按下时 Emacs 只会发出蜂鸣声。类似地, keymap-local-unset 会让按键在当前主模式键映射中未定义,从而使该主模式下重新使用全局定义(或无定义)。
如果你已经重新定义(或取消定义)了一个按键,之后想要撤销修改, 仅取消定义是不够的 —— 你需要用它的 标准定义 重新绑定该按键。要查看一个按键的标准定义名称,可以在全新启动的 Emacs 中进入 Fundamental 模式缓冲区,并用 C-h c 查看。本手册中对按键的说明也会列出它们对应的命令名。
如果你想防止自己误执行某个命令, 更好的做法是禁用该命令,而不是取消按键定义 。当你真的需要使用时,启用被禁用的命令比重新定义按键更省事。参见「禁用命令」。
51.3.6. 在初始化文件中重新绑定键
如果你有一组常用的按键绑定,希望长期生效,可以通过编写 Lisp 代码,把它们写在 初始化文件 里。关于初始化文件的说明,参见《Emacs 初始化文件》。
使用 Lisp 编写按键绑定的 推荐方式 是使用 keymap-global-set 或 keymap-set 函数。例如,下面代码将全局键映射中的 C-z 绑定到 shell 命令(参见交互式子 Shell):
(keymap-global-set "C-z" 'shell)
keymap-global-set 的第一个参数描述按键序列,它是一个由空格分隔多个按键组成的字符串。带修饰符的按键可以通过前缀指定,如 'C-' 表示 Ctrl, 'M-' 表示 Meta。特殊按键(如 TAB 、 RET )可以用尖括号表示: <TAB> 、 <RET> 。
绑定到按键序列的命令名(如上例中的 shell )前面的 单引号 ,表示它是一个常量符号,而非变量。如果省略引号,Emacs 会尝试把 shell 当作变量求值,这通常会报错,也不是你想要的效果。
下面是更多示例,包括绑定功能键和鼠标事件:
(keymap-global-set "C-c y" 'clipboard-yank) (keymap-global-set "C-M-q" 'query-replace) (keymap-global-set "<f5>" 'flyspell-mode) (keymap-global-set "C-<f5>" 'display-line-numbers-mode) (keymap-global-set "C-<right>" 'forward-sentence) (keymap-global-set "<mouse-2>" 'mouse-save-then-kill)
按键序列也可以直接绑定到 Lisp 字符串,而不是命令。字符串的写法与按键序列语法相同。例如,把 C-c h 绑定到字符串 'hello' :
(keymap-global-set "C-c h" "h e l l o")
这样写比较繁琐,可以用便捷函数 key-description :
(keymap-global-set "C-c h" (key-description "hello"))
字符串中可以 直接写非 ASCII 字符 。例如绑定到 'olá' :
(keymap-global-set "C-c h" (key-description "olá"))
但要注意,语言环境与编码系统可能会给非 ASCII 字符的按键绑定带来问题(参见初始化文件中的非 ASCII 字符)。直接使用 Unicode 编码 可以避免这类问题(如何在 Emacs 内查看字符编码,参见国际字符集简介):
(keymap-global-set "C-c h" (key-description "ol\u00E1"))
你也可以使用底层函数 define-key 和 global-set-key 。例如,用底层函数实现上面 C-z 绑定 shell 的效果:
(global-set-key (kbd "C-z") 'shell)
指定按键序列有多种方式,最简单的是使用示例中的 kbd 函数。 kbd 接收一个表示按键序列的字符串,将其转换为适合 global-set-key 等底层函数使用的格式。更多关于用 Lisp 绑定按键的细节,参见《Emacs Lisp 参考手册》中的键映射章节。
正如 局部键映射 中所述,主模式和次要模式都可以定义局部键映射。这些键映射会在会话中 第一次加载该模式 时创建。可以使用 keymap-set 函数修改指定的键映射,使用 keymap-unset 移除按键绑定。
由于模式的键映射只有在加载后才会创建,因此必须 延迟执行 修改它们的代码,例如把代码放到 模式钩子(mode hook) 中(参见钩子)。例如,Texinfo 模式会运行 texinfo-mode-hook 。下面示例展示如何通过钩子,在 Texinfo 模式中添加局部绑定 C-c n 、 C-c p ,并移除 C-c C-x x 的绑定:
(add-hook 'texinfo-mode-hook
(lambda ()
(keymap-set texinfo-mode-map "C-c p" 'backward-paragraph)
(keymap-set texinfo-mode-map "C-c n" 'forward-paragraph)
(keymap-set texinfo-mode-map "C-c C-x x" nil)))
51.3.7. 修饰键
Emacs 的默认按键绑定对 带修饰符的字母 不区分大小写。也就是说, C-A 与 C-a 功能相同, M-A 与 M-a 功能相同。这一规则只适用于字母,不适用于其他按键的 Shift 版本;例如 C-@ 和 C-2 就不是同一个按键。
带 Control 修饰符的字母通常被视为不区分大小写:Emacs 总是把 C-A 当作 C-a 、 C-B 当作 C-b ,依此类推。这是出于历史原因:在非图形化环境中,这些按键本身就没有区别。不过,在 图形界面(GUI) 中,你可以绑定带 Shift 的 Control + 字母组合 :
(keymap-global-set "C-S-n" #'previous-line)
对于其他所有修饰符,你都可以在自定义 Emacs 时让修饰后的字母区分大小写(即使在非图形界面也可以)。例如,你可以让 M-a 和 M-A 执行不同命令。
虽然日常常用的只有 Control 和 Meta 修饰键,但 Emacs 还支持另外三种修饰键: Super 、 Hyper 和 Alt 。大多数终端不支持使用这些修饰键;键盘上标注的 Alt 键通常发出的是 Meta 信号,而非 Alt 。Emacs 的标准按键绑定中 没有 使用 Super 、 Hyper 的按键,使用 Alt 的也很少。但你可以自定义这些修饰键的绑定。它们的标识分别是:
- 's-' :Super
- 'H-' :Hyper
- 'A-' :Alt
即使你的键盘没有这些额外修饰键,也可以用 C-x @ 来输入:
C-x @ h给下一个字符加上 Hyper 修饰C-x @ s给下一个字符加上 Super 修饰C-x @ a给下一个字符加上 Alt 修饰
例如, C-x @ h C-a 就相当于输入 Hyper-Control-a 。(但不能对同一个字符连续按两次 C-x @ 来叠加两个修饰符,因为第一次修饰会作用在 C-x 上。)同理,也可以用 C-x @ S 、 C-x @ c 、 C-x @ m 分别输入 Shift 、 Control 、 Meta ,只是很少用到。
在图形终端下,你可以开启 修饰键栏模式(Modifier Bar mode),通过点击工具栏按钮来模拟缺少的修饰键。参见 “工具栏”。
51.3.8. 重新绑定功能键
按键序列可以包含普通字符,也可以包含功能键。Lisp 用字符(实际是整数)表示键盘上的普通字符,用Lisp 符号表示功能键。如果一个功能键的标签是一个单词,那么这个单词就是对应 Lisp 符号的名称。下面是常见功能键在 Lisp 中的常规名称:
left,up,right,down- 光标方向键。
begin,end,home,next,prior- 其他光标定位键。
select,print,execute,backtabinsert,undo,redo,clearlineinsertline,deleteline,insertchar,deletechar- 杂项功能键。
f1,f2, …f35- 编号功能键(键盘顶部的功能键)。
kp-add,kp-subtract,kp-multiply,kp-dividekp-backtab,kp-space,kp-tab,kp-enterkp-separator,kp-decimal,kp-equalkp-prior,kp-next,kp-end,kp-homekp-left,kp-up,kp-right,kp-downkp-insert,kp-delete- 小键盘按键(主键盘右侧),包含符号或功能键。
kp-0,kp-1, …kp-9- 小键盘数字键。
kp-f1,kp-f2,kp-f3,kp-f4- 小键盘 PF 键。
这些名称是通用惯例,但某些系统(尤其是使用 X 窗口时)可能使用不同名称。想要确定你的终端上某个功能键对应的具体符号,可以按 C-h c 再按该键查看。
关于绑定功能键的示例,参见「在初始化文件中重新绑定按键」。
很多键盘右侧带有数字小键盘。小键盘上的数字键同时也是光标移动键,由标有 'Num Lock' 的按键切换。默认情况下,Emacs 会将这些按键翻译为主键盘上对应的按键。例如:
- 当 'Num Lock' 开启时,小键盘上标有 '8' 的键产生
kp-8,会被翻译成8; - 当 'Num Lock' 关闭时,同一个键产生
kp-up,会被翻译成UP。
如果你重新绑定了 8 或 UP 这类按键,对应的小键盘按键也会受影响。但是,如果你直接重新绑定某个 'kp-' 开头的按键,则不会影响主键盘上的等价按键。注意:带修饰符的按键不会被翻译。例如,按住 Meta 键再按小键盘的 '8' ,会生成 M-kp-8 。
Emacs 提供了便捷方式来绑定数字小键盘按键,通过以下变量: keypad-setup 、 keypad-numlock-setup 、 keypad-shifted-setup 、 keypad-numlock-shifted-setup 。这些选项可以在 keyboard 自定义组中找到(参见简易自定义界面)。你可以重新绑定这些按键以实现其他功能,例如输入数字前缀参数。
51.3.9. 命名 ASCII 控制字符
TAB、RET、BS、LFD、ESC 和 DEL 最初是某些 ASCII 控制字符的名称,它们使用极为频繁,因此拥有了专属的特殊按键。例如, TAB 原本就是 C-i 的另一种写法。后来,用户发现在 Emacs 中区分这些按键与用 Ctrl 输入的对应控制字符会更方便。因此,在大多数现代终端上,它们 不再等同 : TAB 与 C-i 是不同的按键。
如果键盘支持区分,Emacs 就能识别这两种输入。它会把这些特殊按键当作名称为 tab 、 return 、=backspace= 、 linefeed 、 escape 和 delete 的功能键来处理。如果这些功能键没有自己的绑定,会自动转成对应的 ASCII 字符。这样一来,除非特意区分,用户和 Lisp 程序都无需在意二者差别。
如果你不想区分(例如) TAB 和 C-i ,只需为 ASCII 字符 TAB (八进制码 011)设置一次绑定即可。如果你 希望 区分,就分别为该 ASCII 字符和功能键 tab 各设一个绑定。
在普通 ASCII 终端上,无法区分 TAB 和 C-i (其他同类组合也一样),因为终端在两种情况下发送的是同一个字符。
51.3.10. 重新绑定鼠标按钮
Emacs 同样使用 Lisp 符号来表示鼠标按键。Emacs 中的普通鼠标事件是 click events点击事件 :在按下并松开按键、且未移动鼠标时触发。你也可以获得 drag events拖动事件 :按住按键的同时移动鼠标,在最终松开时触发。
基础点击事件的符号为: mouse-1 表示最左侧按键, mouse-2 为下一个,依此类推。例如,可将第二个鼠标按键重新定义为分割当前窗口:
(keymap-global-set "<mouse-2>" 'split-window-below)
拖动事件的符号类似,只是在 'mouse' 前加前缀 'drag-' 。例如,拖动第一个按键会生成 drag-mouse-1 事件。
你还可以为 按下鼠标按键瞬间 的事件定义绑定。这类事件以 'down-' 而非 'drag-' 开头,且只有在存在绑定时才会生成。当你收到一个按下事件后,必然会跟随一个对应的点击或拖动事件。
你可以区分 single单击、double双击、triple clicks三击 。双击是指在大致相同位置快速点击同一按键两次。第一次点击产生普通点击事件;如果第二次点击足够快,则会产生 双击事件 。双击事件的类型以 'double-' 开头,例如 double-mouse-3 。
这意味着你可以给同一位置的第二次点击赋予特殊含义,但必须基于第一次点击已经执行了普通单击的绑定。
这虽然限制了双击能做的事,但界面设计规范建议任何情况下都应遵守这一约束:双击应做与单击类似、但更进一步的操作,双击命令只需负责额外的工作。
如果某个双击事件没有绑定,会自动降级为对应单击事件。因此,若未专门定义双击,它会执行两次单击命令。
Emacs 同样支持以 'triple-' 开头的 三击事件 。Emacs 不把四击当作独立事件类型,三次以上的点击会继续生成三击事件。不过点击总数会记录在事件列表中,熟悉 Emacs Lisp 可自行区分(详见《Emacs Lisp 参考手册》的点击事件)。不建议为超过三击的点击设置独立含义,但可以让后续点击循环复用前三种含义:四击 = 单击,五击 = 双击,六击 = 三击。
Emacs 还会在拖动与按下事件中记录多次点击。例如:双击后按住拖动,会产生 'double-drag-' 事件;第二次按下瞬间会产生 'double-down-' 事件(若无绑定则和所有按下事件一样被忽略)。
变量 double-click-time 设定多击之间允许的最大间隔时间,单位毫秒: nil :不识别双击; t :无时间限制;默认:500(0.5 秒)
变量 double-click-fuzz 设定多击之间允许的鼠标最大移动距离:图形界面:单位为像素;文本终端:单位为 1/8 字符宽度;默认:3
鼠标事件符号中也可体现修饰键状态,使用常见前缀: ‘C-’、‘M-’、‘H-’、‘s-’、‘A-’、‘S-’。修饰键前缀永远在 ‘double-’/‘triple-’ 之前,后者又永远在 ‘drag-’/‘down-’ 之前。
窗口中存在不显示缓冲区文本的特殊区域,如模式行、滚动条。可通过 虚拟前缀键 判断鼠标事件是否来自这些区域。例如,在模式行中点击鼠标,会在普通鼠标符号前加上前缀 mode-line 。下面示例将模式行上的鼠标左键点击绑定到 scroll-up-command :
(keymap-global-set "<mode-line> <mouse-1>" 'scroll-up-command)
完整虚拟前缀键列表:
mode-line- 鼠标位于窗口模式行。
vertical-line- 鼠标位于并排窗口间的垂直分隔线(若有滚动条则会取代该位置)。
vertical-scroll-bar- 鼠标位于垂直滚动条(Emacs 当前仅支持此类滚动条)。
menu-bar- 鼠标位于菜单栏。
tab-bar- 鼠标位于标签栏。
tab-line- 鼠标位于标签行。
header-line- 鼠标位于标题行。
你可以在一个按键序列中放入多个鼠标按键,但这并不常用。
51.3.11. 禁用命令
Diabling a command禁用命令 是指:以交互方式调用该命令时,Emacs 会要求用户确认。禁用命令的目的是 防止用户误执行 ,主要用于那些对新手可能造成困扰的命令。
在 Emacs 中尝试调用被禁用的交互命令时,会立即弹出一个窗口,显示命令名、文档以及操作说明;然后 Emacs 会等待你的输入,让你选择:
- 按要求执行该命令
- 启用该命令并执行
- 取消操作
如果你选择启用命令,还需要再回答一个问题:是永久启用,还是仅当前会话有效。(永久启用会自动修改你的初始化文件。)你也可以按 ! ,仅在当前会话中启用所有命令。
禁用命令的底层机制是:给命令对应的 Lisp 符号设置一个非 nil 的 disabled 属性。对应的 Lisp 代码如下:
(put 'delete-region 'disabled t)
如果 disabled 属性的值是一个字符串,该字符串会在调用命令时一起显示:
(put 'delete-region 'disabled
"建议优先使用 `kill-region'。\n")
除了直接禁用命令,你还可以选择更温和的方式:执行前询问。例如,想在执行 M-> (end-of-buffer) 命令前弹出确认,可以在初始化文件中加入:
(command-query
'end-of-buffer
"确定要跳转到缓冲区末尾吗?")
默认会用 y/n 询问;如果给第三个可选参数传非 nil 值,则会用 yes/no 询问。
你可以通过两种方式禁用命令:直接编辑初始化文件;使用命令 M-x disable-command ,它会自动帮你修改初始化文件。同理, M-x enable-command 会修改初始化文件,永久启用某个命令。参见《Emacs 初始化文件》。
如果 Emacs 以 '-q' 或 '--no-init-file' 参数启动(参见启动选项),它不会修改你的初始化文件。因为 Emacs 没有加载过你的配置,修改可能导致信息丢失。
一个命令是否被禁用,与调用它的按键无关;即使通过 M-x 调用,禁用规则同样生效。但是,从 Lisp 程序中作为函数调用该命令时,禁用不会生效。
51.4. Emacs 初始化文件
Emacs 启动时,通常会尝试从 initialization file初始化文件 (简称 init file)加载一个 Lisp 程序。如果该文件存在,它会定义如何为你初始化 Emacs,以及如何定制各种可选功能。你通过 M-x customize 进行的自定义设置(参见简易自定义界面),默认也会保存在初始化文件中(参见保存自定义设置)。
传统上使用 ~/.emacs 作为初始化文件,不过 Emacs 也会查找以下位置: ~/.emacs.el 、 ~/.emacs.d/init.el 、 ~/.config/emacs/init.el 等。参见《Emacs 如何找到你的初始化文件》。
如果你希望把所有 Emacs 配置集中在一个目录里,推荐使用: ~/.emacs.d/init.el 或符合 XDG 规范的 ~/.config/emacs/init.el 。
你可以使用命令行参数: '-q':禁止加载你的初始化文件; '-u'(或 '--user'):指定加载其他用户的初始化文件。参见《启动选项》。
还存在一个 默认初始化文件 ,它是名为 default.el 的库文件,Emacs 会在标准库搜索路径中查找。官方发行版中并不自带该文件,通常由系统管理员创建,用于存放全局公共配置。如果该文件存在,每次启动 Emacs 都会加载(使用 '-q' 时除外)。 但你的个人初始化文件会优先加载 ;如果其中把 inhibit-default-init 设置为非 nil,则不会再加载 default.el 。
你的系统还可能有一个 站点启动文件 ,名为 site-start.el 。与 default.el 一样,Emacs 会在标准 Lisp 库路径中查找该文件,并在 加载你的个人配置之前 优先加载它。若要禁止加载该文件,可以使用参数 '--no-site-file' 。参见《启动选项》。不建议把部分用户不喜欢的配置放在 site-start.el 中,更适合放在 default.el ,方便用户自行覆盖。
你可以把 default.el 和 site-start.el 放在 Emacs 搜索 Lisp 库的任意目录下。变量 load-path (参见《Emacs Lisp 代码库》)定义了这些目录。很多系统会把这些文件放在 Emacs 安装目录下的 site-lisp 子目录,例如: /usr/local/share/emacs/site-lisp 。
不建议对你的初始化文件进行字节编译(参见《Emacs Lisp 参考手册》中的字节编译)。它通常不会明显加快启动速度,而且忘记重新编译时还容易出问题。更好的做法是使用 Emacs Server 减少启动次数(参见《将 Emacs 作为服务器使用》)。如果你的配置里定义了大量函数,建议把它们移到单独的文件里并进行字节编译,再在初始化文件中加载。
如果你要编写超出简单自定义范围的真实 Emacs Lisp 程序,应当阅读《Emacs Lisp 参考手册》。参见《Emacs Lisp 参考手册》中的 Emacs Lisp 章节。
51.4.1. 初始化文件语法
初始化文件中包含一个或多个 Lisp 表达式。每个表达式由一个函数名后跟若干参数组成,整体用括号包裹。例如: (setq fill-column 60) 会调用 setq 函数,将变量 fill-column (参见 “文本填充”)设为 60。
你可以用 setq 设置任意 Lisp 变量,但在初始化文件中,对某些变量直接用 setq 可能达不到预期效果。有些变量在用 setq 设置时会自动变成 buffer-local缓冲区局部变量 ;而在初始化文件中,你通常需要设置它的 默认值 ,这时应使用 setq-default 。(下一节会同时给出这两种写法的示例。)
部分可定制的次要模式变量,在通过自定义界面(Customize) 设置时会自动启用对应模式,但普通的 setq 不会触发这一逻辑;若要在初始化文件中启用该模式,需要直接调用对应的次要模式命令。最后,少数可定制用户选项的初始化逻辑比较复杂,这类变量必须通过自定义界面设置(参见 “自定义”),或使用 customize-set-variable / setopt (参见 “查看与设置变量”)。
setq 的第二个参数是表示变量新值的表达式。它可以是常量、变量或函数调用表达式。在初始化文件中,常量是最常用的形式,包括:
- Numbers
- 数字以十进制书写,可在开头带负号。
- String
Lisp 字符串语法与 C 字符串基本相同,并增加了一些扩展特性。字符串常量以双引号
"开始和结束。字符串中可以直接包含换行符与特殊字符,但通常使用反斜杠转义序列更清晰:
- '
\n' 换行 - '
\b' 退格 - '
\r' 回车 - '
\t' 制表符 - '
\f' 换页(Ctrl-L) - '
\e' 转义 - '
\\' 反斜杠本身 - '
\"' 双引号 - '
\ooo' 八进制编码为 ooo 的字符
反斜杠与双引号是必须使用转义的字符。
'
\C-' 可作为控制字符前缀,例如 '\C-s表示Ctrl-S=; '\M-' 可作为 Meta 字符前缀,例如 '\M-a=' 表示Meta-A, '\M-\C-a' 表示Ctrl-Meta-A。关于在初始化文件中使用非 ASCII 字符,参见 “初始化文件中的非 ASCII 字符”。
- '
- Characters
Lisp 字符常量以 '?' 开头,后跟一个字符或以 '
\' 开头的转义序列。示例:?x、?\n、?\"、?\)。注意:在 Lisp 中,字符串与字符不可互换,某些语境只能用字符串,某些只能用字符。关于将命令绑定到发送非 ASCII 字符的按键,参见 “初始化文件中的非 ASCII 字符”。
- True
t表示 “真”。- Fasle
nil表示 “假”。- 其他 Lisp 对象
- 写一个单引号 ',后跟你要使用的 Lisp 对象。
如需了解更多 Emacs Lisp 语法,参见《Emacs Lisp 参考手册》中的 “简介” 部分。
51.4.2. 初始化文件示例
下面是一些用 Lisp 表达式实现常见需求的示例:
将一个目录添加到
load-path变量中。之后你就可以把 Emacs 自带之外的 Lisp 库放在这个目录里,并通过M-x load-library加载它们。参见《Emacs Lisp 代码库》。(add-to-list 'load-path "/path/to/lisp/libraries")在 C 模式中,如果光标在一行中间,按
TAB只插入一个制表符。(setq c-tab-always-indent nil)这里的变量取值通常是
t(真),另一种取值是nil(假)。默认让搜索区分大小写(在所有未覆盖该设置的缓冲区中生效)。
(setq-default case-fold-search nil)这会设置默认值,对所有没有为该变量设置局部值的缓冲区生效(见局部变量)。如果用
setq设置case-fold-search,只会影响当前缓冲区的局部值,这通常不是你在初始化文件里想要的效果。指定你自己的邮箱地址(如果 Emacs 无法正确识别)。
(setq user-mail-address "你的邮箱@example.com")
许多 Emacs 包(如 Message 模式)在需要知道你的邮箱时会查阅
user-mail-address。 各种 Emacs 包(例如 Message 模式)在需要知道你的电子邮件地址时,都会读取user-mail-address。参见 “邮件头字段”。将文本模式设为新建缓冲区的默认模式。
(setq-default major-mode 'text-mode)注意这里使用
text-mode,因为它是进入文本模式的命令。其前面的单引号会把这个符号变成常量;否则text-mode会被当作变量名处理。为支持大部分西欧语言的 Latin-1 字符集设置默认环境。
(set-language-environment "Latin-1")关闭行号模式(一个全局次要模式)。
(line-number-mode 0)
在文本模式及相关模式中自动启用自动换行模式(参见钩子)。
(add-hook 'text-mode-hook 'auto-fill-mode)
修改使用剪贴板时的编码系统(参见进程间通信的编码系统)。
(setopt selection-coding-system 'utf-8)加载已安装的名为
foo的 Lisp 库(实际是标准 Emacs 目录下的foo.elc或foo.el文件)。(load "foo")
当 load 的参数是相对文件名(不以 '/' 或 '~' 开头)时, load 会在 load-path 目录列表中搜索(参见 Emacs 的 Lisp 代码库)。
从你的家目录加载编译后的 Lisp 文件
foo.elc。(load "~/foo.elc")这里使用的是完整文件名,因此不会进行目录搜索。
告诉 Emacs:通过加载名为
mypackage的 Lisp 库(即mypackage.elc或mypackage.el文件)来查找函数myfunction的定义:(autoload 'myfunction "mypackage" "Do what I say." t)
其中字符串 "Do what I say." 是该函数的文档字符串。在自动加载定义中指定它,即使包尚未加载,帮助命令也能显示该说明。最后一个参数
t表示此函数是交互式的,即可以通过键入M-x myfunction RET或绑定到按键来交互式调用。如果函数非交互式,则省略t或使用nil。将按键
C-x l重新绑定到函数make-symbolic-link(参见在初始化文件中重新绑定按键)。(keymap-global-set "C-x l" 'make-symbolic-link)或
(keymap-set global-map "C-x l" 'make-symbolic-link)再次注意:单引号用于引用符号
make-symbolic-link,而非其变量值。仅在 Lisp 模式下做同样的绑定。
(keymap-set lisp-mode-map "C-x l" 'make-symbolic-link)将基础模式中当前绑定到
next-line的所有按键,重新定义为执行forward-line。(keymap-substitute global-map 'next-line 'forward-line)
取消
C-x C-v的按键定义。(keymap-global-unset "C-x C-v")取消一个按键定义的原因之一是把它设为前缀键。只要给
C-x C-v定义任何内容,它就会成为前缀,但必须先解除它原本非前缀的定义。让符号 '
$' 在文本模式中具有标点符号的语法属性。注意这里使用了字符常量 '$'。(modify-syntax-entry ?\$ "." text-mode-syntax-table)允许无确认地使用
narrow-to-region命令。(put 'narrow-to-region 'disabled nil)
根据不同平台与 Emacs 版本调整配置。
用户通常希望 Emacs 在所有系统上行为一致,因此同一个初始化文件适用于所有平台。但有时,你用于自定义 Emacs 的某个函数在某些平台或旧版 Emacs 中不可用。要处理这种情况,可以把相关配置放在条件判断里,先检测函数或功能是否存在,示例如下:
(if (fboundp 'blink-cursor-mode) (blink-cursor-mode 0)) (if (boundp 'coding-category-utf-8) (set-coding-priority '(coding-category-utf-8)))
你也可以直接忽略函数未定义时产生的错误:
(ignore-errors (set-face-background 'region "grey75"))
对不存在的变量使用
setq通常无害,因此这类语句不需要加条件。使用
use-package自动加载并配置包。(use-package hi-lock :defer t :init (add-hook 'some-hook 'hi-lock-mode) :config (use-package my-hi-lock) :bind (("M-o l" . highlight-lines-matching-regexp) ("M-o r" . highlight-regexp) ("M-o w" . highlight-phrase)))
这段配置会在
hi-lock的命令或变量首次被使用时延迟加载,绑定 3 个按键到其命令,并在加载hi-lock后额外加载my-hi-lock包(用于进一步定制hi-lock)。use-package功能有完整的官方手册,参见 use-package 用户手册。
51.4.3. 终端专用初始化
每种终端类型都可以对应一个 Lisp 库,当 Emacs 在该类型终端上运行时会自动加载这个库。对于名为 termtype 的终端类型,对应的库为 term / termtype 。(如果关联列表 term-file-aliases 中存在形如 (termtype . alias) 的条目,Emacs 会用 alias 代替 termtype。)该库会按照常规方式在 load-path 目录中搜索,并尝试后缀 .elc 和 .el 。通常它位于大多数 Emacs 库所在目录的 term 子目录下。
终端专用库的常见用途是:借助 input-decode-map ,将终端功能键发出的转义序列映射为更有意义的名称。可参考文件 term/lk201.el 了解具体实现方式。许多功能键会根据 Termcap 数据库中的信息自动映射;终端专用库只需负责映射那些 Termcap 未定义的功能键。
当终端类型名称中包含连字符时,在选择库名时 只取第一个连字符之前的部分 。因此,终端类型 'aaa-48' 和 'aaa-30-rv' 都会使用同一个库 term/aaa。库中的代码可以通过 (getenv "TERM") 获取完整的终端类型名称。
库名由变量 term-file-prefix 的值与终端类型名拼接而成。你可以在 .emacs 文件中将 term-file-prefix 设置为 nil ,从而禁止加载终端专用库。
Emacs 会在初始化的最后阶段运行钩子 tty-setup-hook ,此时 .emacs 文件与所有终端专用库都已加载完毕。如果你想覆盖部分终端专用库的设置,或是为没有对应库的终端定义初始化逻辑,可以向这个钩子添加函数。参见 Hooks(钩子)。
51.4.4. Emacs 查找初始化文件的方式
Emacs 通常会在你家目录下的某个位置查找初始化文件26。详见《Emacs 初始化文件》。
Emacs 按以下顺序查找初始化文件: ~/.emacs.el 、 ~/.emacs 或 ~/.emacs.d/init.el ,你可以任选其中一个文件名使用。(注意:只有直接位于家目录下的位置,其基本名才以点开头。)
Emacs 还会在 XDG 兼容路径 下查找 init.el ,默认目录为 ~/.config/emacs 。你可以通过设置环境变量 XDG_CONFIG_HOME 来覆盖该路径,它的值会替换掉默认 XDG 初始化文件路径中的 ~/.config 。但是,如果 ~/.emacs.d 、 ~/.emacs 或 ~/.emacs.el 存在,会优先使用这些路径。这意味着,你必须删除或重命名它们,才能使用 XDG 路径。
另外注意:如果 XDG 路径和 ~/.emacs.d 都不存在,Emacs 会 自动创建 ~/.emacs.d (并在后续启动时使用它)。
Emacs 会把 user-emacs-directory 设置为它最终决定使用的目录。该目录随后会被用来查找其他用户专属的 Emacs 文件,例如:
custom-file(见 保存自定义设置)- 保存的桌面会话(见 保存 Emacs 会话)
- 以及其他文件。
它还会被用来计算 native-comp-eln-load-path 的值(详见 Emacs Lisp 代码库),这是 Emacs 查找原生编译 Lisp 代码、以及存放异步编译包生成的新编译代码的地方。命令行选项 '--init-directory' (详见初始化选项)可以覆盖上述搜索用户初始化文件后确定的 user-emacs-directory 值。
由于 user-emacs-directory 一旦由 Emacs 确定后,启动流程的其余部分会用它来定位其他文件和目录,我们不建议在你的初始化文件中修改它的值,否则可能干扰或破坏 Emacs 会话的正常启动。
尽管这种做法能兼容旧版 Emacs,但现代 POSIX 平台更推荐将初始化文件放在 ~/.config 下,这样在排查因损坏的初始化文件导致的问题,或归档配置文件时,只需重命名该目录即可。
为了让旧版 Emacs 也能在当前默认位置找到配置文件,你可以执行下面这行 Emacs Lisp 代码:
(make-symbolic-link ".config/emacs" "~/.emacs.d")
但是,如果你从 su 启动的 shell 中运行 Emacs,且环境中未设置 XDG_CONFIG_HOME ,Emacs 会尝试查找你自己的初始化文件,而不是当前切换到的用户的文件。这样做的目的是:即使你以超级用户身份运行,也能使用你自己的编辑器配置。
更精确地说:Emacs 首先确定要使用哪个用户的初始化文件。它从环境变量 LOGNAME 和 USER 中获取你的用户名;如果两者都不存在,则使用有效用户 ID。如果该用户名与真实用户 ID 匹配,Emacs 就使用 HOME ;否则,它会在系统用户数据库中查找对应用户名的家目录。
为简洁起见,Emacs 其余文档通常只使用当前默认路径 ~/.emacs.d/init.el 来指代初始化文件。
51.4.5. 初始化文件中的非 ASCII 字符
如果你的初始化文件在字符串或按键绑定中包含非 ASCII 字符(如带重音的字母),语言与编码系统可能会引发问题。
若要在初始化文件中使用非 ASCII 字符,你应当在初始化文件的第一行添加 -*-coding: 编码系统-*- 标记,并指定一个支持对应字符的编码系统。参见 “识别编码系统”。这是因为,当 Emacs 读取初始化文件中使用这类字符串的部分时,用于解码非 ASCII 文本的默认设置可能尚未完成,可能导致 Emacs 错误解码这些字符串。之后应避免编写会以其他方式修改编码系统的 Emacs Lisp 代码,例如调用 set-language-environment 。
不直接使用非 ASCII 字符的一种替代方案是,使用《Emacs Lisp 参考手册》“通用转义语法” 一节中介绍的某种字符转义语法,这些语法可以仅用 ASCII 字符表示所有 Unicode 码点。
若要绑定非 ASCII 按键,必须使用向量形式(参见在初始化文件中重新绑定按键)。不能使用字符串形式,因为非 ASCII 字符会被解析为 Meta 键。示例:
(global-set-key [?字符] 'some-function)
先按下 C-q ,再按下你想要绑定的按键,即可插入对应的 字符 。
51.4.6. 早期初始化文件
Emacs 的绝大多数自定义配置都应当放在普通的初始化文件中。参见《Emacs 初始化文件》。但有时需要让某些自定义配置在 Emacs 启动过程中早于普通初始化文件加载时就生效,这类配置可以放在 早期初始化文件 中: ~/.config/emacs/early-init.el 或 ~/.emacs.d/early-init.el 。
该文件会在 包管理系统和图形界面(GUI)初始化之前 被加载,因此你可以在其中自定义影响包初始化流程的变量,例如: package-enable-at-startup 、 package-load-list 、 package-user-dir 。注意:像 package-archives 这类 只影响新包安装、不影响已安装包加载 的变量,可以在普通初始化文件中自定义。参见《包安装》。
不建议 把本来可以放在普通初始化文件中的配置移到 early-init.el 里。原因是:早期初始化文件在 GUI 初始化之前就读取,与 GUI 相关的自定义配置在 early-init.el 中 无法可靠生效 ;而普通初始化文件是在 GUI 初始化之后才加载的。如果你必须在早期初始化文件中使用依赖 GUI 功能的配置,应将它们放到 Emacs 启动提供的钩子中运行,例如 window-setup-hook 或 tty-setup-hook 。参见《钩子》。
关于早期初始化文件的更多信息,可参见《Emacs Lisp 参考手册》中的《初始化文件》章节。
51.5. 保存持久化认证信息
部分需要连接外部服务的 Emacs 包会要求身份认证(参见 “输入密码”),例如《Gnus 手册》中的 Gnus、《Tramp 手册》中的 Tramp 等。为避免反复输入相同的用户名和密码,Emacs 提供了 auth-source 库,用于持久化保存这类认证信息。
默认情况下,认证信息会从以下文件中读取: ~/.authinfo 、 ~/.authinfo.gpg 或 ~/.netrc 。这些文件的语法与 ftp 等工具使用的 netrc 文件类似,格式如下:
machine mymachine login myloginname password mypassword port myport
同样, auth-source 库支持多种存储后端,目前包括传统的 netrc 后端、JSON 文件、密钥服务 API(Secret Service API),以及类 UNIX 系统的标准密码管理器 pass。
所有这些存储方式都可以通过用户选项 auth-sources 进行自定义,详见 Emacs auth-source 相关文档。
当交互式输入的密码在已配置的后端中不存在时,部分后端会提供 持久化保存密码 的功能。你可以通过自定义用户选项 auth-source-save-behavior 来修改这一行为。
52. 退出与中止
C-gC-Break(仅 MS‑DOS)- 退出:取消正在运行或尚未输入完成的命令。
C-]- 退出最内层的递归编辑层级,并取消调用该层级的命令 (
abort-recursive-edit)。 ESC ESC ESC- 根据场景执行退出或中止操作 (
keyboard-escape-quit)。 M-x top-level- 中止当前所有正在执行的递归编辑层级。
C-/C-x uC-_- 取消之前对缓冲区内容所做的修改 (
undo)。
在命令执行完毕前取消命令有两种方式:使用 C-g 退出,以及使用 C-] 或 M-x top-level 中止。退出用于取消尚未输入完成或正在运行的命令。中止用于退出递归编辑层级,并取消调用该递归编辑的命令(见 “递归编辑层级”)。
使用 C-g 退出可以取消你不想要的半输入命令或数字参数。此外,若命令正在运行, C-g 可以相对安全地停止该命令。例如,如果你中止一个耗时较长的删除命令,你的文本要么仍 全部 保留在缓冲区,要么 全部 保存在删除环中,或两者都有。如果区域处于激活状态, C-g 会取消标记激活,除非 “临时标记模式(Transient Mark mode)” 已关闭(见 “禁用临时标记模式”)。如果你正在进行增量搜索, C-g 的行为会比较特殊;有时需要连续按两次 C-g 才能退出搜索。详见 “增量搜索”。
如果在小缓冲区中输入 C-g ,会退出打开该小缓冲区的命令并关闭它。如果该小缓冲区不是最近打开的(当 minibuffer-follows-selected-frame 为 nil 时可能出现,见 “使用小缓冲区”), C-g 在确认后会一并关闭更近打开的小缓冲区并退出相关命令。
在 MS‑DOS 上, C-Break 与 C-g 一样作为退出键使用。原因是在 MS‑DOS 中,命令运行时无法在与用户交互的间隙识别 C-g ,而 C-Break 则可以随时被识别。详见 “MS‑DOS 键盘使用”。
C-g 的工作原理是在按下时立即将变量 quit-flag 设为 t ;Emacs Lisp 会频繁检查该变量,若非 nil 则退出。只有当 Emacs 正等待输入时, C-g 才会作为命令实际执行,此时运行的是 keyboard-quit 。
在文本终端下,如果在第一个 C-g 被识别之前再次按下 C-g ,会激活紧急退出功能并返回 shell。详见 “紧急退出”。
有些情况下你无法退出。当 Emacs 正在等待操作系统完成某项操作时,除非 Emacs 为特定系统调用做了专门处理,否则无法退出。我们已经为用户通常希望退出的系统调用做了处理,但你仍可能遇到未处理的情况。一种非常常见的情况是:通过 NFS 等待文件输入或输出时,Emacs 本身支持退出,但很多 NFS 实现不允许用户程序在服务器挂起时停止等待。
使用 C-] (abort-recursive-edit) 中止用于退出递归编辑层级并取消调用它的命令。 C-g 无法做到这一点,因为它的作用是取消递归编辑内部尚未输入完成的命令。两种操作都很有用。例如,如果你在递归编辑中输入了 C-u 8 作为数字参数,可以用 C-g 取消该参数并继续留在递归编辑中。
ESC ESC ESC (keyboard-escape-quit) 序列既可退出也可中止。(我们这样定义是因为在很多 PC 程序中 ESC 表示 “退出”。)它可以像 C-g 一样取消前缀参数、清除选中区域、退出查询替换;也可以像 C-] 一样退出小缓冲区或递归编辑;还可以像 C-x 1 一样取消分屏,只保留一个窗口。但它不能停止正在运行的命令,因为它作为普通命令执行,只有等 Emacs 准备好接收下一条命令时才会生效。
M-x top-level 命令等价于连续多次按下 C-] ,一次性退出所有递归编辑层级;如果小缓冲区处于激活状态,也会退出。 C-] 一次只退出一层,而 top-level 一次性全部退出。与 C-g 不同, C-] 和 top-level 都和其他普通命令一样,只在 Emacs 准备接收命令时生效。 C-] 是一个普通按键,其功能仅来自按键映射中的绑定。详见 “递归编辑层级”。
严格来说, C-/ (undo) 并不是取消正在执行的命令,而是可以理解为 “取消已经执行完的命令”。关于撤销功能的更多信息,见 “撤销”。
53. 处理 Emacs 故障
本节介绍如何识别并处理 Emacs 未按预期运行的情况,例如 keyboard code mixups(键盘编码错乱)、garbled displays(显示乱码)、running out of memory(内存不足)、crashes and hang(崩溃与卡死)等。
如果你认为自己发现了 Emacs 中的错误,请参阅报告错误。
53.1. 递归编辑层级
Recursive editing levels 递归编辑层级 是 Emacs 中重要且实用的功能,但如果你不了解它,可能会误以为是程序出错。
如果模式行里显示主模式和次模式名称的括号外出现了方括号 '[…]' ,说明你进入了 递归编辑层级 。如果你并非有意进入,或者不明白这是什么意思,直接退出该层级即可。退出方法:输入 M-x top-level 。详见 “递归编辑层级”。
53.2. 屏幕乱码
如果文本终端上的文字显示异常,首先要检查缓冲区里的内容是否真的出错。按下 C-l (recenter-top-bottom) 重新刷新整个屏幕。如果刷新后显示正常,说明问题仅出在上一次的屏幕更新过程中。(否则,请参阅下一节。)
显示刷新问题通常是因为你所用终端的 terminfo 配置不正确。Emacs 发行包中的 etc/TERMS 文件提供了这类已知问题的修复方案。 INSTALL 文件里也有专门章节给出这类问题的通用解决建议。
如果你确认使用的是正确的 terminfo 配置,那么问题可能出在 terminfo 条目本身存在错误,或是 Emacs 中存在针对某些终端类型的 bug。
53.3. 文本乱码
如果按 C-l 刷新后文本依然异常,先按 C-h l (view-lossage) 查看你刚才输入了哪些命令,导致了当前结果。然后逐步使用 C-x u (undo) 回退操作,直到恢复到你认为正确的状态。
如果缓冲区开头或结尾有一大段文本看似丢失,检查模式行中是否出现 'Narrow' 字样。如果出现,说明你看不见的文本很可能仍然存在,只是暂时被限制显示。输入 C-x n w (widen) 即可重新显示全部内容。见 窄化显示
53.4. 内存不足
如果你收到错误提示 “Virtual memory exceeded”(超出虚拟内存),请使用 C-x s (save-some-buffers) 保存已修改的缓冲区。这种保存方式对额外内存的需求最小。Emacs 会保留一部分 备用内存 ,在出现该错误时启用;这部分内存通常足以让 C-x s 完成保存工作。当备用内存耗尽时,模式行开头会显示 '!MEM FULL!' ,表示已无备用内存。
保存好修改的缓冲区后,你可以退出当前 Emacs 会话并重新启动,或者使用 M-x kill-some-buffers 在当前 Emacs 进程中释放空间。如果释放的空间足够,Emacs 会重新补充内存储备,模式行上的 '!MEM FULL!' 也会消失,这意味着你可以安全地继续在同一个 Emacs 会话中编辑。
内存不足 时,不要使用 M-x buffer-menu 来保存或关闭缓冲区,因为缓冲区菜单本身需要相当多的内存,而备用内存可能不足以支撑它运行。
在 GNU/Linux 系统中,Emacs 通常不会收到内存不足的通知;相反,当内存耗尽时,操作系统可能会直接杀死 Emacs 进程。这个机制被称为 out-of-memory killer内存溢出杀手 (OOM killer)。在这种机制下,Emacs 无法及时感知内存不足,也就无法像上面描述的那样让你保存缓冲区。
不过,你可以关闭操作系统的这一行为,让 Emacs 在被杀死之前有机会以更合理的方式处理内存不足问题。方法如下:使用 root 权限编辑文件 /etc/sysctl.conf ,加入以下两行,然后在 shell 中执行 sysctl -p :
vm.overcommit_memory=2 vm.overcommit_ratio=0
请注意:以上设置会影响系统上的 所有进程 ,并会改变系统在内存压力下的整体行为,而不仅仅是 Emacs 进程。
53.5. Emacs 崩溃时
Emacs 本不应崩溃,但如果真的发生,它会在退出前生成一份崩溃报告。崩溃报告会输出到标准错误流。如果 Emacs 是在 GNU 或类 Unix 系统的图形桌面环境下启动的,标准错误流通常会被重定向到如 ~/.xsession-errors 之类的文件,你可以在那里查找崩溃报告。在 MS-Windows 上,崩溃报告除了输出到标准错误流外,还会写入 Emacs 进程当前目录下名为 emacs_backtrace.txt 的文件。
崩溃报告的格式取决于平台。在某些平台(例如使用 GNU C 库的系统)上,崩溃报告包含一份 回溯信息 (backtrace),描述崩溃前的程序执行状态,可用于协助调试崩溃原因。下面是 GNU 系统上的一个示例:
Fatal error 11: Segmentation fault Backtrace: emacs[0x5094e4] emacs[0x4ed3e6] emacs[0x4ed504] /lib64/libpthread.so.0[0x375220efe0] /lib64/libpthread.so.0(read+0xe)[0x375220e08e] emacs[0x509af6] emacs[0x5acc26] …
数字 '11' 是与崩溃对应的系统信号编号,本例中为段错误。十六进制数字是程序地址,可以通过调试工具与源代码行关联起来。例如,GDB 命令 'list *0x509af6' 可以显示与 'emacs[0x509af6]' 对应的源代码行。如果你的系统装有 addr2line 工具,可以使用下面的 shell 命令输出带源代码行号的回溯信息:
sed -n 's/.*\[\(.*\)]$/\1/p' backtrace |
addr2line -C -f -i -p -e bindir/emacs-binary
在 MS-Windows 上,回溯信息的格式略有不同,例如:
Backtrace: 00007ff61166a12e 00007ff611538be1 00007ff611559601 00007ff6116ce84a 00007ff9b7977ff0 …
因此不需要通过 sed 过滤,直接使用以下命令即可显示源代码行号:
addr2line -C -f -i -p -e bindir/emacs-binary < backtrace
这里的 backtrace 是存放回溯信息的文本文件名(在 MS-Windows 上即为 Emacs 启动目录下的 emacs_backtrace.txt ), bindir 是 Emacs 可执行文件所在目录, emacs-binary 是 Emacs 可执行文件名:在 GNU 和类 Unix 系统上通常是 emacs ,在 MS-Windows 和 MS-DOS 上是 emacs.exe 。如果你的 addr2line 版本较旧不支持 -p 选项,去掉该参数即可。
在支持核心转储文件的系统上,Emacs 还可以在崩溃时可选地生成 核心转储 (core dump)。核心转储是一个包含崩溃前程序完整运行状态的大容量文件,通常通过载入调试器(如 GDB)进行分析。在很多平台上,核心转储默认是关闭的,你必须显式启用,例如在 shell 中执行: ulimit -c unlimited (可以把这条命令加到 shell 启动脚本里。)
53.6. 崩溃后的恢复
如果 Emacs 或计算机崩溃,你可以通过 自动保存文件 恢复崩溃时正在编辑的文件。方法是:重新启动 Emacs,执行命令: M-x recover-session
该命令会先打开一个缓冲区,列出所有中断的会话文件及对应日期。你需要选择要恢复的会话(通常是最近一次),将光标移到该项,然后按 C-c C-c 。
之后, recover-session 会逐一处理该会话中你正在编辑的文件,询问是否恢复。若输入 y ,它会显示文件与自动保存文件的时间,并再次确认是否恢复。第二次必须确认 yes ,Emacs 才会从自动保存文件中恢复文本。
恢复完成后,你选择恢复的文件会出现在 Emacs 缓冲区中, 必须手动保存 ,才能真正更新磁盘上的文件。
作为最后手段:如果你有未关联到任何文件的缓冲区内容,或自动保存记录的内容不够新,你可以借助 GDB 调试器与脚本 etc/emacs-buffer.gdb ,从核心转储文件(core dump) 中提取内容 —— 前提是已保存核心转储,且 Emacs 可执行文件未去除调试符号。
获取核心转储后,立即将其重命名(如 core.emacs ),避免下次崩溃覆盖它。
使用该脚本的方法:用 GDB 加载 Emacs 程序与核心转储,例如: 'gdb /usr/bin/emacs core.emacs' 。在 GDB 提示符下加载恢复脚本: 'source /usr/src/emacs/etc/emacs-buffer.gdb' 。然后输入命令: ybuffer-list :查看可恢复的缓冲区列表及编号。 ysave-buffer :保存指定编号的缓冲区到文件。建议使用 不存在的文件名 保存;若文件已存在,脚本不会备份旧内容。
53.7. 紧急退出
在文本终端下,如果你在 Emacs 还没来得及响应第一个 C-g 时就按下第二个 C-g , emergency escape紧急退出 功能会立刻挂起 Emacs。这样无论 Emacs 卡死得多严重,你都能退出 GNU Emacs。正常情况下,Emacs 会极快地响应第一个 C-g ,第二个 C-g 不会触发紧急退出。但如果某些问题导致 Emacs 无法正常处理第一个 C-g ,第二个就能让你回到 shell。
当你从紧急退出造成的挂起中恢复 Emacs 时,它会提示已恢复,并在继续之前询问一两个问题:
Emacs is resuming after an emergency escape.
Auto-save? (y or n)
Abort (and dump core)? (y or n)
每个问题用 y 或 n 加 RET 回车回答。
- 对 'Auto-save?' 回答
y,会立即对所有开启了自动保存的已修改缓冲区执行自动保存;回答n则跳过。如果 Emacs 处于无法安全执行自动保存的状态,该问题会被省略。 - 对 'Abort (and dump core)?' 回答
y,会让 Emacs 崩溃并生成核心转储文件。这是为了让高手能分析 Emacs 一开始无法退出的原因。生成核心转储后程序不会继续运行。
如果对这个问题回答 n ,Emacs 会继续执行。运气好的话,它最终会执行你之前请求的退出操作。如果不行,之后每一次连续按 C-g 都会再次触发紧急退出。
如果 Emacs 并没有真卡死,只是慢,你可能会不小心触发双击 C-g 。这时只需恢复运行并对两个问题都回答 n ,就能回到之前的状态,你之前请求的退出操作会慢慢执行。
紧急退出仅在文本终端下有效 。在图形界面下,你可以用鼠标关闭 Emacs 或切换到其他程序。
在 MS-DOS 系统中,需要连续按 C-Break (两次)才能触发紧急退出;但在某些情况下(如系统调用卡死、或 Emacs 在 C 代码的紧凑循环中卡住),该操作可能无效。
53.8. 当 DEL 键无法删除时
每个键盘都有一个大键,通常标为 BACKSPACE ,一般用来删除刚输入的最后一个字符。在 Emacs 中,这个键理应等价于 DEL 。
在图形界面下启动 Emacs 时,它会自动判断哪个键应该是 DEL 。在某些特殊情况下,Emacs 从系统获取到错误信息,导致 BACKSPACE 变成向前删除 ,而不是向后删除。
有些键盘还有 Delete 键,通常用于向前删除。如果这个键在 Emacs 里变成向后删除,也说明 Emacs 获取了错误信息 —— 只是方向相反。
在文本终端下,如果你发现按 BACKSPACE 会像 C-h 一样弹出帮助,而不是删除字符,说明该键实际发送的是 'BS' 字符。Emacs 本应把 'BS' 当作 DEL 处理,但没有生效。
所有这类情况,临时解决方法都一样:执行命令 M-x normal-erase-is-backspace-mode 。它会在 Emacs 处理 DEL 的两种模式之间切换。如果启动时模式不对,执行一次通常就能切到正确模式。在文本终端下,如果 BS 已经被当作 DEL 使用,你想查看帮助时请用 F1 代替 C-h ; C-? 通常也可以(如果它发送的是编码 127 )。
要让所有 Emacs 会话都永久修复 ,把下面其中一行写到你的初始化文件里(见 Emacs 初始化文件)。对于 BACKSPACE 向前删的情况,用这行让 BACKSPACE 等价于 DEL :
(normal-erase-is-backspace-mode 0)
对于另外两种问题,用这行:
(normal-erase-is-backspace-mode 1)
另一种永久修复方法是自定义变量 normal-erase-is-backspace :值为 t : BS / BACKSPACE 作为 DEL ;值为 nil :使用另一种模式。参见「简易自定义界面」。
54. 报告漏洞
如果你认为在 Emacs 中发现了错误,请进行报告。我们无法保证一定会修复,也不一定会认同这是一个错误,但我们非常希望收到你的反馈。你希望新增的功能建议也同样适用本节内容。本章将帮助你判断是否真的发现了错误,并在确认后构建一份有效的错误报告。
当你发现可能是错误的问题时,通用流程如下:
- 先确认你发现的问题是否为已知问题,或是已经被报告 / 修复的错误。参见「查阅现有错误报告与已知问题」,其中会说明如何查找已知问题和错误。
- 如果你不确定当前表现是否属于错误,参见「何时算作错误」,其中说明了我们认定的 Emacs 明确错误类型。
- 当你确定发现了错误后,参见「理解错误报告」,它会帮助你以最高效的方式描述问题,让我们更容易复现与排查。
- 接下来,参见「错误报告清单」,其中详细说明了如何提交错误报告以及需要包含哪些信息。简单来说,你可以通过 Emacs 命令
report-emacs-bug以电子邮件方式提交错误报告,该命令会辅助你完成整个流程。提交错误报告会启动错误调查与修复流程,你会收到相关讨论邮件的副本,我们可能会请你提供更多信息、测试可能的修复方案等。 - 如果你认为发现了需要保密沟通的安全问题,请直接联系 GNU Emacs 维护者。联系方式可在 Emacs 发行版中的
admin/MAINTAINERS文件中查看。 - 最后,如果你希望为 Emacs 提交具体修改(无论是修复错误、新增功能还是改进文档),详情请参见「为 GNU Emacs 提交补丁」。
- 查看现有漏洞报告与已知问题
- 漏洞的判定标准
- 理解漏洞报告
- 漏洞报告检查清单
- 提交 GNU Emacs 补丁
54.1. 查看现有漏洞报告与已知问题
在报告错误之前,请尽可能先检查该问题是否已被我们知晓。事实上,它可能已经在 Emacs 的后续发行版或开发版中被修复。下面是你可以查阅已知问题的主要渠道:
etc/PROBLEMS文件:按C-h C-p即可阅读。该文件列出了在编译、安装与运行 Emacs 时常见的典型问题,重点收录那些由其他软件导致、难以在 Emacs 内部直接修复的问题。其中通常会提供变通方案与解决建议。GNU 缺陷跟踪系统:地址为 https://debbugs.gnu.org。Emacs 相关的缺陷与问题都归类在 emacs 软件包下。该系统会记录每个缺陷的状态、初始报告、缺陷提交者与 Emacs 开发者后续的讨论和修复信息。你可以按标题、严重程度等条件搜索缺陷。如需更复杂的搜索条件,可使用 https://debbugs.gnu.org/cgi/search.cgi。
除了网页浏览,你也可以在 Emacs 内部使用 debbugs 包浏览缺陷(可通过包菜单安装,参见 Emacs Lisp 包)。该包提供命令:
M-x debbugs-gnu:列出缺陷、M-x debbugs-gnu-search:搜索特定缺陷、M-x debbugs-gnu-usertags:查看由 Emacs 维护者添加的用户标签。‘bug-gnu-emacs’ 邮件列表(同时作为新闻组 gnu.emacs.bug):存档地址为 https://lists.gnu.org/mailman/listinfo/bug-gnu-emacs。该列表是发送到缺陷跟踪系统的 Emacs 错误报告与后续消息的镜像,同时还包含 2008 年初缺陷跟踪系统启用之前的旧报告。
你可以订阅该列表。请注意,其用途是向 Emacs 维护者提供缺陷与功能需求信息,因此报告可能包含大量内容,围观者请勿对此抱怨。
- ‘emacs-pretest-bug’ 邮件列表:该列表已不再使用,仅作历史参考。它曾用于接收 Emacs 开发版(未正式发布)的错误报告。2003 年至 2007 年中的存档可在 https://lists.gnu.org/r/emacs-pretest-bug/ 查看。现在发送到该列表的邮件会自动转发到 ‘bug-gnu-emacs’。
- ‘emacs-devel’ 邮件列表:有时有人会在此报告错误,但这并非该列表的主要用途。提交错误报告最好还是发到专用的缺陷列表。你在报告错误前 无需 阅读此列表。
54.2. 漏洞的判定标准
如果 Emacs 访问了非法内存地址(又称 “段错误”),或因程序内部问题导致系统错误退出(而非 “磁盘已满” 这类外部问题),那么这 肯定是一个错误 。
如果 Emacs 的显示内容与缓冲区实际内容不一致,这属于错误。但你应先确认是否是 缓冲区缩窄 等功能导致(参见缩窄),这类功能会隐藏缓冲区部分内容或改变显示方式。
某个命令执行时间过长、迟迟无法完成, 可能 是错误,但你必须确认这确实是 Emacs 的问题。有些命令本身就需要较长时间。按下 C-g (MS‑DOS 下为 C-Break ),然后按 C-h l 查看 Emacs 接收到的输入是否符合你的意图;如果输入明显应该被快速处理,可提交错误报告。如果你不确定该命令是否本就耗时,可查阅手册或寻求帮助确认。
如果你熟悉的某个命令,在其正常逻辑本应合理生效的场景下却抛出 Emacs 错误信息,这 很可能是一个错误 。
如果一个命令执行了错误的操作,这是错误。但请务必先确认它 本应 做什么。如果你不熟悉该命令,它可能实际上运行正常。如有疑问,查阅该命令的文档(参见 “按命令或变量名查看帮助”)。
一个命令的设计逻辑,可能并非编辑操作的最优方案。这是一类很重要的问题,但也属于 主观判断 。而且,很可能因为你不了解某些现有功能而得出这种结论。建议你在通过常规方式查阅文档、确信已理解其用法、并确定你想要的功能确实不存在之后,再对此类问题提出反馈。也可以咨询其他 Emacs 用户。如果仔细阅读手册后仍不理解命令的用途,可检查索引和术语表中是否有不明确的术语。
如果重新认真阅读手册后,你仍然不理解命令应该如何使用,这说明 手册本身存在错误 ,你应当报告。手册的作用就是让非 Emacs 专家(包括你)清晰理解所有内容。报告文档错误与报告程序错误同等重要。
如果函数或变量的内置文档与手册内容不一致,则其中必有一个错误,这属于 bug。
对于 非 Emacs 自带 的扩展包问题,最好先向该包的开发者报告。
54.3. 理解漏洞报告
当你确定存在错误时,进行报告非常重要,并且要以有用的方式来报告。最有价值的内容是: 精确描述你输入的每一条命令 —— 从启动 Emacs 的 Shell 命令开始,直到问题出现为止,以及执行这些命令后产生的结果。
报告错误最重要的原则是:只 陈述事实 。假设和文字描述无法替代详细的原始数据。陈述事实本应很直接,但很多人却倾向于给出自己的推测,并用推测代替事实。如果这些解释是基于对 Emacs 实现方式的猜测,它们可能毫无用处;与此同时,因为缺少事实,我们将无法获得关于该错误的真实信息。如果你确实想调试问题,并报告并非猜测的可靠分析,这很有用 —— 但请 同时附上原始事实 。
举个例子:假设你输入 C-x C-f /glorp/baz.ugh RET 打开一个你知道体积很大的文件,而 Emacs 显示了「I feel pretty today」。错误报告需要包含 全部这些信息 。你不应该想当然地认为问题是由文件大小导致,并只说:“我打开了一个大文件,Emacs 显示了‘I feel pretty today’。” 这就是我们所说的 “猜测式解释” 。问题的根源可能只是文件名里有一个字母 ‘z’。如果是这样,我们收到你的报告后,会用某个没有字母 ‘z’ 的大文件去测试,结果无法复现问题。我们根本不可能猜到,应该去打开一个文件名里带 ‘z’ 的文件。
你甚至不应该用 “打开文件” 来代替 C-x C-f 。因为打开文件的方式不止一种,无法确定所有方式都能触发该问题。同样,与其说 “如果一行里有三个字符”,不如直接写 “输入 RET A B C RET C-p 之后”—— 也就是说, 请告诉我们你是如何输入文本的 ,即你那边能触发问题的具体操作。
如果可能,请尽量用 emacs -Q 启动 Emacs(这样 Emacs 会在无任何自定义配置的环境下启动;参见初始选项),并重复触发错误的步骤,快速尝试复现问题。如果这样能复现错误,就可以排除你个人配置导致的问题,让错误更容易被复现。此时你的错误报告应首先说明:你是用 emacs -Q 启动的 Emacs,然后附上 精确的复现步骤 。如果可以,请提供复现错误所需文件的 完整内容 。
有些错误无法在 emacs -Q 下复现;有些则完全难以复现。这种情况下,你仍应报告你所掌握的信息 —— 但和之前一样,请 只描述你第一次触发错误时的原始操作事实 。
如果你有多个问题需要报告,请 为每个问题单独提交一份错误报告 。
54.4. 漏洞报告检查清单
在报告错误之前,请先确认该问题是否已被他人报告(参见查阅现有错误报告与已知问题)。
如果条件允许,可以尝试使用最新版 Emacs,查看问题是否已被修复。更好的做法是尝试最新的开发版。我们理解这对部分用户来说并不容易,因此 并非必须 在报告前做这一步。
编写 Emacs 错误报告的最佳方式是使用命令 M-x report-emacs-bug 。该命令会创建一个邮件缓冲区(参见发送邮件),并自动插入部分关键信息。但它无法提供所有必需内容,你仍需阅读并遵守下面的指南,在发送邮件前手动补充其他重要信息。你可能觉得 M-x report-emacs-bug 插入的某些信息无关紧要,但除非你非常确定,否则最好保留,由开发者自行判断是否有用。
写完报告后,按 C-c C-c 即可发送给 Emacs 维护者,邮件地址为 bug-gnu-emacs。如果你无法在 Emacs 内部发送邮件,可以将报告内容复制到你常用的邮件客户端(如果系统支持,可按 C-c M-i 让 Emacs 自动帮你复制),然后发送到该地址。你也可以直接写邮件到该地址,描述问题并包含下面提到的必要信息。
如果你想向 Emacs 提交代码(修复问题或实现新功能),最简单的方式是向 Emacs 问题跟踪器发送补丁。可使用命令 M-x submit-emacs-patch ,用法与报告错误类似;详见为 GNU Emacs 提交补丁。
无论哪种方式,你的报告都会发送到 ‘bug-gnu-emacs’ 邮件列表,并保存到 GNU 缺陷跟踪系统 https://debbugs.gnu.org。请提供有效的回复邮箱地址,以便我们需要向你询问更多信息。提交内容需要审核,因此你的报告可能会延迟一段时间才出现在跟踪系统中。
报告错误不需要了解 GNU 缺陷跟踪系统的工作方式,但如果你愿意,可以阅读其在线文档,了解可用的各类功能。
所有发送到 ‘bug-gnu-emacs’ 邮件列表的邮件都会同步到新闻组 ‘gnu.emacs.bug’,反之亦然。但我们 不建议 你通过新闻组发布错误报告(或回复)。这会让我们在需要补充信息时很难联系到你,并且与缺陷跟踪系统的集成效果不佳。
如果你的数据超过 500,000 字节,请不要直接放在错误报告里;可以说明可按需提供,或放到网上并给出地址。大型附件最好压缩后发送。
GNU 缺陷跟踪系统会为你的报告分配一个错误编号;后续讨论时请使用该编号,并把错误地址保留在收件人列表中,以便跟踪器记录讨论内容。错误地址格式类似 ‘[email protected]’,其中 nnnnn 是错误编号。
为了让维护者能够排查错误,你的报告应包含以下所有内容:
描述你观察到的、你认为不正确的行为。例如:“Emacs 进程收到致命信号”,或 “最终文本如下,我认为这是错误的”。
当然,如果错误是 Emacs 直接崩溃,那很明显。但如果是文本错误,维护者可能看不出问题所在。不要把这交给运气。
即使你遇到的问题是崩溃,也请 明确说明 。可能存在一些特殊情况,例如你的源码不同步,或遇到了系统 C 库的错误。(这种情况真实发生过!)你的版本会崩溃,我们这边可能不会。如果你明确说明会崩溃,那么当我们这里不崩溃时,就知道错误没有复现。如果你不说,我们就无法判断错误是否出现 —— 无法从观察中得出结论。
通常,对行为和复现步骤的描述需要包含以下一个或多个方面:
- 复现错误所需的任何文件的完整内容。如果你能给出不依赖任何文件就能复现问题的方法,也请提供。这会极大方便调试。如果确实需要文件,请确保我们能看到 精确内容 。例如,行尾是否有空格、缓冲区最后一行是否有换行符都可能有影响(理论上程序不应该关心最后一行是否结束,但错误往往不按理论来)。
我们需要输入的精确命令以复现错误。如果可能,给出在使用 -Q 参数启动的 Emacs 下的完整复现步骤(参见初始选项)。这会跳过你的个人配置。
精确记录 Emacs 输入的一种方法是使用操作记录文件(dribble file)。使用命令
M-x open-dribble-file开始记录。之后 Emacs 会把你所有的输入复制到指定文件,直到 Emacs 进程退出。注意:敏感信息(如密码)可能会被记录在该文件中。- 如果错误是 Emacs 手册或 Emacs Lisp 参考手册与实际行为不符,或文字令人困惑,请复制你认为有问题的手册内容。如果章节不长,给出章节名即可。
如果错误表现为 Emacs 错误信息, 精确抄写错误信息原文 ,以及显示 Lisp 程序如何触发该错误的 调用栈(backtrace) 非常重要。
为了准确获取错误信息,从
*Messages*缓冲区复制到错误报告中。复制全部内容,不要只复制一部分。- 检查你加载到 Lisp 环境中的任何程序(包括初始化文件)是否设置了可能影响 Emacs 行为的变量。同时,查看在不加载配置文件的全新启动的 Emacs 中是否会出现问题(使用
-Q参数启动以避免加载配置文件)。如果问题在纯净环境中不出现,你 必须 提供触发问题所需加载的 所有程序的精确内容 。 - 如果问题确实依赖于非 Emacs 标准的配置文件或其他 Lisp 程序,你应先向其维护者反馈,确认不是这些程序自身的错误。在他们确认是正确使用 Emacs 却出现问题后,再由他们报告错误。
- 如果你想提到 GNU Emacs 源码中的某处内容,请附上该行代码及前后几行上下文。不要只给行号。开发版源码的行号与你使用的版本并不一致。维护者需要额外工作量才能确定你指定行号的代码,而且无法保证准确。
对于文本模式终端上可能出现的显示错误:终端类型(环境变量
TERM的值),该终端在/etc/termcap中的完整 termcap 条目(该文件在不同机器上并不完全相同),Emacs 实际发送给终端的输出内容。收集终端输出的方法是:启动 Emacs 后立即执行命令 M-x open-termscript,它会让你输入一个文件名,之后 Emacs 会把所有终端输出记录到该文件,直到进程退出。如果问题在 Emacs 启动时就出现,可以把下面的 Lisp 表达式加入你的 Emacs 初始化文件:
(open-termscript "~/termscript")这样在 Emacs 第一次显示屏幕时就会开始记录。
注意:如果没有对应类型的终端,修复依赖终端的错误通常非常困难,有时甚至无法修复。
- Emacs 版本号。没有它,我们无法判断在当前版本中检查该错误是否有意义。
M-x report-emacs-bug会自动包含该信息;如果你不使用该命令,可以用M-x emacs-version RET获取版本号。如果该命令无效,你使用的很可能不是 GNU Emacs,应去别处报告错误。 - 你使用的机器类型、操作系统名称与版本号(同样由
M-x report-emacs-bug自动包含)。M-x emacs-version RET也会提供这些信息。从*Messages*缓冲区复制完整输出以保证准确,或使用C-u M-x emacs-version RET将版本信息直接插入当前缓冲区。 - 编译 Emacs 时传给
configure命令的命令行参数(由M-x report-emacs-bug自动包含)。 你对 Emacs 源码所做任何修改的完整列表。(除非错误在未修改的 Emacs 中也出现,否则我们可能没有时间排查。但如果你做了修改却不告知,只会误导我们。)
请 精确描述 这些修改。只用文字描述不够 —— 请提供统一上下文差异(unified context diff)。
添加自有文件或移植到其他机器也属于源码修改。
- 与标准 GNU Emacs 安装流程不一致的任何细节。
如果与非 ASCII 文本或国际化相关,需提供启动 Emacs 时生效的区域设置(locale)。
M-x report-emacs-bug会自动包含;在 GNU/Linux、Unix 或 POSIX 风格 shell(如 Bash)下,也可以用以下命令查看:echo LC_ALL=$LC_ALL LC_COLLATE=$LC_COLLATE LC_CTYPE=$LC_CTYPE \ LC_MESSAGES=$LC_MESSAGES LC_TIME=$LC_TIME LANG=$LANG
也可以使用 locale 命令(如果系统有)查看区域设置。
以下内容在错误报告中 不是必需 的:
对错误 “外围情况” 的描述 —— 对可复现错误来说没有必要。
很多人遇到错误后会花大量时间研究修改输入文件的哪些内容会让错误消失或不影响。
这通常耗时且用处不大,因为我们排查错误的方式是在调试器中运行单个示例并打断点,而不是从一堆示例中纯推理。你可以节省时间,不必寻找更多示例,直接提交报告,回去继续编辑,然后发现下一个错误。
当然,如果你能找到更简单的复现示例来代替原始示例,会更方便。输出错误更容易观察,调试耗时更短等。
但简化并非必须;如果你做不到或没时间,直接用原始用例报告即可。
核心转储文件(core dump)。
调试 core dump 可能有用,但只能在你的机器上、用你的 Emacs 可执行文件进行。因此发送 core dump 给维护者没有用。 绝对不要 把 core 文件放在邮件报告里!这种超大邮件会带来极大麻烦。
Emacs 运行的系统调用跟踪日志。
系统调用跟踪只对少数特殊调试有用,大多数情况下信息量很少。但奇怪的是,很多人似乎认为报告崩溃就应该发系统调用跟踪。这可能是调试无源码或无调试符号程序时养成的习惯。
对大多数程序来说,调用栈(backtrace)远比系统调用跟踪有用得多。即使在 Emacs 中,简单的调用栈通常也更有用,当然你可以配合变量值和用 pr 打印的 Lisp 对象来补充完整信息。
错误的补丁。
一份高质量的错误修复补丁是很有用的。但不要以为只提供补丁就足够,从而省略错误报告所需的其他信息(例如测试用例)。我们可能会发现你补丁中存在的问题,并决定用其他方式修复,也可能完全无法理解你的补丁。如果我们不明白你想要修复的是什么错误,或者你的补丁为何能起到改进作用,我们就不能合入它。
有关如何让我们更容易理解并合入你的补丁的指南,请参见为 GNU Emacs 提交补丁。
对错误原因或依赖条件的猜测。
这类猜测通常是错的。即使是专家,不先用调试器确认事实也无法猜对。
如果你愿意调试 Emacs 并提供更多错误信息,以下建议会很有用:
如果错误表现为错误信息,尝试为错误提供 Lisp 调用栈。在错误发生前使用
M-x toggle-debug-on-error(即先执行该命令,再复现错误)。这会让错误触发 Lisp 调试器并显示调用栈。把调试器的调用栈内容复制到错误报告中。(在触发错误前加载相关 Lisp 的*.el源文件,调用栈会更详细,如果你知道如何查找和加载这些文件,可以这样做。)调试错误建议使用 Edebug。详见 Emacs Lisp 参考手册中的 Edebug 章节。
只有在你知道如何复现错误时,才能使用调试器。如果无法复现,至少复制完整的错误信息。
如果 Emacs 似乎陷入无限循环或极长操作,在变量
debug-on-quit不为nil时按C-g,会启动 Lisp 调试器并显示调用栈。该调用栈对调试此类长循环很有用,如果你能生成,请复制到报告中。如果 Emacs 不响应
C-g(例如inhibit-quit被设置),可以从外部发送debug-on-event指定的信号(默认为 SIGUSR2)让其进入调试器。来自 GDB 等 C 调试器的额外信息可能帮助他人在没有对应机器的情况下定位问题。如果你不会用 GDB,请阅读 GDB 手册 —— 篇幅不长,使用简单。你可以在与 Emacs 相同的站点找到 GDB 发行版及在线手册。在 GDB 中运行 Emacs,应切换到编译 Emacs 的 src 子目录,然后执行
gdb ./emacs。保持当前目录为 src 很重要,这样 GDB 才会读取该目录下的.gdbinit文件。(你也可以在 GDB 内用source ./.gdbinit加载。)但收集额外信息时要思考,这些信息是否能说明错误原因。
例如,很多人只发送 C 层调用栈,这本身用处不大。带参数的简单调用栈通常无法说明 GNU Emacs 内部发生了什么,因为调用栈中的大多数参数都是指向 Lisp 对象的指针。这些指针的数值没有任何意义,重要的是它们指向的对象内容(而大部分内容本身也是指针)。
为了提供有用信息,你需要 以 Lisp 语法显示 Lisp 对象的值 。对栈底部附近几个栈帧中的每个 Lisp 对象变量都这样做。查看源码以确定哪些变量是 Lisp 对象,因为调试器会把它们当作整数。
要以 Lisp 语法显示变量值,先打印其数值,然后使用 GDB 自定义命令 pr 以 Lisp 语法打印对象。(如果必须使用其他调试器,调用函数 debug_print 并将对象作为参数。)pr 命令由
.gdbinit定义,且只在调试运行中的进程时有效(不适用于 core dump)。为了让 Lisp 错误中断 Emacs 并返回 GDB,在
Fsignal处设置断点。要查看正在运行的 Lisp 函数调用栈,使用 GDB 命令
xbacktrace。文件
.gdbinit还定义了其他几个用于查看 Lisp 对象类型和内容的命令,名称以 ‘x’ 开头。这些命令比pr层级更低、使用更不便,但在pr无法工作时(如调试 core dump 或 Emacs 收到致命信号时)仍可使用。Emacs 发行版中的
etc/DEBUG文件提供了更详细的调试建议与技巧。该文件还包含排查 Emacs 无响应问题的说明(很多人以为 Emacs “hung卡死”,但实际上可能只是进入了无限循环)。可以使用变量
data-directory中保存的路径,找到你 Emacs 安装中的etc/DEBUG文件。
54.5. 提交 GNU Emacs 补丁
如果你愿意为 GNU Emacs 编写错误修复或功能改进,这将非常有帮助。在提交修改时,请遵守以下指南,以便维护者能够轻松处理。如果你不遵守这些指南,你的信息可能仍然有用,但处理起来会花费额外精力。维护 GNU Emacs 本身已是繁重工作,只有你尽力配合,我们才能及时处理所有贡献。
每份补丁在我们正式评估前,都必须包含以下几部分信息,具体说明如下:
准备好所有内容后,请使用命令 M-x submit-emacs-patch 发送补丁。该命令会提示你输入补丁的主题和补丁文件,然后创建并打开一个 Message 模式缓冲区,将补丁文件作为附件,并允许你补充更多说明以及下面要求的其他信息。完成后按 C-c C-c ,即可通过电子邮件将补丁发送给开发者。补丁会被提交到 GNU 缺陷跟踪系统 https://debbugs.gnu.org,系统会像处理错误报告一样为你的提交分配编号。开发者通常会进行回复,可能会向你询问更多细节或补充信息,因此请务必提供有效的回复邮箱地址。
以下是我们要求你在提交补丁时提供的内容:
- 说明你要修复的问题,或补丁带来的改进
- 如果是修复已有错误,最好在 ‘bug-gnu-emacs’ 邮件列表的相关讨论下回复,或在 GNU 缺陷跟踪系统的对应条目下回复,并解释你的修改为何能修复该错误。
- 如果是新增功能,请包含功能描述与你的实现说明。
- 如果是新发现的错误,请为你修复的问题提供一份完整的错误报告(参见「错误报告清单」)。在合入修改前,我们需要确认其正确性。即使修改本身正确,如果没有办法复现它要修复的问题,我们也可能难以理解。
- 在代码修改中加入恰当的注释,帮助日后阅读源码的人理解为什么需要这项修改。
不要把不同目的的修改混在一起,请分开单独提交。
如果你出于两个不同原因做了两项修改,我们可能不会同时合入两者,可能只合入其中一个,或是在不同版本中分别合入。如果你把所有修改混在同一组差异文件里,我们就需要额外工作来拆分,判断哪些部分对应哪个目的。如果我们没有时间处理,可能会长期推迟合入你的补丁。
如果你每项修改完成后就立即提交,并附带独立说明,那么两项修改就不会纠缠在一起,我们也可以分别正常评估,无需额外拆分。
每项修改完成后请立即提交。有些人以为把大量修改积攒到一起提交是在帮我们,但如上所述,这其实是最糟糕的做法。
既然你应该分开提交每项修改,不妨完成后立刻发送。如果修改很重要,我们可以选择立即合入。
- 补丁本身,可以通过以下方式生成:
- 如果你使用 Emacs 代码仓库,请先确保本地副本是最新的(例如用
git pull)。你可以将修改提交到私有分支,然后用git format-patch master从主干版本生成补丁。(这是 推荐方式 ,会让我们更方便地应用补丁。)你也可以不提交修改,直接使用下面介绍的git diff。 使用
diff -u生成差异文件。如果使用 GNU diff,对 C 代码生成差异时请用:diff -u -F'^[_a-zA-Z0-9$]\+ *('。这会显示每处修改所在的函数名。生成差异时,避免新旧版本混淆。请将旧版本作为 diff 的第一个参数,新版本作为第二个参数。并在文件名中明确区分哪个是旧版本,哪个是你修改后的新版本。
- 如果你使用 Emacs 代码仓库,请先确保本地副本是最新的(例如用
为你的修改编写提交日志。这既能节省我们额外编写的工作量,也能帮助解释你的修改,方便我们理解。
提交日志的目的是说明修改的理由、代码如何解决对应问题,并让别人能清楚看到修改内容。因此你需要 具体说明修改了哪些函数、为什么修改 。关于我们对规范、清晰的提交日志的格式与要求,详见 Emacs 源码树中
CONTRIBUTE文件的「Commit messages」部分。你也可以参考近期提交的提交日志范例,了解需要包含哪些信息以及我们使用的风格。注意:与其他一些项目不同, 我们要求文档(即 Texinfo 文件)也必须提供提交日志 。参见变更日志,亦可参阅网址:https://www.gnu.org/prep/standards/html_node/Change-Log-Concepts.html,以及《GNU 编码标准》中的变更日志相关理念章节。
编写修复代码时,请记住:我们不能合入会破坏其他系统的修改。请考虑你的修改在其他类型系统上编译或运行时可能产生的影响。
有时人们提交的修复总体上可能是改进,但很难完全确定。合入这类修改成本很高,因为我们必须非常仔细地审查。当然,一份充分说明修改合理性的解释能帮助我们信服。
最安全的修改是只针对某台机器或某个系统使用的文件或代码片段。它们是安全的,因为不会在其他机器或系统上引入新错误。
请将补丁设计为 明显安全可合入 的形式,帮助我们分担工作量。
55. 为 Emacs 开发做贡献
Emacs 是一个协作型项目,我们鼓励所有人参与贡献。
你可以通过多种方式为 Emacs 贡献力量:
- 发现并报告错误;详见《报告错误》。
- 在 Emacs 用户邮件列表 https://lists.gnu.org/mailman/listinfo/help-gnu-emacs 解答问题。
- 编写文档,既可在 Wiki 上,也可在 Emacs 源码仓库中贡献(详见《为 GNU Emacs 提交补丁》)。
- 检查已有错误报告是否在新版 Emacs 中已修复:https://debbugs.gnu.org/cgi/pkgreport.cgi?which=pkg&data=emacs。
- 修复已有的错误报告。
- 实现 Emacs 发行版中
etc/TODO文件里列出的功能,并提交补丁。 - 实现新功能,并提交补丁。
- 开发可与 Emacs 配合使用的扩展包,并自行发布或上传到 GNU ELPA(https://elpa.gnu.org/)。
- 将 Emacs 移植到新平台,不过如今这种情况已不常见。
如果你想参与改进 Emacs,请通过 emacs-devel 邮件列表联系维护者。你可以询问推荐的项目,也可以提出自己的想法。
如果你有功能需求或改进建议,最佳提交渠道是 bug-gnu-emacs。请尽可能清晰地说明你希望看到的改动,以及你认为它能如何改进 Emacs、为什么值得改进。
如果你已经写出了改进代码,请告知我们。如果你还未开始工作,建议在动手前先联系 emacs-devel;我们可以给出建议,让你的扩展能更好地融入 Emacs 整体。
实现功能时,请遵循 Emacs 编码规范;详见《编码规范》。此外,较大规模的贡献需要向 FSF 转让版权;详见《版权转让》。
Emacs 的开发版可以从源码仓库下载,那里有一群开发者在积极维护。访问详情请见 Emacs 项目页面:https://savannah.gnu.org/projects/emacs/。
基于当前最新开发版编写补丁非常重要。如果你从旧版本开始修改,补丁可能会过时(导致维护者难以合入),或者 Emacs 本身的改动已让你的补丁不再必要。下载源码仓库后,请阅读 INSTALL.REPO 文件了解编译说明(它与普通版本的编译方式有一定区别)。
如果你希望做更全面的贡献,可以查看 Emacs 源码树中的 CONTRIBUTE 文件,了解如何成为一名 Emacs 开发者。该文件随每一个正式发布的 Emacs 源码压缩包一同分发,也可在 Emacs 在线源码仓库中找到。如果你按 https://savannah.gnu.org/projects/emacs/ 中的说明克隆了 Emacs 仓库,可在源码树顶层目录找到该文件。
如需查阅 Emacs 相关文档(以理解如何实现你想要的改动),可参考:
55.1. 编码标准
贡献的代码应遵循 GNU 编码规范。该手册可在网上查阅,地址为:https://www.gnu.org/prep/standards/。你的系统中也可能在 Info 文档里提供了本地版本,详见《GNU 编码规范》。
若代码未遵循该规范,我们需要安排人员对其进行修正后,方可纳入使用。
Emacs 另有额外的样式与编码约定,具体包括:
- 《Emacs Lisp 参考手册》中 “技巧与约定” 附录,地址为:https://www.gnu.org/software/emacs/manual/html_node/elisp/Tips.html。
- 拟纳入 Emacs 的 Lisp 代码,应避免使用
advice-add或with-eval-after-load函数。 - 移除所有源码文件和文本文件中的尾随空白字符。
- 在 Lisp 代码中,使用
?\s表示空格字符,而非直接使用?。
55.2. 版权转让
FSF(自由软件基金会)是 GNU Emacs 的版权持有者。FSF 是一家非营利组织,其全球使命是推动计算机用户自由,维护所有自由软件用户的权利。通用信息详见其官网:https://www.fsf.org/。
一般而言,对于对 GNU Emacs 及 GNU ELPA 中托管的软件包做出的 非 trivial(非微小)贡献 ,我们要求将版权转让给 FSF。相关背景原因见:https://www.gnu.org/licenses/why-assign.html。
版权转让流程十分简便,许多国家的居民可以 全程在线完成 。如需开始办理,请按照 Emacs 发行包中 etc/copyright-assign.txt 文件里的说明操作。你可以通过 [email protected] 邮件列表咨询任何相关问题(我们会为你解答或指引至能解答的人员)。
(请注意:在 emacs-devel 列表中讨论 “为何部分 GNU 项目要求版权转让” 属于偏离主题,相关讨论请前往 gnu-misc-discuss 列表。)
版权声明 也是一种可选方式,但我们更倾向于版权转让。注意:版权声明与版权转让一样,需要你向 FSF 提交签署后的文件,仅声明 “此作品已进入公有领域” 是不够的。此外,版权声明 不能适用于后续作品 ,每次提交新内容都需要重新办理。
我们接受 小幅改动 (大致为少于 15 行代码)无需版权转让。该限制为 累计上限 (例如:3 个独立的 5 行补丁合计也会达到上限)。
56. 如何获取 GNU Emacs 相关帮助
如果你需要关于安装、使用或修改 GNU Emacs 的帮助,有两种途径可以获取:
- 发送邮件至 help-gnu-emacs 邮件列表,或在新闻组
gnu.emacs.help发布求助信息。(该邮件列表与新闻组是互通的,使用其中任意一个均可。) - 在服务目录中查找可提供有偿帮助的人员。
附录 A GNU 通用公共许可证
附录 B GNU 自由文档许可证
附录 C Emacs 启动命令行参数
Emacs 支持在启动时通过命令行参数执行各类操作。这些参数主要用于与其他编辑器兼容,以及完成复杂任务。 日常编辑我们不建议使用它们 (如需从命令行访问已运行的 Emacs 进程,可参见《将 Emacs 作为服务器使用》)。
以 '-' 开头的参数是选项,'+行号' 也属于选项。所有其他参数用于指定要打开的文件。Emacs 启动时会打开这些文件,命令行中 最后一个指定的文件 成为当前缓冲区,其他文件也会在各自缓冲区中打开。与大多数程序一样,特殊参数 '--' 表示:后续所有参数均为文件名,而非选项,即使它们以 '-' 开头。
Emacs 命令选项可设置多项内容,例如 X 窗口的大小、位置、颜色等。部分选项用于高级场景,比如在批处理模式下对文件运行 Lisp 函数。本章各节将按用途分类介绍可用选项。
选项有两种写法:
- 短格式:以单个 ‘-’ 开头
- 长格式:以 ‘–’ 开头
例如 ‘-d’ 是短格式,对应的长格式是 ‘–display’。
以 ‘–’ 开头的长格式更易记,但输入更长。不过你不必完整拼写,只要输入无歧义的缩写即可。当长格式需要参数时,选项名与参数之间可以用空格或等号分隔。例如 ‘–display’ 可以写成: ‘–display sugar-bombs:0.0’ 或 ‘–display=sugar-bombs:0.0’
我们推荐使用等号,因为关系更清晰,下面表格中也统一使用等号。
大多数选项用于初始化 Emacs 或设置会话参数,称为初始选项。少数选项用于执行具体动作,如加载库、调用 Lisp 函数,称为动作选项。这些选项与文件名统称为动作参数。动作参数以字符串列表形式保存在变量 command-line-args 中。(实际上,Emacs 启动时 command-line-args 会包含所有命令行参数;初始化过程中,已处理的初始选项会从列表中移除,最终只保留动作参数。)
C.1 动作参数
下面是动作参数一览表:
- '
file' - '
--file=file' - '
--find-file=file' - '
--visit=file' 打开指定的 file文件 。参见 “访问文件”。
Emacs 启动时,会在一个窗口显示启动缓冲,在另一个窗口显示 file文件 缓冲(参见 “多窗口”)。如果你提供了多个文件参数,最后一个文件会被显示出来;其他文件也会被打开,但缓冲不显示。
如果关闭了启动缓冲(参见 “进入 Emacs”):只带一个文件参数启动:在单个窗口显示该文件;带两个文件参数:在两个不同窗口分别显示;带两个以上文件:最后一个文件单独显示,另一个窗口显示缓冲菜单列出其余文件(参见 “对多个缓冲操作”)。要禁止这种缓冲菜单行为,将变量
inhibit-startup-buffer-menu设为t。- '
+linenum file' - 打开文件,并跳转到第 linenum 行。
- '
+linenum:columnnum file' - 打开文件,跳转到第 linenum 行,并将光标定位到第 columnnum 列。
- '
-l file' - '
--load=file' 用
load函数加载名为 file 的 Lisp 库。如果 file 不是绝对路径,Emacs 先在当前目录查找,再在load-path目录列表中查找(参见 “Emacs Lisp 代码库”)。警告:如果前面的命令行参数已经打开了文件,当前目录就是最后打开的那个文件所在目录。
- '
-L dir' - '
--directory=dir' 将目录 dir 添加到
load-path最前面。如果指定多个 '-L',顺序会被保留;例如 '-L /foo -L /bar' 会让load-path以 ("foo" "/bar" …) 开头。如果 /dir 以 ':' 开头,Emacs 会去掉 ':' 并把剩余部分追加到load-path末尾(Windows 上用 ';' 代替 ':' ,即使用path-separator的值)。注意: '
-L' 对load-path的修改不影响站点启动文件(stite-start.el)、early-init和init文件加载的目录,因为这些文件在 -L 生效前就已加载解析。 注意:通过该选项对load-path所做的修改 不会影响 Emacs 查找站点启动文件(stite-start.el)以及由用户的 early-init 和 init 文件加载的 Lisp 包所在的目录(参见《Emacs 初始化文件》),因为 '-L' 选项对load-path的修改是在这些文件加载并解析完成之后才生效的。- '
-f function' - '
--funcall=function' - 调用 Lisp 函数 function 。如果是交互式函数(命令),会像按键调用一样交互式读取参数;否则无参调用。
- '
--eval=expression' - '
--execute=expression' - 执行 Lisp 表达式 expression 。
- '
--insert=file' - 将文件 file 的内容插入到处理该参数时的当前缓冲。通常是
*scratch*缓冲(参见 “Lisp 交互缓冲”),但如果前面的参数已打开文件或切换缓冲,则可能是别的缓冲。效果等同于M-x insert-file(参见 “其他文件操作”)。 - '
--kill' - 直接退出 Emacs,不询问确认。
- '
--help' - 打印帮助信息,列出所有可用选项,然后正常退出。
- '
--version' - 打印 Emacs 版本,然后正常退出。
- '
--fingerprint' - 打印 Emacs 指纹,用于唯一标识本次编译版本。
C.2 初始选项
initial options初始选项 用于指定 Emacs 会话的参数。本节介绍较为通用的初始选项;其他一些与 X Window 系统相关的专用选项将在后续小节说明。
部分初始选项会影响初始化文件的加载。默认情况下,Emacs 会先加载 site-start.el (若存在),再加载你个人的初始化文件(若存在),最后加载默认初始化文件 default.el (若存在)(详见《Emacs 初始化文件》)。部分选项会阻止加载这些文件,或用其他文件替代它们。
- '
-chdir directory' - '
--chdir=directory' - 在执行其他操作前先切换到指定目录。主要用于 X 窗口系统下的会话管理,让 Emacs 在退出时的目录重新启动,方便桌面状态的保存与恢复。
- '
-t device' - '
--terminal=device' - 使用指定 device设备 作为终端输入输出设备。该选项隐含启用 '
--no-window-system'。 - '
-d display' - '
--display=display' - 使用 X Window 系统,并在指定的 display 上打开初始 Emacs 窗口。详情见《指定显示名称》。
- '
-nw' - '
--no-window-system' - 不直接与窗口系统交互,即使设置了
DISPLAY环境变量也忽略。Emacs 会直接使用启动它的终端进行所有显示与输入。 - '
-batch' - '
--batch' 以 batch mode批处理模式 运行 Emacs。批处理模式用于从 shell 脚本、Makefile 等中运行 Emacs Lisp 程序。若要运行 Lisp 程序,可将 '
-batch' 与 '-l' 、 '-f' 或 '--eval' 配合使用(详见《动作参数》)。示例见《命令参数示例》。在批处理模式下,Emacs 不显示编辑文本,标准终端中断字符(如
C-z、C-c)保持原有作用。原本在回显区打印信息的函数,会改为输出到标准输出流(stdout)或标准错误流(stderr)。(准确来说: prin1、princ、print 输出到stdout;message、error输出到stderr。)原本从小缓冲读取键盘输入的函数,会改为从终端标准输入流(stdin)读取。'
--batch' 隐含 '-q'(不加载初始化文件),但仍会加载site-start.el。它还会让 Emacs 在处理完所有命令行选项后自动退出。此外,它会禁用自动保存(除非缓冲区明确要求自动保存),且保存文件时默认不执行fsync系统调用(除非另行指定)。在 '
--batch' 模式下运行出错时,会打印 Emacs Lisp 调用栈回溯。若要关闭此行为,将backtrace-on-error-noninteractive设为nil。- '
--script file' 以类似 '
--batch' 的批处理模式运行 Emacs,并读取执行指定 file文件 中的 Lisp 代码。该选项常用于可执行脚本文件,首行可写:
#!/usr/bin/emacs --script
这样会以 '
--script' 启动 Emacs 并将脚本文件名作为参数。Emacs Lisp 会将首行的 '#!' 视为注释分隔符。- '
-x' 该选项只能用于可执行脚本文件,用法:
#!/usr/bin/emacs -x
该选项与 '
--script' 类似,但会禁止加载初始化文件(效果同 '--quick'),且不能在普通命令行中使用(因为它不指定要加载的脚本)。此外,当执行到脚本末尾时,Emacs 会退出,并将最后一个表达式的值作为脚本的退出码(如果该值是数字);否则始终以退出码0退出。注意:在这种模式下,Emacs 读取 Lisp 代码时会忽略所有文件局部变量(见《指定文件变量》),包括文件首行与靠近文件末尾的局部变量区段。- '
--no-build-details' - 从 Emacs 可执行文件中省略系统名、编译时间等细节信息,使构建更具确定性。此选项不适合日常(交互)使用,因为会让
system-name等命令返回nil。 - '
-q' - '
--no-init-file' - 不加载任何用户初始化文件(详见《Emacs 初始化文件》)。使用该选项启动时,Customize 定制界面不允许保存设置(详见《简易定制界面》)。不会禁止加载
site-start.el。 - '
--no-site-file' - '
-nsl' - 不加载
site-start.el(详见《Emacs 初始化文件》)。 '-Q' 也有此效果,但 '-q' 等选项没有。 - '
--no-site-lisp' - 不将
site-lisp目录加入load-path(详见《Emacs 初始化文件》)。 '-Q' 也有此效果。 - '
--init-directory' - 指定 Emacs 查找初始化文件的目录。注意:该选项只覆盖
user-emacs-directory的值(Emacs 通常在搜索初始化文件时自动确定),不会改变对~/.emacs等初始化文件的搜索逻辑。特别地:若该目录下没有init.el,Emacs 仍会使用原本会找到的init.el,但其他用户级 Emacs 文件会在该目录下查找。若想强制使用该目录下的init.el,请在启动前确保该文件已存在。 - '
--no-splash' - 不显示启动欢迎屏幕。也可在初始化文件中将
inhibit-startup-screen设为非 nil 实现相同效果(详见《进入 Emacs》)。 - '
--no-x-resources' - 不加载 X 资源。也可在初始化文件中将
inhibit-x-resources设为t实现相同效果(详见《X 资源》)。 - '
-Q' - '
--quick' - 以最小定制化快速启动 Emacs。等价于同时使用: '
-q'、'--no-site-file'、'--no-site-lisp'、'--no-x-resources'、'--no-splash'。 - '
-daemon' - '
--daemon[=name]' - '
--bg-daemon[=name]' - '
--fg-daemon[=name]' - 以守护进程方式启动 Emacs:启动后只运行 Emacs 服务器,不打开任何窗口。之后可使用
emacsclient连接 Emacs 进行编辑。(可指定服务器名称;使用时需在emacsclient中通过 '--socket-name' 指定相同名称,详见《emacsclient 选项》。)详见《将 Emacs 用作服务器》。“后台守护进程” 会与终端脱离并在后台运行('--daemon' 是 '--bg-daemon' 的别名)。 - '
--no-desktop' - 不重新加载任何已保存的桌面状态。详见《保存 Emacs 会话》。
- '
-u user' - '
--user=user' - 加载指定用户的初始化文件,而非当前用户的初始化文件27。
- '
--debug-init' - 对初始化文件中的错误启用 Emacs Lisp 调试器。详见《GNU Emacs Lisp 参考手册:出错时进入调试器》。
- '
--module-assertions' - 对动态加载模块启用严格正确性检查,供模块开发者验证模块是否符合 API 规范。若触发模块相关断言,Emacs 会直接中止。详见《GNU Emacs Lisp 参考手册:编写动态加载模块》。
- '
--dump-file=file' - 从指定文件加载 Emacs 转储状态。默认情况下,已安装的 Emacs 会在安装架构相关文件的目录中查找
emacs.pdmp,该目录由变量exec-directory指定。(若从编译后的 src 目录直接运行未安装的 Emacs,会在可执行文件所在目录查找转储文件。)若重命名或移动了转储文件,可用此选项告诉 Emacs 其位置。
C.3 命令参数示例
下面是一个带参数和选项使用 Emacs 的示例。假设你有一个名为 hack-c.el 的 Lisp 程序文件,该文件加载后,会对当前缓冲区(预期为 C 语言程序)执行一些有用的操作。
emacs --batch foo.c -l hack-c -f save-buffer >& log
该命令的作用是:打开 foo.c ,加载 hack-c.el (它会对打开的文件进行修改),保存 foo.c (注意 save-buffer 就是绑定到 C-x C-s 快捷键的函数),然后退回到 shell(这是由 '--batch' 选项决定的)。'--batch' 还能确保将输出重定向到 log 文件时不会出现问题,因为 Emacs 不会假设自身运行在有图形显示的终端环境中。
C.4 环境变量
environment 环境 是操作系统的一项特性;它由一组带有名称和值的变量组成。每个变量称为 environment variable环境变量 ;环境变量的名称 区分大小写 ,按照惯例通常只使用大写字母。变量的值都是文本字符串。
环境之所以有用,是因为子进程会自动从父进程继承环境。这意味着你可以在登录 Shell 中设置一个环境变量,而你运行的所有程序(包括 Emacs)都会自动获取到它。Emacs 的子进程(例如 Shell、编译器和版本控制工具)也会从 Emacs 继承环境。
在 Emacs 内部,命令 M-x getenv 读取环境变量的名称,并在回显区显示其值。 M-x setenv 用于在 Emacs 环境中设置变量, C-u M-x setenv 则删除一个变量。(在变量值中使用 '$' 进行环境变量替换,与文件名中的用法相同;参见带 $ 的文件名。)变量 initial-environment 存储 Emacs 继承而来的初始环境。
在 Emacs 外部设置环境变量的方式取决于操作系统,尤其是你所使用的 Shell。例如,在 Bash 中把环境变量 ORGANIZATION 设置为 'not very much' 的方法是:
export ORGANIZATION="not very much"
而在 csh 或 tcsh 中的写法是:
setenv ORGANIZATION "not very much"
当 Emacs 在 X Window System 下运行时,各种控制 X 的环境变量对 Emacs 同样生效。更多信息请参考 X 相关文档。
C.4.1 通用变量
下面按字母顺序列出在 Emacs 中具有特殊含义的环境变量。其中大多数变量也被其他一些程序使用。Emacs 不要求必须设置这些环境变量,但如果设置了,就会使用它们的值。
CDPATH- 当你指定相对目录时,
cd命令会根据该变量搜索对应的目录。 COLORTERM- 如果该变量的值设为 'truecolor' ,即使未安装 terminfo 数据库,也会告知 Emacs 在文本模式终端下使用 24 位真彩色。Emacs 会使用内置命令,通过 RGB 值请求真彩色,而不是依赖缺失的 terminfo 信息。
DBUS_SESSION_BUS_ADDRESS- 当 Emacs 编译时启用了 D-Bus 支持,该变量会被 D-Bus 使用。通常无需修改。将其设为无效地址(如 'unix:path=/dev/null' ),可以禁止连接 D-Bus 会话总线,也会阻止尚未运行的 D-Bus 会话总线自动启动。
EMACSDATA- Emacs 自带的与架构无关的文件所在目录,用于初始化变量
data-directory。 EMACSDOC- 文档字符串文件所在目录,用于初始化 Lisp 变量
doc-directory。 EMACSLOADPATH- 由冒号分隔的目录列表28,用于搜索 Emacs Lisp 文件。如果设置了该变量,它会替换 load-path 变量的常规初始值(参见 Emacs 的 Lisp 代码库)。空元素代表
load-path的默认值;例如,使用 ‘EMACSLOADPATH="/tmp:"’ 会将/tmp添加到默认load-path的最前面。若要在列表中间指定空元素,可连续使用两个冒号,如 ‘EMACSLOADPATH="/tmp::/foo"’。 EMACSPATH- 由冒号分隔的可执行文件搜索目录列表。如果设置,Emacs 在初始化
exec-path变量时,会在 PATH(见下文)之外额外使用该路径(参见 在 Emacs 中运行 Shell 命令)。 EMAIL- 你的邮箱地址,用于初始化 Lisp 变量
user-mail-address。Emacs 邮件界面会在发出邮件的 From 信头中使用该地址(参见 邮件信头字段)。 ESHELL- 在 shell-mode 中用于覆盖
SHELL环境变量(参见 交互式子 Shell)。 HISTFILE- 登录会话之间保存 Shell 命令的文件名。使用 Bash 时默认为
~/.bash_history、使用 ksh 时默认为~/.sh_history、其他 Shell 默认为~/.history HOME- 你在目录树中的文件根位置,用于展开以波浪号 ~ 开头的文件名。如果设置,应设为绝对路径。(若设为相对路径,Emacs 会相对于启动目录解释,但不推荐这样用。)如果未设置,HOME 通常默认使用 LOGNAME、USER 或用户 ID 对应用户的家目录,都失败则用
/。在 MS-DOS 上,默认为 Emacs 启动目录,并自动去掉末尾的/bin(如果存在)。在 Windows 上,HOME 默认是用户配置目录下的 Application Data 子目录(通常是 C:/Documents and Settings/用户名/Application Data);但为兼容,如果在 C:/ 下找到 .emacs 文件,会直接用 C:/ 作为 HOME。 HOSTNAME- Emacs 运行所在的主机名。
INFOPATH- 由冒号分隔的目录列表,用于搜索 Info 格式文档。
LC_ALLLC_COLLATELC_CTYPELC_MESSAGESLC_MONETARYLC_NUMERICLC_TIMELANG用户偏好的区域设置。区域分为 6 个类别:
LC_COLLATE:排序规则、LC_CTYPE:字符编码、LC_MESSAGES:系统消息语言、LC_MONETARY:货币格式、LC_NUMERIC:数字格式、LC_TIME:日期时间格式。若某个类别变量未设置,默认使用LANG的值;LANG也未设置则使用默认 C 区域。但如果设置了LC_ALL,它会覆盖所有其他区域相关环境变量。在 Windows 和 macOS 上,如果环境中未设置
LANG,Emacs 会根据系统全局默认值自动设置。Windows 可在 “区域设置” 控制面板配置,macOS 在 “语言与地区” 系统偏好设置中配置。在 Android 系统上运行图形界面会话时,
LANG会被设为固定值,但语言与区域环境由系统的「语言与输入法」偏好设置决定。详见《在 Android 下运行 Emacs》。LC_CTYPE类别的值会与locale-language-names、locale-charset-language-names和locale-preferred-coding-systems中的条目进行匹配,以选择默认的语言环境和编码系统。详见《语言环境》。LOGNAME- 用户登录名,参见
USER。 MAIL- 系统邮件收件箱文件名。
MH- MH 系统的配置文件名称,参见《Emacs MH-E 接口》文档。
NAME- 你的真实姓名,用于初始化变量 user-full-name(参见 邮件信头字段)。
NNTPSERVER- 新闻组服务器名称,被 mh 和 Gnus 包使用。
ORGANIZATION- 所属单位 / 机构名称,用于在 Gnus 发文中设置 ‘Organization:’ 信头。
PATH- 由冒号分隔的可执行文件目录列表,用于初始化变量
exec-path(参见 在 Emacs 中运行 Shell 命令)。 PWD- 如果设置,应为 Emacs 启动时的默认工作目录。
REPLYTO- 如果设置,指定变量
mail-default-reply-to的初始值(参见 邮件信头字段)。 SAVEDIR- 默认保存新闻文章的目录名,被 Gnus 包使用。
SHELL- 用于解析并执行 Emacs 内部运行程序的解释器名称,用于初始化变量
shell-file-name(参见 单条 Shell 命令)。 SMTPSERVER- 发件邮件服务器名称,用于初始化变量
smtpmail-smtp-server(参见 邮件发送)。 TERM- Emacs 使用的终端类型。除非以批处理模式运行,否则该变量必须设置。在 MS-DOS 上默认为 internal,表示使用内置终端仿真驱动本机显示。
TERMCAP- 描述
TERM指定终端编程方式的 termcap 库文件路径,默认为/etc/termcap。 TMPDIRTMPTEMP- 这些环境变量用于初始化
temporary-file-directory,即存放临时文件的目录(参见 备份文件)。Emacs 优先使用TMPDIR;未设置时,类 Unix 系统默认用/tmp;Windows 和 MS-DOS 则依次回退到TMP、TEMP,最后是c:/temp。 TZ- 指定默认时区,可能还包含夏令时信息。参见《GNU Emacs Lisp 参考手册》中的时区规则。在 MS-DOS 上,如果启动时未设置
TZ,Emacs 会根据 DOS 返回的国家代码设置合适的默认值。在 Windows 上,Emacs 完全不使用TZ。 USER- 用户登录名,参见
LOGNAME。在 MS-DOS 上默认为 ‘root’。 VERSION_CONTROL- 用于初始化
version-control变量(参见 单文件或编号备份)。
。
C.4.2 杂项变量
这些变量仅在特定配置下使用:
COMSPEC- 在 MS-DOS 和 MS-Windows 系统中,用于指定执行批处理文件与 Shell 内部命令时所使用的命令解释器名称。在 MS-DOS 上,该变量还会被用作 SHELL 环境变量的默认值。
NAME- 在 MS-DOS 上,该变量默认取
USER变量的值。 EMACSTEST- 在 MS-DOS 上,用于指定一个文件,用来记录内置终端仿真器的运行日志。该功能在提交 Bug 报告时非常有用。
EMACSCOLORS在 MS-DOS 上,用于指定屏幕颜色。通过这种方式设置颜色很实用,否则 Emacs 启动时会短暂显示默认颜色。
该变量的值应为默认字体的 前景色(第一个字符)和背景色(第二个字符) 的两位十六进制编码。每个字符对应标准 PC 文本模式显示器上的目标颜色编码。例如,要在浅灰色背景上显示蓝色文字,可设置: ‘EMACSCOLORS=17’ 其中 1 是蓝色编码,7 是浅灰色编码。
PC 显示器通常只支持 8 种背景色,但 Emacs 会将 DOS 显示切换到可使用全部 16 种颜色作为背景的模式,因此背景色的 4 个位都会被实际使用。
PRELOAD_WINSOCK- 在 MS-Windows 上,如果设置该变量,Emacs 会在启动时就加载并初始化网络库,而不是等到第一次需要时才加载。
WAYLAND_DISPLAY- 使用
--with-pgtk编译的 Pgtk 版 Emacs 可以原生运行在 Wayland 上。WAYLAND_DISPLAY用于指定与合成器(compositor)的连接。 emacs_dir在 MS-Windows 上, ~emacs_dir~ 是一个特殊的环境变量,用于表示 Emacs 安装目录的完整路径。如果 Emacs 安装在标准目录结构中,它会自动计算该值。除非你的安装方式是非标准的,否则一般不需要手动设置该变量,因为与其他环境变量不同,它会在 Emacs 启动时被覆盖。
在设置其他环境变量(如
EMACSLOADPATH)时,使用 ~emacs_dir~ 而不是写死绝对路径会很方便。这允许多个版本的 Emacs 共用同一套环境变量配置,也允许你移动 Emacs 安装目录,而无需修改任何环境变量或注册表设置。
C.4.3 MS-Windows 系统注册表
在 MS-Windows 系统中,环境变量 emacs_dir 、 EMACSLOADPATH 、 EMACSDATA 、 EMACSPATH 、 EMACSDOC 、 SHELL 、 TERM 、 HOME 、 LANG 和 PRELOAD_WINSOCK 也可以在系统注册表的 HKEY_CURRENT_USER 或 HKEY_LOCAL_MACHINE 项下的 /Software/GNU/Emacs 键中进行设置。Emacs 启动时,除了检查环境变量,还会在系统注册表中查找这些变量。
Emacs 按以下顺序确定这些变量的值:
- 首先检查环境变量;
- 如果环境中没有该变量,就在注册表 /Software/GNU/Emacs 下查找同名注册表项,优先查找
HKEY_CURRENT_USER,找不到再查HKEY_LOCAL_MACHINE; - 仍然无法确定值时,使用编译内置的默认值。
注意:注册表设置会对整个系统全局生效,影响这台机器上运行的所有 Emacs 会话。因此,如果你运行多个 Emacs 版本、同时使用安装版与免安装版,或自行编译新版 Emacs,注册表配置会让它们都使用相同目录,这通常不是你想要的效果。出于这个原因, 我们不建议在注册表中设置这些变量 。如果你已经有这类注册表配置,建议删除。
如果你运行 Emacs 的 Windows 安装程序 addpm.exe,它会把 emacs_dir、EMACSLOADPATH、EMACSDATA、EMACSPATH、EMACSDOC、SHELL 和 TERM 等已存在的注册表项更新为与该 addpm.exe 对应 Emacs 版本适配的值。注意:addpm.exe 不会新建任何注册表项,只会更新已有的项(这些项多半来自旧版 Emacs),使其与新版 Emacs 兼容。安装新版 Emacs 时不再需要运行 addpm.exe;仅当你从旧版本升级,且因某些原因无法删除注册表旧设置时,才建议这样做。
除上述环境变量外,你还可以在 /Software/GNU/Emacs 注册表项中添加配置,用来指定 X 资源(参见 X 选项与资源)。大多数可以在 .Xdefaults 文件中设置的项,都可以通过该注册表键来配置。
C.5 指定显示名称
环境变量 DISPLAY 会告诉所有 X 客户端(包括 Emacs)在何处显示窗口。在常规情况下,当你启动 X 服务器并在本地运行任务时,它的值会被默认设置。你可以手动指定显示位置,一个常见的场景是:登录到另一台机器并在那里运行 Emacs,同时让窗口显示在你本地的终端上。
DISPLAY 的格式为:‘host:display.screen’。 host :X 窗口系统服务器所在的主机名; display :用于区分同一台机器上不同服务器(X 终端)的编号; screen :用于让一个 X 服务器管理多个终端屏幕。句点和 screen 部分是可选的。如果写出来, screen 通常是 0。
例如,如果你的主机名为 ‘glasperle’,且服务器是配置中的第一个(或唯一)服务器,那么你的 DISPLAY 就是:‘glasperle:0.0’
你在启动 Emacs 时可以显式指定显示名称,方法有两种:修改 DISPLAY 环境变量;使用选项 '-d display' 或 '--display=display'。示例:
emacs --display=glasperle:0 &
你可以用 '-nw' 选项禁止使用 X 窗口系统,让 Emacs 直接使用其控制的文本终端进行显示。参见「初始选项」。
有时,安全机制会阻止远程机器上的程序在本地显示。这种情况下,运行 Emacs 会出现类似提示:
Xlib: connection to "glasperle:0.0" refused by server
你可以在本地机器上使用 xhost 命令,授予远程机器访问权限,通常就能解决这个问题。
C.6 字体指定选项
你可以使用命令行选项 '-fn 字体' (或 '--font',它是 '-fn' 的别名)来指定默认字体:
-fn font--font=font- 使用指定 font字体 作为默认字体。
在命令行中将字体名传递给 Emacs 时,如果字体名包含 Shell 会特殊处理的字符(如空格),你可能需要用引号将其括起来进行转义。例如:
emacs -fn "DejaVu Sans Mono-12"
有关字体名称以及指定默认字体的其他方式,详见「字体」章节。
C.7 窗口颜色选项
你可以使用下列命令行选项来指定 Emacs 界面各部分的颜色。颜色可以用颜色名或 RGB 三元组来指定(参见 “面部颜色”)。
- '
-fg color' - '
--foreground-color=color' - 指定前景色,覆盖默认面部(default face)指定的颜色(参见 “文本外观”)。
- '
-bg color' - '
--background-color=color' - 指定背景色,覆盖默认面部指定的颜色。
- '
-bd color' - '
--border-color=color' - 指定 X 窗口边框的颜色。如果 Emacs 是带 GTK+ 支持编译的,该选项无效。
- '
-cr color' - '
--cursor-color=color' - 指定 Emacs 光标(表示光标位置)的颜色。
- '
-ms color' - '
--mouse-color=color' - 指定鼠标在 Emacs 窗口内时的鼠标指针颜色。
- '
-r' - '
-rv' - '
--reverse-video' - 反色显示:交换前景色与背景色。
- '
--color=mode' 在文本终端运行 Emacs 时,设置颜色支持模式。该选项会覆盖终端在
termcap或terminfo数据库中声明的颜色数量。参数 mode 可以是以下之一:- ‘never’
- ‘no’
- 即使终端支持颜色,也不使用颜色。
- ‘default’
- ‘auto’
- 与不使用 –color 选项效果相同:Emacs 启动时检测终端是否支持颜色,支持则开启彩色显示。
- ‘always’
- ‘yes’
- ‘ansi8’
- 无条件开启颜色支持,并使用 ANSI 转义序列定义的 8 种标准颜色。
- ‘num’
- 使用支持 num 种颜色的模式。若 num = -1,关闭颜色支持(等同于 'never');若 num = 0,使用该终端的默认颜色支持(等同于 'auto');否则使用对应 num 色的标准模式。根据终端能力,Emacs 通常可支持 8、16、88 或 256 色。若没有对应 num 色的模式,Emacs 按 num = 0 处理,即使用终端默认颜色模式。
该选项在 MS-Windows 和 MS-DOS 上无效。
如果省略 mode ,默认为 ansi8。
颜色模式可以在 Emacs 运行期间动态修改:当前模式可通过帧参数
tty-color-mode获取,修改该参数即可切换模式29。这意味着你也可以通过default-frame-alist而非命令行选项来设置初始值。
例如,要使用珊瑚色鼠标指针、石板蓝色文本光标,可输入:
emacs -ms coral -cr 'slate blue' &
你可以通过 '-rv' 选项或 X 资源 'reverseVideo' 来反色显示前景与背景。
'-fg'、 '-bg' 和 '-rv' 选项在图形界面与文本终端下均有效。
C.8 窗口大小与位置选项
以下是用于指定 Emacs 初始窗口大小和位置的命令行选项列表:
-g widthxheight[{+-}xoffset{+-}yoffset]]
--geometry=widthxheight[{+-}xoffset{+-}yoffset]]
- 指定窗口大小 width (宽度)和 height (高度,以字符列和行数计量),以及位置 xoffset 和 yoffset (以像素计量)。宽度和高度参数对所有窗口生效,而 xoffset 和 yoffset 仅对初始窗口生效。
- 将窗口宽高设为屏幕大小。通常不显示窗口管理器装饰。(启动 Emacs 后,可按
F11切换全屏状态,对应命令toggle-frame-fullscreen。) - 将 Emacs 窗口最大化。通常会保留窗口管理器装饰。(启动后可按
M-F10切换最大化状态,对应命令toggle-frame-maximized。) - 将窗口高度设为屏幕高度。
- 将窗口宽度设为屏幕宽度。
在 --geometry 选项中, {+-} 表示加号或减号。 xoffset 前的加号表示距离屏幕左侧,减号表示从右侧算起。 yoffset 前的加号表示距离屏幕顶部,减号表示从底部算起。 xoffset 和 yoffset 本身可以是正或负,但只改变方向,不改变含义。
Emacs 使用与 xterm 相同的单位解析 geometry 参数: width宽度 和 height高度 以字符为单位,因此大字体会生成更大的窗口。(若使用等宽字体以外的比例字体,Emacs 以其最大边界宽度作为宽度单位。) xoffset 和 yoffset 以像素为单位。
geometry 中不必指定所有字段。如果省略 xoffset 和 yoffset ,窗口管理器会决定窗口位置,通常允许你用鼠标放置。例如: '164x55' 指定窗口宽 164 列、高 55 行。
窗口默认宽度为 80 字符,默认高度在 35–40 行之间,取决于操作系统与窗口管理器。你可以只写宽度、只写高度,或都不写:
- 以数字开头:视为宽度,如 81
- 以 x 加数字开头:视为高度,如 x45
以 '+' 或 '-' 开头表示只设置偏移,不设置大小:
- '-3' 只设置 xoffset ;
- '+3-3' 同时设置 xoffset 和 yoffset ,将窗口放在屏幕左下角附近。
你可以在 X 资源文件(见X资源)中为任意字段设置默认值,再用 --geometry 覆盖部分字段。
由于模式行和回显区占用窗口最后 2 行,初始文本区域的高度会比 geometry 指定的高度少 2 行。在非 X 工具集版本的 Emacs 中,菜单栏还会额外占用 1 行;但在 X 工具集版本中,菜单栏是附加的,不计入指定高度。工具栏同理。
开启 / 关闭菜单栏或工具栏会改变文本可用空间。如果 Emacs 默认启动带工具栏,并按此计算 geometry,但你的初始化文件又关闭了工具栏,最终窗口大小会与预期不一致。想要在无工具栏时得到正确尺寸,应先用 X 资源关闭工具栏,再让 Emacs 按此处理 geometry。
使用 --fullscreen 、 --maximized 、 --fullwidth 、 --fullheight 时,部分窗口管理器需要将变量 frame-resize-pixelwise 设为非 nil 值,才能真正全屏或最大化。
部分窗口管理器可设置忽略程序或用户指定的位置,此时 Emacs 无法正确摆放窗口。
C.9 内部边框与外部边框
Emacs 窗口包含内部边框和外部边框。内部边框是窗口文本区域周围一圈额外的背景色条带,由 Emacs 自身绘制。外部边框由 X 窗口系统在窗口的工具栏与菜单栏外侧绘制。此外还有外部边框由窗口管理器负责绘制,其大小无法在 Emacs 内部设置。
- '-ib width='
- '
--internal-border=width' - 以像素为单位,指定窗口文本区域周围内部边框的 width宽度 。
- '
-bw width' - '
--border-width=width' - 以像素为单位,指定外部边框的 width宽度 。
你指定的窗口大小不包含边框尺寸。窗口的位置从最外层外部边框的外边缘开始计算。
使用 '-ib n' 选项可以指定宽度为 n 像素的内部边框,默认值为 1 。使用 '-bw n' 可以指定外部边框宽度(不过窗口管理器可能不会遵循你指定的值)。外部边框的默认宽度是 2 。
C.10 框架标题
每个 Emacs 框架都始终有一个标题,会显示在窗口装饰和图标上,作为框架的名称。默认标题的格式为 'invocation-name@machine' (只有一个框架时),或显示当前选中窗口的缓冲区名称(多个框架时)。
你可以通过命令行选项为初始 Emacs 框架指定自定义标题:
-T title--title=title- 将 title 指定为初始 Emacs 窗口的标题。
--name 选项(参见 X 资源)也可用于指定初始 Emacs 框架的标题。
C.11 图标
-iconic--iconic- 以图标化(最小化)状态启动 Emacs。
-nbi--no-bitmap-icon- 禁用 Emacs 自带图标。
大多数窗口管理器都允许你将 Emacs 窗口图标化("最小化")以隐藏窗口。有些窗口管理器会用小图标代替被最小化的窗口,另一些则会将其完全隐藏。 '-iconic' 选项让 Emacs 启动后直接处于图标化状态,而不是立刻显示窗口。文本框架只有在你取消图标化("恢复窗口")后才会显示。
默认情况下,Emacs 使用带有 Emacs 标志的图标。在 Gnome 等桌面环境中,该图标也会在其他场景下显示,例如在切换窗口时。 -nbi 或 --no-bitmap-icon 选项让窗口管理器自行选择图标 —— 通常只显示一个带框架标题的小矩形。
C.12 其他显示选项
--parent-id id- 通过 XEmbed 协议将 Emacs 作为 X 客户端窗口打开,以 id 作为父窗口 ID。目前该选项主要对开发者有用。
-vb--vertical-scroll-bars- 启用垂直滚动条。
-lsp pixels--line-spacing=pixels- 以像素为单位,指定 piexls 像素作为行与行之间的额外间距。
-nbc--no-blinking-cursor- 在图形界面中关闭光标闪烁。
-D--basic-display- 禁用菜单栏、工具栏、滚动条、工具提示,并关闭 font-lock-mode 和光标闪烁。此选项有助于创建简化显示问题调试的测试用例。
--xrm 选项(参见 X 资源)用于指定额外的 X 资源值。
附录 D X 窗口系统选项与资源
你可以使用 X 资源自定义 Emacs 中一些与 X 相关的行为,这与使用 X 的常规程序一致。
当 Emacs 编译时启用了 GTK+ 支持,各类图形组件(如菜单栏、滚动条、对话框)的外观由 GTK+ 资源决定,本文也会对此进行说明。如果编译时未启用 GTK+ 支持,则这些组件的外观由额外的 X 资源决定。
在 Windows 系统上,你可以通过系统注册表自定义部分相同的设置(参见 Windows 系统注册表)。
D.1 X 资源
在 X Window 系统下运行的程序,会将用户选项组织在 类(class)和资源(resource) 的层级结构中。你可以在 X 资源文件中为这些选项指定默认值,该文件通常名为 ~/.Xdefaults 或 ~/.Xresources 。对该文件的修改不会立即生效,因为 X 服务器会保存自己的资源列表;若要更新,可使用 xrdb 命令,例如: xrdb ~/.Xdefaults 。
通过 X 资源指定的设置,通常会 覆盖 Emacs 初始化文件中的等效设置(参见《Emacs 初始化文件》),尤其是对初始框架的参数(参见《框架参数》)。
(MS-Windows 系统不支持 X 资源文件;在这类系统上,Emacs 会从 Windows 注册表中查找 X 资源:首先查找 'HKEY_CURRENT_USER\SOFTWARE\GNU\Emacs' 项,该设置仅对当前用户生效,并会覆盖系统级设置;然后查找 'HKEY_LOCAL_MACHINE\SOFTWARE\GNU\Emacs' 项,该设置对系统所有用户生效。在 MS-Windows 上,菜单和滚动条是原生组件,因此只能通过「显示」控制面板中的系统级设置来自定义。你也可以使用 '-xrm' 命令行选项设置资源,详见下文。)
X 资源文件中的每一行,用于为一个选项或一组相关选项指定值。行的先后顺序无关紧要。每条资源定义由 program程序名 和 resource资源名 组成,两者均区分大小写。示例如下:
emacs.cursorColor: dark green
程序名 是该资源所作用的可执行文件名称。对 Emacs 而言,通常为 emacs。若要指定一个定义适用于所有 Emacs 实例,而不考虑可执行文件名称,则使用 'Emacs'。
资源名 是程序设置项的名称。例如,Emacs 识别 'cursorColor' 资源,用于控制文本光标的颜色。
资源会被归入具名的类(class)中。例如, 'Foreground' 类包含 'cursorColor'、'foreground' 和 'pointerColor' 等资源(参见《Emacs X 资源一览表》)。你可以直接使用类名,为该类下所有资源指定默认值,而不必单独指定资源名,例如:
emacs.Foreground: dark green
如果将变量 inhibit-x-resources 设置为非 nil 值,Emacs 将完全不处理 X 资源。若使用 -Q (或 --quick )命令行选项启动 Emacs, inhibit-x-resources 会被自动设为 t (参见《初始选项》)。
此外,你可以使用以下命令行选项来覆盖 X 资源文件中的设置:
-name name--name=name该选项将初始 Emacs 框架的程序名设为 name ,同时也将初始框架的标题设为 name 。此选项不影响后续创建的框架。
若不指定该选项,默认使用 Emacs 可执行文件的名称作为程序名。
为保持一致,
-name也会为那些不属于任何特定框架的其他资源值指定名称。命名 Emacs 调用的资源同样属于一个类,类名为 'Emacs'。如果你写成 'Emacs' 而非 'emacs',则该资源将作用于所有 Emacs 进程中的所有框架,无论框架标题和可执行文件名是什么。
-xrm resource-value--xrm=resource-value该选项为当前 Emacs 进程指定 X 资源值。
resource-value 的格式与在 X 资源文件中直接书写的格式一致。可以使用多个
-xrm选项来指定多条资源定义。你也可以用#include "filename"作为 resource-value ,来引入一整个资源文件。通过 '-xrm' 指定的资源值,优先级 高于 所有其他资源定义。
D.2 Emacs 的 X 资源表
下表列出了 Emacs 可识别的 X 资源名称。注意,部分资源在使用不同 X 工具包(GTK+、Lucid 等)编译的 Emacs 中无效,文中会标注这种情况。
- alpha(类 Alpha)
- 设置 'alpha' 框架参数,控制框架透明度(参见《Emacs Lisp 参考手册》中的框架参数)。
- alphaBackground(类 AlphaBackground)
- 设置 'alpha-background' 框架参数,控制背景透明度(参见《Emacs Lisp 参考手册》中的框架参数)。
- background(类 Background)
- 背景颜色(参见外观颜色)。
- bitmapIcon(类 BitmapIcon)
- 值为 'on' 时,告知窗口管理器显示 Emacs 图标;为 'off' 则不显示。关于图标的说明,参见图标相关章节。
- borderColor(类 BorderColor)
- 框架外部边框颜色。如果 Emacs 启用 GTK+ 编译,此项无效。
- borderWidth(类 BorderWidth)
- 框架外部边框宽度,单位像素。如果 Emacs 启用 GTK+ 编译,此项无效。
- cursorBlink(类 CursorBlink)
- 启动时值为 'off'、'false' 或 '0',Emacs 将关闭光标闪烁模式(参见光标显示)。
- cursorColor(类 Foreground)
- 文本光标颜色。若在 Emacs 启动时指定该资源,会将其设为光标面版的背景色(参见文字外观)。
- font(类 Font)
- 默认面版使用的字体名称(参见字体)。也可指定字体集名称(参见字体集)。
- fontBackend(类 FontBackend)
- 逗号分隔的字体渲染后端列表,按优先级排序。例如 'x,xft' 表示优先使用 X 核心字体驱动,失败则回退到 Xft。通常建议不设置,Emacs 会自动尝试所有可用后端。
- foreground(类 Foreground)
- 文本默认前景色。
- fullscreen(类 Fullscreen)
- 期望的全屏模式。取值可为
fullboth、maximized、fullwidth、fullheight,分别对应命令行选项 '-fs'、'-mm'、'-fw'、'-fh'(参见窗口大小与位置选项)。 仅对初始框架生效 。 - geometry(类 Geometry)
窗口大小与位置。格式与 '
-g'/'--geometry' 命令行参数相同(参见窗口大小与位置选项)。大小对 Emacs 会话中所有框架生效,位置 仅对初始框架生效 (若为特定框架名的资源,则仅对该框架)。
注意不要写成 'emacs*geometry' ,否则会同时影响菜单与主框架。
- horizontalScrollBars(类 ScrollBars)
- 值为 'off'/ 'false' / '0' 时,Emacs 启动时关闭水平滚动条(参见滚动条)。
- iconName(类 Title)
- 图标上显示的名称。
- internalBorder(类 BorderWidth)
- 框架内部边框宽度,单位像素。
- lineSpacing(类 LineSpacing)
- 行之间额外的间距,单位像素。
- menuBackground(类 Background)
- 非工具包版本 Emacs 的菜单背景色。(工具包版本参见 Lucid 菜单与对话框 X 资源、Motif 菜单 X 资源及 GTK+ 资源)。
- menuBar(类 MenuBar)
- 值为 'off' / 'false' / '0' 时,Emacs 启动时关闭菜单栏(参见菜单栏)。
- minibuffer(类 Minibuffer)
- 若为 'none',当前框架不创建小缓冲,而是使用独立的小缓冲框架。
- paneFont(类 Font)
- 非工具包版本的 Emacs 中,菜单面板标题所使用的字体名称。(工具包版本请参见 Lucid 菜单与对话框 X 资源、Motif 菜单 X 资源以及 GTK+ 资源。)
- paneForeground(类 Foreground)
- 在非工具包版本的 Emacs 中,指菜单面板标题的前景色。(工具包版本请参见 Lucid 菜单与对话框 X 资源、Motif 菜单 X 资源以及 GTK+ 资源。)
- pointerColor(类 Foreground)
- 鼠标光标颜色。在多数现代图形桌面环境中无效,因为系统不允许 Emacs 直接修改鼠标光标。
- privateColormap(类 PrivateColormap)
- 若为 'on',且默认视觉类型为 PseudoColor 并被 Emacs 使用,则启用私有颜色表。
- reverseVideo(类 ReverseVideo)
- 'on' 表示反转默认前景与背景色;'off' 表示按指定颜色显示。
- screenGamma(类 ScreenGamma)
- 颜色伽马校正,等价于框架参数 screen-gamma。
- scrollBar(类 ScrollBar)
- 值为 off/false/0 时,Emacs 启动时关闭滚动条(参见滚动条)。
- scrollBarWidth(类 ScrollBarWidth)
- 滚动条宽度,单位像素,等价于框架参数
scroll-bar-width。Emacs 启用 GTK+ 编译时 不要设置 此项。 - selectionFont(类 SelectionFont)
- 非工具包版本 Emacs 的弹出菜单项字体。(工具包版本请参见 Lucid 菜单与对话框 X 资源、Motif 菜单 X 资源以及 GTK+ 资源。)
- selectionForeground(类 SelectionForeground)
- 非工具包版本 Emacs 的弹出菜单项前景色。(工具包版本请参见 Lucid 菜单与对话框 X 资源、Motif 菜单 X 资源以及 GTK+ 资源。)
- selectionTimeout(类 SelectionTimeout)
- 等待选区回复的毫秒数。超时未回复则放弃。0 表示无限等待。
- synchronous(类 Synchronous)
- 'on' 表示以同步模式运行 Emacs,常用于调试 X 相关问题。
- title(类 Title)
- 初始 Emacs 框架标题栏显示的名称。
- toolBar(类 ToolBar)
- 值为 off/false/0 时,Emacs 启动时关闭工具栏(参见工具栏)。
- tabBar(类 TabBar)
- 值为 on/yes/1 时,Emacs 启动时启用标签栏(参见标签栏)。
- useXIM(类 UseXIM)
- 为 false/off 时禁用 X 输入法(XIM)。仅在 Emacs 编译支持 XIM 时有效。在较慢的 X 客户端 / 服务器连接中可关闭以提升性能。
- inputStyle(类 InputStyle)
- 控制 X 输入法预编辑文本的显示方式,可选值:
- 'callback'
- 在当前缓冲区显示预编辑文本
- 'offthespot'
- 在 Emacs 提供的独立区域显示
- 'overthespot'
- 在光标位置弹出窗口显示
- 'none'
- 由输入法自行决定(通常等价于 overthespot)
- 'native'
- 由工具包处理输入法(目前仅 GTK 实现)
- 'root'
- 在输入法指定的屏幕位置显示
- synchronizeResize(类 SynchronizeResize)
- 为 off/false 时,Emacs 在重绘完成后不通知窗口管理器。否则窗口管理器会等待内容更新后再绘制,避免出现空白区域。设为 'extended' 会启用扩展帧同步协议,兼容部分合成窗口管理器,并使 Emacs 显示与显示器刷新率同步。
- verticalScrollBars(类 ScrollBars)
- 'left':左侧垂直滚动条;'right':右侧;'off':关闭(参见滚动条)。
- visualClass(类 VisualClass)
- X 颜色显示的 visual class视觉类型 。取值以 TrueColor、PseudoColor、DirectColor、StaticColor、GrayScale、StaticGray 之一开头,后跟 '-depth' 加色深位数。
你也可以用 X 资源单独自定义 Emacs 面版(参见文字外观)。例如设置 'face.attributeForeground' 等价于自定义对应面版的 'foreground' 属性。但我们推荐在 Emacs 内部自定义面版,而非使用 X 资源。参见自定义面版。
D.3 Lucid 菜单与对话框 X 资源
如果 Emacs 在编译时使用 Lucid 部件开启了 X 工具集支持,你可以通过 X 资源自定义菜单栏(见菜单栏)、弹出菜单和对话框(见使用对话框)的外观。菜单栏相关资源属于 'pane.menubar' 类(照例,可使用 Emacs 可执行文件名,或对所有 Emacs 进程使用 'Emacs')。弹出菜单相关资源属于 menu* 类。对话框相关资源属于 dialog* 类。
示例:让菜单栏条目使用 'Courier-12' 字体(见字体),写法如下:
Emacs.pane.menubar.font: Courier-12
Lucid 部件可以显示当前语言环境下的多语言文本。要启用该功能,请指定 fontSet 资源而非 font 资源,详见字体集。若同时指定 font 和 fontSet,优先使用 fontSet。
下面是菜单栏、弹出菜单与对话框可用的资源列表:
font- 菜单项文本所用字体。
fontSet- 菜单项文本所用字体集。
background- 背景色。
buttonForeground- 选中项的前景色。
foreground- 前景色。
disabledForeground- 禁用菜单项的前景色。
highlightForeground- 鼠标或键盘导航高亮菜单项的前景色。
highlightBackground- 鼠标或键盘导航高亮菜单项的背景色。
horizontalSpacing- 项之间的水平间距(像素),默认值:3。
verticalSpacing- 项之间的垂直间距(像素),默认值:2。
arrowSpacing- 子菜单指示箭头与对应文本之间的水平间距(像素),默认值:10。
shadowThickness- 3D 按钮、箭头等图形元素的阴影线条粗细,默认值:1。
borderThickness- 菜单栏与弹出菜单的外边框粗细,默认值:1。
cursor- 菜单栏与弹出菜单中使用的光标名称,默认值:
right_ptr。 margin- 菜单栏边距(字符数),默认值:1。
D.4 Motif 菜单 X 资源
如果 Emacs 在编译时使用 Motif 或 LessTif 部件开启了 X 工具集支持,你可以通过 X 资源自定义菜单栏(见菜单栏)、弹出菜单和对话框(见使用对话框)的外观。不过,这些资源的组织方式与 Lucid 部件不同。
菜单栏的资源名称属于 'pane.menubar' 类,且必须按以下格式指定:
Emacs.pane.menubar.subwidget.resource: value
对于弹出菜单,资源位于 menu* 类,而非 'pane.menubar' 。对于对话框,资源位于 'dialog' 类。在每种情况下,每一条菜单文本都是一个子部件(subwidget);子部件的名称与菜单项文本相同。例如,菜单栏中的「文件」菜单就是一个名为 'emacs.pane.menubar.File' 的子部件。
通常,你希望为整个菜单栏指定相同的资源。为此,可以使用 '*' 代替具体的子部件名称。例如,要为所有菜单栏项(包括子菜单)指定字体 '8x16',写法如下:
Emacs.pane.menubar.*.fontList: 8x16
子菜单中的每一项也拥有自己的 X 资源名称;例如,「File文件」子菜单中有一项名为「保存(当前缓冲区)」。针对子菜单项的资源写法如下:
Emacs.pane.menubar.popup_*.menu.item.resource: value
下面是为「保存(当前缓冲区)」项指定字体的示例:
Emacs.pane.menubar.popup_*.File.Save (current buffer).fontList: 8x16
对于二级子菜单中的项(例如「工具」→「拼写检查」下的「补全单词」),资源遵循以下模板:
Emacs.pane.menubar.popup_*.popup_*.menu.resource: value
示例:
Emacs.pane.menubar.popup_*.popup_*.Spell Checking.Complete Word: value
(这应写为一整行。)
如果你希望子菜单项的外观与菜单栏本身不同,必须先为所有项指定资源,再单独为子菜单覆盖该值。示例如下:
Emacs.pane.menubar.*.fontList: 9x18 Emacs.pane.menubar.popup_*.fontList: 8x16
要为 LessTif 文件选择框指定资源,使用 fsb*,示例:
Emacs.fsb*.fontList: 8x16
以下是 LessTif 菜单栏与弹出菜单可用的资源列表:
armColor- 激活状态按钮的显示颜色。
fontList- 所用字体。
marginBottommarginHeightmarginLeftmarginRightmarginTopmarginWidth- 菜单项在边框内部四周留出的空白大小。
borderWidth- 菜单项四周边框的宽度。
shadowThickness- 边框阴影的宽度。
bottomShadowColor- 边框底部与右侧的阴影颜色。
topShadowColor- 边框顶部与左侧的阴影颜色。
D.5 GTK+ 资源
如果 Emacs 编译时启用了 GTK+ 工具集支持,定制其 GTK+ 部件(如菜单、对话框、工具栏和滚动条)最简单的方式,是选择合适的 GTK+ 主题,例如通过 GNOME 主题选择器。
在 GTK+ 2 中,你还可以使用 GTK+ 资源来定制 Emacs 使用的 GTK+ 部件外观。这些资源可以在以下文件中配置: ~/.emacs.d/gtkrc (专用于 Emacs 的 GTK+ 资源)、 ~/.gtkrc-2.0 (通用 GTK+ 资源)。我们推荐使用 ~/.emacs.d/gtkrc ,因为在 GNOME 下运行 ~GConf~ 时,GTK+ 可能会忽略 ~/.gtkrc-2.0 。但需要注意:部分 GTK+ 主题可能会覆盖 ~/.emacs.d/gtkrc 中的自定义设置,对此我们无法处理。GTK+ 资源不会影响 Emacs 中与 GTK+ 部件无关的部分,例如主窗口的字体和颜色,这些由普通 X 资源控制(见 X 资源)。
后续小节会介绍如何为 Emacs 定制 GTK+ 资源。关于 GTK+ 资源的详细说明,可参考 GTK+ API 文档:https://developer-old.gnome.org/gtk2/stable/gtk2-Resource-Files.html 。
在 GTK+ 3 中,GTK+ 资源已被一套完全不同的系统取代。GTK+ 部件的外观现在由类似 CSS 的样式文件决定:GTK+ 安装目录下的 gtk-3.0/gtk.css ,本地样式配置: ~/.themes/主题名/gtk-3.0/gtk.css (其中 “主题名” 为当前使用的 GTK+ 主题)。因此,本节对 GTK+ 资源的说明不适用于 GTK+ 3。关于 GTK+ 3 样式系统的详情,参见:https://developer-old.gnome.org/gtk3/3.0/GtkCssProvider.html 。
D.5.1 GTK+ 资源基础
在 GTK+ 2 资源文件(通常是 ~/.emacs.d/gtkrc )中,最简单的资源设置方式是直接给变量赋值。例如,在资源文件中加入下面这一行,可以将所有 GTK+ 部件的字体改为 'courier-12':
gtk-font-name = "courier 12"
注意:这里的字体名称必须使用 GTK 字体格式(也叫 Pango 字体名),而不是 Fontconfig 风格的字体名或 XLFD 格式。参见「字体」章节。
要定制部件,你需要先定义一个样式(style),然后将该样式应用到部件上。下面是一个为菜单设置字体的示例('#' 开头表示注释):
# 定义样式 ‘my_style’ style "my_style" { font_name = "helvetica bold 14" } # 指定部件类型 ‘*emacs-menuitem*’ 使用 ‘my_style’ widget "*emacs-menuitem*" style "my_style"
本例中的部件名称包含通配符,因此该样式会应用到所有匹配 '*emacs-menuitem*' 的部件。部件的命名遵循从外层到内层的包含结构。下面这个示例会将 'my_style' 专门应用到 Emacs 菜单栏:
widget "Emacs.pane.menubar.*" style "my_style"
下面是一个更详细的示例,展示如何修改滚动条的各个部分:
style "scroll" { fg[NORMAL] = "red" # 箭头颜色 bg[NORMAL] = "yellow" # 滑块与箭头周围背景 bg[ACTIVE] = "blue" # 滑道颜色 bg[PRELIGHT] = "white" # 鼠标悬停时的滑块颜色 } widget "*verticalScrollBar*" style "scroll"
D.5.2 GTK+ 组件名称
一个 GTK+ 部件由 widget name部件名称 和 widget class部件类 共同指定。 部件名称指向某个具体部件(例如 'emacs-menuitem'),而部件类则指代一类相似的部件(例如 'GtkMenuItem')。每个部件一定有类,但不一定有名称。
Absolute names 绝对名称 是由部件名称或部件类组成的序列,对应部件之间的嵌套层次结构。例如:如果名为 top 的 GtkWindow 窗口包含名为 box 的 GtkVBox 容器,而该容器又包含名为 menubar 的 GtkMenuBar 菜单栏,那么菜单栏部件的绝对类名是:GtkWindow.GtkVBox.GtkMenuBar。它的绝对部件名是:top.box.menubar。
GTK+ 资源文件中可以使用两种命令来设置部件外观。
widget- 根据部件名称或类来为部件指定样式。
widget_class- 仅根据部件类来为部件指定样式。
上一小节已经给出 widget 命令的使用示例;widget_class 的用法与之类似。注意:部件名称 / 类和样式名都必须用双引号括起来,并且这些命令必须写在 GTK+ 资源文件的顶层位置。
如前所述,你可以使用 Shell 通配符语法来指定部件名称或类:
- * 匹配零个或多个字符
- ? 匹配单个字符
下面这个示例为所有部件指定样式:
widget "*" style "my_style"
D.5.3 Emacs 中的 GTK+ 组件名称
macs 框架所使用的 GTK+ 部件如下:
- Emacs(类:GtkWindow)
- pane(类:GtkVBox)
- menubar(类:GtkMenuBar)
- [菜单项部件]
- [未命名部件](类:GtkHandleBox)
- emacs-toolbar(类:GtkToolbar)
- [工具栏项部件]
- emacs-toolbar(类:GtkToolbar)
- emacs(类:GtkFixed)
- verticalScrollBar(类:GtkVScrollbar)
- menubar(类:GtkMenuBar)
- pane(类:GtkVBox)
Emacs 窗口的内容会在 emacs 部件中绘制。注意:即使存在多个 Emacs 窗口,每个滚动条部件的名称都是 verticalScrollBar 。
例如,下面两种写法都可以设置菜单栏样式:
widget "Emacs.pane.menubar.*" style "my_style" widget_class "GtkWindow.GtkVBox.GtkMenuBar.*" style "my_style"
对于 GTK+ 对话框,Emacs 使用名为 emacs-dialog 、属于 GtkDialog 类的部件。对于文件选择,Emacs 使用名为 emacs-filedialog 、属于 GtkFileSelection 类的部件。
由于弹出菜单和对话框的部件是独立窗口,不包含在 Emacs 部件内部,因此它们的 GTK+ 绝对名称不以 Emacs 开头。要定制这些部件,可使用通配符,示例如下:
widget "*emacs-dialog*" style "my_dialog_style" widget "*emacs-filedialog* style "my_file_style" widget "*emacs-menuitem* style "my_menu_style"
如果想将一个样式应用到 Emacs 中的所有菜单,可以这样写:
widget_class "*Menu*" style "my_menu_style"
D.5.4 GTK+ 样式
下面是两个 GTK+ 样式声明的示例:
pixmap_path "/usr/share/pixmaps:/usr/include/X11/pixmaps" style "default" { font_name = "helvetica 12" bg[NORMAL] = { 0.83, 0.80, 0.73 } bg[SELECTED] = { 0.0, 0.55, 0.55 } bg[INSENSITIVE] = { 0.77, 0.77, 0.66 } bg[ACTIVE] = { 0.0, 0.55, 0.55 } bg[PRELIGHT] = { 0.0, 0.55, 0.55 } fg[NORMAL] = "black" fg[SELECTED] = { 0.9, 0.9, 0.9 } fg[ACTIVE] = "black" fg[PRELIGHT] = { 0.9, 0.9, 0.9 } base[INSENSITIVE] = "#777766" text[INSENSITIVE] = { 0.60, 0.65, 0.57 } bg_pixmap[NORMAL] = "background.xpm" bg_pixmap[INSENSITIVE] = "background.xpm" bg_pixmap[ACTIVE] = "background.xpm" bg_pixmap[PRELIGHT] = "<none>" } style "ruler" = "default" { font_name = "helvetica 8" }
样式 'ruler' 继承自 'default' 。通过这种方式,你可以在已有样式的基础上进行扩展。字体与颜色的语法将在下面说明。
如示例所示,可以根据部件的状态为前景和背景指定不同的值。可用状态如下:
NORMAL- 部件的默认状态。
ACTIVE- 部件准备执行操作时的状态。也用于滚动条的滑道(例如
bg[ACTIVE] = "red"会将滚动条滑道设为红色)。已被按下但未释放的按钮处于该状态。 PRELIGHT- 鼠标指针悬停在可操作部件上时的状态 —— 例如鼠标悬停在滚动条滑块或菜单项上。鼠标悬停在未按下的按钮上时,按钮处于该状态。
SELECTED- 用户已选中数据的状态,例如选中文本或列表中选中的项。Emacs 中不使用该状态。
INSENSITIVE- 部件可见但无法正常操作的状态,例如不可点击的按钮、禁用的菜单项。若要将禁用菜单项显示为黄色,可使用:
fg[INSENSITIVE] = "yellow"。
样式声明中可设置的项如下:
bg[state] = color- 指定部件的背景色。注意:可编辑文本不使用 bg,而是使用 base。
base[state] = color- 指定可编辑文本的背景色。在 Emacs 中,该颜色用于文件对话框中文本区域的背景。
bg_pixmap[state] = "pixmap"指定图片背景(代替纯色背景)。pixmap 为图片文件名。GTK+ 支持多种图片格式,包括 XPM、XBM、GIF、JPEG 和 PNG。若想让部件使用与父部件相同的图片,使用 ‘<parent>’。若不想使用任何图片,使用 ‘<none>’。‘<none>’ 可用于取消继承自父样式的背景图片。
不能使用绝对路径指定文件。GTK+ 会在
pixmap_path指定的目录中查找图片。pixmap_path是由冒号分隔的目录列表,放在双引号内,需定义在 gtkrc 文件顶层(不在 style 内部):pixmap_path "/usr/share/pixmaps:/usr/include/X11/pixmaps"fg[state] = color- 指定部件使用的前景色。即菜单与按钮中的文字颜色、滚动条箭头颜色。可编辑文本请使用 text。
text[state] = color- 可编辑文本的颜色。在 Emacs 中,该颜色用于文件对话框的文本区域。
font_name = "font"- 指定部件中文本所用字体。font 为 GTK(或 Pango)格式的字体名,例如 'Sans Italic 10'(见字体)。名称不区分大小写。
指定颜色有三种方式:颜色名称、RGB 三元组或 GTK 风格的 RGB 三元组。有关颜色名称与 RGB 三元组的说明,请参阅「外观颜色」章节。颜色名称需要用双引号括起来,例如:‘"red"’。
- RGB 三元组不需要双引号,例如:#ff0000。
- GTK 风格的 RGB 三元组格式为
{ r, g, b },其中 r、g、b 可以是:- 取值范围在 0–65535 的整数,或
- 取值范围在 0.0–1.0 的浮点数。
附录 E Emacs 29 版本反向更新说明
附录 F Emacs 与 macOS / GNUstep
本节介绍在 GNU/Linux 或其他操作系统上使用基于 GNUstep 库构建的 Emacs,或是在 macOS 上使用原生窗口系统支持的 Emacs 时的相关特性。在 macOS 上,Emacs 可以构建为无窗口系统支持、基于 X11 或使用 Cocoa 界面三种形式;本节仅适用于采用 Cocoa 构建的版本。该版本不支持 macOS 10.6 之前的系统。
GNUstep 是自由软件;macOS 则不是。由于 macOS 是非自由操作系统,它剥夺了用户本应享有的自由,这是一种不公。为了你的自由,我们强烈建议你切换到自由操作系统。
我们在专有操作系统上支持 GNU Emacs,是希望这份对自由的体验能激励用户从中脱离出来。
出于历史与技术原因,Emacs 内部使用术语 Nextstep ,而非 “Cocoa” 或 “macOS”;例如,本节描述的大部分命令与变量都以 'ns-' 开头,即 'Nextstep' 的缩写。NeXTstep 是 NeXT 公司在 1980 年代发布的应用程序接口,Cocoa 是其直接继承者。除 Cocoa 外,还有另一套 NeXTstep 风格的系统:GNUstep,它是自由软件。截至撰写本文时,Emacs 对 GNUstep 的支持仍处于 alpha 阶段(详见 GNUstep 支持),我们希望在未来加以完善。
F.1 macOS 与 GNUstep 下的 Emacs 基本使用
默认情况下, Alt 键和 Option 键等同于 Meta 键。Mac 的 Cmd 键等同于 Super 键,Emacs 提供了一组基于该修饰键的按键绑定,以模仿其他 Mac / GNUstep 应用程序的行为(参见 macOS / GNUstep 下的窗口系统事件)。你可以按常规方式修改这些绑定(参见自定义按键绑定)。修饰键本身也可以自定义,参见 Mac / GNUstep 自定义配置。
S-mouse-1 (Shift + 鼠标左键)会将选区调整到点击位置,作用与 mouse-3 (鼠标右键, mouse-save-then-kill )相同;它不会像普通 Emacs 中那样弹出修改默认字体的菜单(参见文本缩放)。这一改动让 Emacs 的行为更接近其他 Mac / GNUstep 应用。
当你通过菜单、或使用 Cmd-o 、 Cmd-S 绑定打开 / 保存文件时,Emacs 会使用图形化文件对话框输入文件名。但如果你使用标准 Emacs 按键序列(如 C-x C-f ),Emacs 则会使用 迷你缓冲(minibuffer) 输入文件名。
在 GNUstep 环境下,如果处于 X-windows 环境中,你需要使用 Cmd-c (而非 C-w 或 M-w )将文本复制到 X 主选区(primary selection);否则 Emacs 会使用剪贴板选区。同样, Cmd-y (而非 C-y )会从 X 主选区恢复文本,而不是从 kill-ring 或剪贴板。
F.1.1 获取环境变量
许多可能在 Emacs 中运行的程序(如 latex、man)都依赖环境变量的设置。如果 Emacs 从 终端(shell)启动,它会自动继承这些环境变量,其子进程也会从 Emacs 继承。但如果 Emacs 从访达(Finder) 启动,它不属于任何 shell 的子进程,因此不会设置这些环境变量,这常会导致其启动的子进程行为与从终端启动时不同。
对于 PATH 和 MANPATH 变量,macOS 推荐使用系统级方式设置:通过 /etc/paths 文件和 /etc/paths.d 目录。
F.2 Mac / GNUstep 定制
Nextstep 移植版提供了一些专属的自定义选项,例如会影响修饰键、全屏行为等。如需查看所有此类选项,可使用: M-x customize-group RET ns RET 。
F.2.1 修饰键
F.2.1 修饰键
下列变量控制实际修饰键的行为:
ns-alternate-modifierns-right-alternate-modifier- 分别对应左右
Option/Alt键。 ns-command-modifierns-right-command-modifier- 分别对应左右
Command键。 ns-control-modifierns-right-control-modifier- 分别对应左右
Control键。 ns-function-modifier- 对应
Function(fn) 键。
每个变量的值可以是一个符号,用于描述该键的通用用途;也可以是如下格式的列表: (:ordinary symbol :function symbol :mouse symbol) 分别表示该修饰键与普通按键、功能键(如方向键等不产生字符的键)、鼠标点击组合时的行为。
如果 symbol符号 为 control、meta、alt、super 或 hyper 之一,则表示它在 Emacs 中对应的修饰键。如果 symbol符号 为 none,则 Emacs 不会使用该按键,该键保留系统原生行为。例如,macOS 下的 Option 键就会继续用于输入特殊字符。
右侧按键对应的变量(如 ns-right-alternate-modifier )也可设为 left ,表示与对应左侧按键行为相同。
F.2.2 框架变量
ns-use-proxy-icon- 该变量用于控制是否在标题栏显示代理图标。可通过该图标将当前缓冲区对应的文件拖拽到其他应用、打印机、桌面等,与访达(Finder)行为一致。可能需要关闭
tool-bar-mode才能看到代理图标。 ns-confirm-quit- 该变量控制退出 Emacs 时是否显示图形化确认对话框。
ns-auto-hide-menu-bar- 该变量控制选中 Emacs 窗口时是否自动隐藏 macOS 菜单栏。若非 nil,则仅当鼠标移到屏幕顶部时才显示菜单栏。
ns-use-native-fullscreen- 控制使用原生全屏还是非原生全屏。原生全屏仅在 macOS 10.7 及以上 可用。
F.2.3 macOS 触控板 / 鼠标滚轮变量
这些变量仅适用于 macOS 10.7(Lion)及以上 系统。
ns-use-mwheel-acceleration- 该变量控制 Emacs 是否忽略系统鼠标滚轮加速度。当值为
nil时,鼠标滚轮的每一次 “滚动一格” 都精确对应一次滚轮事件。当值为 非 nil(默认值)时,根据用户的输入情况,每一次 “滚动一格” 可能对应多次滚轮事件。 ns-use-mwheel-momentum- 控制 Emacs 是否忽略触控板滚动的惯性效果。为非 nil(默认)时,快速滑动后抬起手指,缓冲区仍会短暂继续滚动。
ns-mwheel-line-height控制触控板滚动灵敏度。苹果触控板以像素而非行为单位滚动,Emacs 会将系统像素值转为行数。设为数字时,表示 Emacs 将多少像素视为一行;为 nil 或非数字时,使用默认行高。
数值越低,触控板越灵敏;数值越高,灵敏度越低。
F.3 macOS / GNUstep 下的窗口系统事件
Nextstep 应用会接收一系列在 X 窗口系统中没有对应物的特殊事件。这些事件以专门定义的按键事件形式发送,不对应任何普通按键序列。在 Emacs 中,这些按键事件可以像普通按键一样绑定到函数上。以下是这些事件的列表:
ns-open-file当其他 Nextstep 应用请求 Emacs 打开文件时触发此事件。典型场景是用户在访达(Finder)中双击文件。默认情况下,Emacs 会新建一个窗口(frame)并在其中打开该文件(
ns-find-file)。一个例外是:如果当前选中的缓冲区是*scratch*,Emacs 会直接在当前窗口中打开该文件。你可以通过修改变量
ns-pop-up-frames来改变 Emacs 对ns-open-file事件的响应方式:默认值 fresh:即上面描述的行为;值为 t:总是在新窗口中打开文件;值为 nil:总是在当前选中窗口中打开文件。ns-open-temp-file- 当其他应用请求 Emacs 打开临时文件时触发。默认处理方式是直接生成一个
ns-open-file事件,行为与上面描述一致。 ns-open-file-line- 部分应用(如 ProjectBuilder、gdb)不仅会请求打开特定文件,还会指定文件中的某一行或多行。Emacs 会打开该文件并高亮显示指定行 (
ns-open-file-select-line)。 ns-power-off- 当用户注销系统而 Emacs 仍在运行,或从应用菜单选择 “退出 Emacs” 时触发此事件。默认行为是保存所有访问过文件的缓冲区。
ns-show-prefs- 当用户从应用菜单选择 “偏好设置” 时触发。默认绑定到
customize命令。
Emacs 还允许用户使用 Nextstep 服务,通过一组以 'ns-service-' 开头、以服务名结尾的命令实现。输入 M-x ns-service- Tab 即可查看这些命令列表。这些函数要么作用于选中的文本(并用结果替换原文本),要么接收字符串参数并返回结果字符串。你也可以使用 Lisp 函数 ns-perform-service 向任意服务传递任意字符串并接收返回结果。注意:新安装的服务可能需要重启 Emacs 才能使用。
F.4 GNUstep 支持
Emacs 可以在 GNUstep 环境下编译并运行,但仍存在一些待解决的问题。感兴趣的开发者可联系 emacs-devel 邮件列表。
附录 G Emacs 与 Haiku 系统
Haiku 是一款类 Unix 操作系统,最初是作为 BeOS 操作系统的重新实现而诞生的。
本附录介绍在 Haiku 系统上,使用Application Kit(Haiku 原生窗口系统)编译的 Emacs 时的特殊特性。此处说明的特性不适用于 Haiku 上无窗口系统支持或基于 X11 编译的 Emacs。
G.1 Haiku 系统下的安装与启动
在 Haiku 系统中安装 Emacs 时,会有两个可执行文件被复制到二进制目录,二者除部分文件系统标识元数据外完全相同。第一个是普通 Emacs 可执行文件 emacs ;第二个是 Emacs,它包含图标与应用 “signature签名”,帮助系统识别其支持的文件类型与关联窗口,从而让它能接收文件类型关联,并可直接从 Tracker(Haiku 文件管理器)打开文件。
Emacs 内部会设置若干文件属性,使系统同一时间只允许运行一个 Emacs 实例。在建立显示连接时会检查这一约束;如果已有显示连接存在,任何试图新建连接的 Emacs 进程都会被强制终止。
出于上述及其他原因:
- 应使用 Emacs 启动图形界面(GUI)会话;
- 应使用 emacs 启动其他类型的 Emacs 会话。
在 Haiku 下,Emacs 无法接收 Hyper 这类特殊修饰键,也无法接收由系统 Super 键映射生成的带重音字符。
默认按键映射:
- 系统 Option 键 → Emacs Super 修饰键
- 系统 Command 键 → Emacs Meta 修饰键
- 系统 Control 键 → Emacs Control 修饰键
- 系统 Shift 键 → Emacs Shift 修饰键
在标准 PC 键盘上,Haiku 应会将这些键映射到类 GNU 系统常用的位置,但可能需要调整系统配置。
你可以通过下面这些变量,自定义系统修饰键与 Emacs 修饰键的对应关系:
haiku-meta-keysym- 系统中会被 Emacs 当作 Meta 键的修饰键,默认为 command。
haiku-control-keysym- 系统中会被 Emacs 当作 Control 键的修饰键,默认为 control。
haiku-super-keysym- 系统中会被 Emacs 当作 Super 键的修饰键,默认为 option。
haiku-shift-keysym- 系统中会被 Emacs 当作 Shift 键的修饰键,默认为 shift。
每个变量的值可以是:command、control、option、shift 或 nil。若为 nil 或其他无效值,则使用默认映射。
在 Haiku 上,Emacs 默认使用系统原生提示框(tooltip)。这类提示框响应更快,但无法显示文本属性或字体样式(face)。如果你需要这些特性,可将变量 use-system-tooltips 设为 nil ,Emacs 会改用自身实现的提示框。
与 X 窗口系统不同,Haiku 不提供系统级资源数据库。由于很多重要选项通过 X resource 配置(见X 窗口系统选项与资源),Emacs 提供了模拟兼容层:启动时,Emacs 会加载用户配置目录(通常是 /boot/home/config/settings )里名为 GNU Emacs 的文件。该文件应为扁平化系统消息格式,键与值均为字符串,对应属性名与属性值。可使用 xmlbmessage 工具生成此类文件。
如果变量 haiku-debug-on-fatal-error 为非 nil(默认为 t ),Emacs 在收到致命信号时会自动启动系统调试器。若你的系统无法使用 GDB,提交 Bug 时请附上系统调试器生成的报告。
G.2 Haiku 系统下的字体后端与选择
在为 Haiku 窗口系统支持编译时,Emacs 支持多种不同的字体后端,具体支持的子集取决于 Emacs 配置时已存在并启用的依赖库。你可以通过在启动 Emacs 的命令行中指定: -xrm Emacs.fontBackend:后端名 来选择使用哪种字体后端,其中 “后端名” 是下面列出的其中一种;也可以通过修改 font-backend 框架参数来为单个框架指定字体后端。
其中有两个后端: ftcr 和 ftcrhb ,与它们在 X Window 系统中的对应实现完全相同。此外还有一个 Haiku 专用后端,名为 haiku,它使用 Haiku 应用服务器来绘制字体,但目前无法显示彩色字体或 Emoji。
附录 H Emacs 与 Android 系统
Android 是由开放手机联盟(Open Handset Alliance)开发的移动操作系统。本节介绍在搭载 Android 2.2 或更高版本 的安卓设备上使用 Emacs 时的特殊事项。
安卓设备通常依靠触摸屏、数位板或虚拟键盘进行用户输入。关于在 Emacs 中使用这类设备的更多信息,参见触摸屏输入与虚拟键盘。
H.1 Android 版本历史
Android 是由 开放手机联盟(Open Handset Alliance) 开发的、面向移动设备的操作系统。该联盟由一批致力于开发可运行统一软件套件的手机设备的公司组成。Android 据称是自由软件。
与昔日的 X 联盟类似,开放手机联盟认为,“开放”(即定期发布 Android 源代码)仅仅是用来提升 Android 平台普及度的工具。计算机企业通常会开发专有软件,开放手机联盟中的企业也不例外 —— 设备上预装的绝大多数 Android 版本都属于 专有软件 ,因为它们包含专有组件,而这些组件往往连用户都无法替换。
Android 的设计并未考虑尊重用户的自由。几乎所有版本的 Android(包括一些号称是自由软件的版本)都支持 数字限制管理(DRM) 技术,这类技术的目的就是限制用户在自己的设备上复制、传输媒体文件的能力。大多数 Android 设备还预装了谷歌的专有应用,这些应用是系统运行以及许多其他 Android 应用正常工作所必需的。
因此,从实际使用的角度来看,我们必须将 Android 视为专有软件。这是一种不公。如果你正在使用 Android,我们强烈建议你切换到自由操作系统,哪怕只是为了你自身的自由。
我们之所以在专有操作系统上支持 GNU Emacs,是因为希望这份自由的体验,能激励用户最终摆脱这些专有系统的束缚。
H.2 在 Android 上启动 Emacs
Android 设备上的 Emacs 并非通过源码或包管理器安装,而是在其他操作系统中为 Android 平台编译,将生成的二进制文件打包为归档文件,再传输到系统中完成安装。
安装完成后,系统会在桌面(亦称 “主屏幕”)添加应用图标。点击该图标即可启动 Emacs。
启动过程中,Emacs 会在系统日志缓冲区输出信息;若要在启动时查看该缓冲区,需要在另一台计算机上安装 Android 调试桥(adb) 工具。
在 Android 系统中开启「USB 调试」功能,并通过 USB 连接到已安装 adb 工具的另一台设备后,可在该设备上运行以下命令查看日志:
adb logcat | grep -E "(android_run_debug_thread|[Ee]macs)"
假设 adb 工具已安装在 GNU/Linux 或类 Unix 系统中,可按以下步骤连接设备:
- 打开系统设置应用中的「关于设备」页面,连续点击「版本号」或「内核版本」5~7 次,开启设备的「开发者选项」。
- 打开设置应用中「系统」分类下的「开发者选项」设置页面。
- 开启「USB 调试」开关。
- 用 USB 数据线一端连接设备,另一端连接计算机 USB 接口。
- 在计算机上运行 adb shell 命令。此时会失败或卡住,因为尚未授予计算机访问该设备的权限。
- 在设备上确认弹出的授权对话框,允许当前计算机访问。
根据 Android 与 adb 版本的不同,建立连接的方式可能存在差异。更多细节可参考官方文档:https://developer.android.com/studio/command-line/adb
Emacs 启动后,直接以异步 Shell 命令的方式运行 logcat 命令(参见「从 Emacs 运行 Shell 命令」)即可显示日志缓冲区内容。
由于无法从其他 Android 程序直接启动 emacsclient (参见「将 Emacs 作为服务器使用」),Emacs 为 emacsclient 提供了一层封装,并将其注册为系统中可打开任意文件的应用。
当使用该封装程序打开文件时,它会以 --reuse-frame 、 --timeout=10 、 --no-wait 及待打开文件名作为参数调用 emacsclient 。成功后,焦点会切换到已打开的 Emacs 窗口。
但若启动封装程序时 Emacs 尚未运行,则会先启动 Emacs 并将文件作为参数传入。注意:如果启动后的 Emacs 没有开启 Emacs 服务器,后续再用该封装打开文件将会失败。
部分文件会以 内容标识符(content identifier) 的形式交给 Emacs,系统通过独立于普通文件系统 API 的方式提供访问。Emacs 使用 /content/by-authority 和 /content/by-authority-named 这两个伪目录来访问这类文件。不要对这些目录的内容做任何假设,也不要手动尝试打开其中的文件。
Android 4.3 及更早版本不支持此功能,这类系统会先将文件复制到临时目录再打开。
除普通文本文件外,Emacs 还将其 emacsclient 封装注册为可打开 org-protocol 链接的程序(参见《Org 手册》中的「协议」)。
此外,该封装程序还被注册为可处理 mailto URI 的邮件发送程序;当被调用来打开此类链接时,会调用 message-mailto 函数并将该 URI 作为第一个参数。此功能在 Emacs 服务器未启动时无效。
H.3 Emacs 在 Android 上可访问的文件
Emacs 在 Android 系统中会暴露一个特殊目录:目录名为 /assets ,其中包含 etc、lisp 和 info 目录,这些目录在 GNU 与类 Unix 系统中通常安装在 /usr/share/emacs 下。在 Android 系统上,ls 的 Lisp 模拟层(参见 MS-Windows 上的 ls 模拟)默认也会启用,因为系统自带的 ls 二进制文件因厂商不同而异,通常不支持 Emacs 所需的全部功能。已知部分 Android 系统自带的 ls 甚至不支持 -l 参数。
该目录存在的原因是:Android 在解压应用安装包时不会将其内容释放到文件系统中,而是要求像 Emacs 这样的程序通过专用的「资源管理器(asset manager)」接口读取其内容。这种实现方式带来了以下特性:
- 子进程(如 ls)无法从
/assets目录运行;如果你将current-directory设置为/assets、/content/storage或其子目录并尝试运行子进程,进程会从主目录启动。 /assets或/content目录内不存在.和..目录。/assets目录中的文件一律为只读,且每次打开时可能会被多次读入内存。
除 /assets 目录外,Android 应用通常还可以访问另外四个目录:
- 应用数据目录同时作为 Emacs 的主目录,始终可读写。
- 应用库目录启动时会自动添加到
exec-path并设为exec-directory,其中包含与 Emacs 一同发布的工具可执行文件。 - 外部存储目录用户在系统设置中为 Emacs 开启「文件和媒体」权限后,Emacs 即可访问。
- Android 5.0 及以上版本的文档提供者目录这些目录独立于 Unix 虚拟文件系统存在,存放由外部应用提供的文件(参见:在 Android 上从其他程序访问文件)。
尽管常规 Android 系统的(通常只读)根目录下并不存在名为 content 或 assets 的真实文件,但如果你使用的 Android 已被定制,可能需要访问同名真实文件。这些文件会与上述特殊目录冲突,但仍可通过相对于根目录的 “父” 目录书写路径来访问,示例如下: /../content 、 /../assets
外部存储目录位于 /sdcard 。其他目录没有固定位置(详见下文),不过应用数据目录通常被符号链接到: /data/data/org.gnu.emacs/files
旧版 Android 会将应用库目录放在应用数据目录父目录下名为 lib 的位置。如今,该目录通常位于 /data/app 下一个随机生成名称的目录中。
为了方便与 Emacs 共享同一用户 ID 的应用内脚本(它们无法访问 exec-directory 变量),Emacs 启动时会尽力将应用库目录符号链接到传统位置(应用数据目录的父目录下)。
如果重新安装了 Emacs 导致应用库目录位置改变,下次由系统启动 Emacs 时,该符号链接会自动更新指向新位置。
在运行极旧版 Linux 内核(2.6.29)的 Android 设备上,Emacs 需要在临时文件目录中创建以 temp~unlinked 开头的文件,才能读取资源文件。 请勿手动创建此类名称的文件 ,否则可能被覆盖或删除。
在 Android 11 及更高版本中,系统限制应用使用 open、readdir 等文件相关系统调用访问 /sdcard 目录下的文件。
这项限制被称为分区存储(Scoped Storage),据称可提升系统安全性。但遗憾的是,这也意味着即使拥有权限,Emacs 也无法访问这些目录下的文件。好在开放手机联盟版的 Android 允许按应用关闭此限制;系统设置中对应的选项路径为:
System -> Apps -> Special App Access -> All files access -> Emacs 系统 → 应用 → 特殊应用权限 → 所有文件访问 → Emacs
在你适当开启或关闭此设置,并为 Emacs 授予「文件和媒体」权限后,Emacs 即可正常访问 /sdcard 下的文件。部分定制 / 专有版 Android 可能没有这些设置项。
H.4 在 Android 上从其他程序访问文件
Android 5.0 引入了一类新程序:文档提供者(document provider)。这类程序是小型服务,可独立于资源管理器与 Unix 文件系统,提供对其自身文件的访问。Emacs 支持访问它们提供的文件与目录,并将这些文件放置在 /content/storage 目录下。
在 Emacs 获得对这些目录的访问权限之前,必须先申请访问权限。这一步通过运行命令 android-request-directory-access (参见按名称运行命令)完成,该命令会弹出文件选择对话框。
如果从对话框中选中一个目录,其内容随后会在新目录 /content/storage/authority/id 中可用:其中 authority 是文档提供者的名称, id 是文档提供者为该目录分配的唯一标识符。
当不再需要此类目录时,可以将其路径传给命令 android-relinquish-directory-access 来删除。
在这些目录中创建子进程时,会应用与 /assets 目录相同的限制(参见 Emacs 在 Android 上可以访问哪些文件),因为它们并不存在于 Unix 文件系统中。此外,虽然 Emacs 通常可以在这些目录中写入和创建文件,但无法创建符号链接或硬链接。
由于文档提供者可能会执行耗时的网络操作来获取文件内容,因此在这些目录中的文件访问操作可能会花费较长时间。
H.5 在 Android 下运行 Emacs
从用户角度来看,Android 大致是一个单用户操作系统;但从应用与 Emacs 的角度来看,该系统中却存在数量极多的 “用户”。
每个应用都运行在属于自己的用户身份下,其主目录被设置为自身的应用数据目录(参见《Emacs 在 Android 上可以访问哪些文件》)。30
同时,每个应用都被禁止访问大量系统目录以及其他应用的应用数据目录。
Emacs 发行版中还包含若干二进制程序。这些可执行文件在打包时会被作为库文件放入库目录,因为否则系统在安装 Emacs 时不会将它们解压。这意味着:在子进程中启动这些程序时,Lisp 代码不能直接写 ctags 或 emacsclient,而必须在命令行中指定 libctags.so 或 libemacsclient.so 。要确定具体使用哪个名称,可以查看下列变量的值:ctags-program-name、etags-program-name、hexl-program-name、emacsclient-program-name、movemail-program-name、ebrowse-program-name 和 rcs2log-program-name。参见《Emacs Lisp 参考手册》中的 “子进程创建”。
包含 Emacs 启动文件的 /assets 目录,对并非由 zygote(负责启动应用的系统服务)直接创建的进程是不可访问的。由于必需的 Lisp 文件位于 /assets 目录中,这就导致 Emacs 无法以子进程的方式启动自身。Emacs 附带了一个名为 libandroid-emacs.so 的特殊二进制文件,安装在库目录下,它会尽最大努力启动 Emacs 以 批处理模式(batch mode) 运行 Lisp 代码。该实现方式是参考 Android 源码设计的,并未得到 Android 兼容性定义文档的认可,因此实际效果可能因设备而异。
即使预先知道 libandroid-emacs.so 的路径,想要在非现有 Emacs 会话子进程的环境中运行 Emacs,仍需要做特殊准备,因为必须让它能正确识别已安装应用包内及已解压的资源与共享库位置。系统命令: pm path org.gnu.emacs 。会输出应用包的位置,但该命令必须以特殊方式调用才能满足系统对用户应用创建的伪终端与包管理器等系统服务之间通信的限制,也就是说,需要将标准 I/O 流重定向到真实文件或管道。获取到该值后,必须将其写入环境变量 EMACS_CLASS_PATH ,这样下面这段示例 shell 脚本就可以安装为 emacs 命令并在任意可访问位置使用:
#!/system/bin/sh package_name=`pm path org.gnu.emacs 2>/dev/null </dev/null \ | sed 's/^package://'` emacs= EMACS_CLASS_PATH=$package_name for libdir in `dirname $package_name`/lib/*; do ld_path=${ld_path:+${ld_path}:}$libdir test -x "$libdir"/libandroid-emacs.so \ && emacs="$libdir"/libandroid-emacs.so done export EMACS_CLASS_PATH test -x "$emacs" || exit 1 exec $emacs "$@"
Android 10 及更高版本还出于安全考虑,禁止 Emacs 在应用数据目录内运行可执行文件。在这些系统上,Emacs 默认会使用一种 兼容方案 :通过一个额外子进程实现可执行文件加载器,并对所有子进程进行进程追踪,以此运行所有子进程。但这在某些情况下可能引发问题。此时可以将变量 android-use-exec-loader 设为 nil 来关闭该兼容方案。
当该兼容方案生效时,通过 process-id 函数获取的进程 ID 是加载器进程的 ID;其子进程与加载器属于同一个进程组。因此 interrupt-process 等相关函数仍可正常工作,但将 process-id 返回的 PID 用于其他用途则不可行。
该进程追踪机制带来一个影响:下级 shell 中的任务控制(参见 “交互式子 shell”)无法停止进程,发送给 Emacs 创建的子进程的 SIGSTOP 信号也不会生效。
此外,Android 12 会在 Emacs 处于后台时,终止持续占用 CPU 的子进程。系统每隔五分钟判断哪些进程占用 CPU 过多,并终止占用 CPU 时间最长的进程。
Android 12.1 和 Android 13 提供了关闭此行为的选项:开启 “USB 调试”(参见《在 Android 上启动 Emacs》),将 Android 设备连接到另一台计算机,然后执行:
adb shell "settings put global settings_enable_monitor_phantom_procs false"
系统层面的 “语言与输入法” 偏好设置不会影响程序的 C 区域设置,但 Emacs 启动时会参考这些设置:根据选中的语言与区域变体生成区域名称,并据此选择语言环境(参见 “语言环境”),这不会覆盖 LANG 或其他与区域相关的环境变量。以这种方式设置的语言环境所使用的编码系统一律为 utf-8-unix 。
在 Android 5.0 及更高版本上启动 Emacs 时,环境变量 LANG (见通用变量)会被设为 en_US.utf8 ,这能让链接到 Android C 库的子进程正常输出内容。更早的 Android 版本完全不支持区域设置,因此该变量会被设为 C。
系统将应用进程视为可回收的实体。当所有 Emacs 窗口进入后台后,为节省系统资源,Emacs 随时可能被系统终止。
在 Android 7.1 及更早版本中,Emacs 将自身标记为 “后台服务”,这会让系统尽量不杀死 Emacs,除非内存极度紧张。
Android 8.0 移除了后台服务的这种特殊待遇。但 Emacs 提供了兼容方案:系统会将创建 常驻通知 的应用视为正在执行活跃任务,并避免杀死这类应用。因此在这些系统上,Emacs 运行期间会一直显示一条常驻通知。
Android 13 之前,Emacs 显示通知不需要权限。Android 13 及更高版本中,通知会在用户授予 Emacs 通知权限后才显示。即便如此,仅尝试显示通知就足以避免被系统 “突然杀死”;通知是否实际显示不影响 Emacs 在后台运行的能力,关闭通知也不会带来任何负面影响。
但这不能保证系统一定不会杀死 Emacs。尽管开放手机联盟的 Android 示例实现行为正常,但很多厂商在其定制版 Android 中对后台程序增加了额外限制。相关厂商列表及有时可用的规避方案可参见:https://dontkillmyapp.com/。
Android 还定义了一套权限系统,用于决定允许 Emacs 访问哪些系统服务。应用必须声明所需的权限;具体权限如何生效,则取决于所使用的 Android 版本:
在较新版本的 Android 系统中(例如 Android 6.0 及更高版本),Emacs 在安装时仅会获得以下权限,具体取决于已安装的 Android 版本是否包含对应权限:
android.permission.ACCESS_ADSERVICES_AD_ID android.permission.ACCESS_ADSERVICES_ATTRIBUTION android.permission.ACCESS_ADSERVICES_CUSTOM_AUDIENCE android.permission.ACCESS_ADSERVICES_TOPICS android.permission.ACCESS_LOCATION_EXTRA_COMMANDS android.permission.ACCESS_NETWORK_STATE android.permission.ACCESS_NOTIFICATION_POLICY android.permission.ACCESS_WIFI_STATE android.permission.AUTHENTICATE_ACCOUNTS android.permission.BLUETOOTH android.permission.BLUETOOTH_ADMIN android.permission.BROADCAST_STICKY android.permission.CALL_COMPANION_APP android.permission.CHANGE_NETWORK_STATE android.permission.CHANGE_WIFI_MULTICAST_STATE android.permission.CHANGE_WIFI_STATE android.permission.CREDENTIAL_MANAGER_QUERY_CANDIDATE_CREDENTIALS android.permission.CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS android.permission.CREDENTIAL_MANAGER_SET_ORIGIN android.permission.DELIVER_COMPANION_MESSAGES android.permission.DETECT_SCREEN_CAPTURE android.permission.DISABLE_KEYGUARD android.permission.ENFORCE_UPDATE_OWNERSHIP android.permission.EXPAND_STATUS_BAR android.permission.FLASHLIGHT android.permission.FOREGROUND_SERVICE android.permission.FOREGROUND_SERVICE_CAMERA android.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE android.permission.FOREGROUND_SERVICE_DATA_SYNC android.permission.FOREGROUND_SERVICE_FILE_MANAGEMENT android.permission.FOREGROUND_SERVICE_HEALTH android.permission.FOREGROUND_SERVICE_LOCATION android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION android.permission.FOREGROUND_SERVICE_MICROPHONE android.permission.FOREGROUND_SERVICE_PHONE_CALL android.permission.FOREGROUND_SERVICE_REMOTE_MESSAGING android.permission.FOREGROUND_SERVICE_SPECIAL_USE android.permission.FOREGROUND_SERVICE_SYSTEM_EXEMPTED android.permission.GET_PACKAGE_SIZE android.permission.GET_TASKS android.permission.HIDE_OVERLAY_WINDOWS android.permission.HIGH_SAMPLING_RATE_SENSORS android.permission.INTERNET android.permission.KILL_BACKGROUND_PROCESSES android.permission.MANAGE_ACCOUNTS android.permission.MANAGE_OWN_CALLS android.permission.MODIFY_AUDIO_SETTINGS android.permission.NFC android.permission.NFC_PREFERRED_PAYMENT_INFO android.permission.NFC_TRANSACTION_EVENT android.permission.PERSISTENT_ACTIVITY android.permission.QUERY_ALL_PACKAGES android.permission.READ_BASIC_PHONE_STATE android.permission.READ_INSTALL_SESSIONS android.permission.READ_NEARBY_STREAMING_POLICY android.permission.READ_PROFILE android.permission.READ_SOCIAL_STREAM android.permission.READ_SYNC_SETTINGS android.permission.READ_SYNC_STATS android.permission.READ_USER_DICTIONARY android.permission.RECEIVE_BOOT_COMPLETED android.permission.REORDER_TASKS android.permission.REQUEST_COMPANION_PROFILE_GLASSES android.permission.REQUEST_COMPANION_PROFILE_WATCH android.permission.REQUEST_COMPANION_RUN_IN_BACKGROUND android.permission.REQUEST_COMPANION_START_FOREGROUND_SERVICES_FROM_BACKGROUND android.permission.REQUEST_COMPANION_USE_DATA_IN_BACKGROUND android.permission.REQUEST_DELETE_PACKAGES android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS android.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE android.permission.REQUEST_PASSWORD_COMPLEXITY android.permission.RESTART_PACKAGES android.permission.RUN_USER_INITIATED_JOBS android.permission.SET_WALLPAPER android.permission.SET_WALLPAPER_HINTS android.permission.SUBSCRIBED_FEEDS_READ android.permission.SUBSCRIBED_FEEDS_WRITE android.permission.TRANSMIT_IR android.permission.UPDATE_PACKAGES_WITHOUT_USER_ACTION android.permission.USE_BIOMETRIC android.permission.USE_CREDENTIALS android.permission.USE_EXACT_ALARM android.permission.USE_FINGERPRINT android.permission.USE_FULL_SCREEN_INTENT android.permission.VIBRATE android.permission.WAKE_LOCK android.permission.WRITE_PROFILE android.permission.WRITE_SMS android.permission.WRITE_SOCIAL_STREAM android.permission.WRITE_SYNC_SETTINGS android.permission.WRITE_USER_DICTIONARY
其余权限必须由用户在系统设置应用中手动授予。具体操作方式因设备而异,详情请咨询你的设备制造商。
在 Android 5.1 及更早版本中,Emacs 在安装时会自动获得其申请的以下权限:
ndroid.permission.ACCESS_COARSE_LOCATION android.permission.ACCESS_FINE_LOCATION android.permission.BODY_SENSORS android.permission.CALL_PHONE android.permission.CAMERA android.permission.CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD android.permission.GET_ACCOUNTS android.permission.POST_NOTIFICATIONS android.permission.PROCESS_OUTGOING_CALLS android.permission.READ_CALENDAR android.permission.READ_CALL_LOG android.permission.READ_CELL_BROADCASTS android.permission.READ_CONTACTS android.permission.READ_EXTERNAL_STORAGE android.permission.READ_PHONE_NUMBERS android.permission.READ_PHONE_STATE android.permission.READ_SMS android.permission.RECEIVE_MMS android.permission.RECEIVE_SMS android.permission.RECEIVE_WAP_PUSH android.permission.RECORD_AUDIO android.permission.REQUEST_INSTALL_PACKAGES android.permission.SEND_SMS android.permission.SMS_FINANCIAL_TRANSACTIONS android.permission.SYSTEM_ALERT_WINDOW android.permission.WRITE_CALENDAR android.permission.WRITE_CALL_LOG android.permission.WRITE_CONTACTS android.permission.WRITE_EXTERNAL_STORAGE android.permission.WRITE_SETTINGS android.permission.ACCESS_LOCATION_EXTRA_COMMANDS android.permission.ACCESS_NETWORK_STATE android.permission.ACCESS_WIFI_STATE android.permission.BLUETOOTH android.permission.BLUETOOTH_ADMIN android.permission.BROADCAST_STICKY android.permission.CHANGE_NETWORK_STATE android.permission.CHANGE_WIFI_MULTICAST_STATE android.permission.CHANGE_WIFI_STATE android.permission.DISABLE_KEYGUARD android.permission.EXPAND_STATUS_BAR android.permission.FLASHLIGHT android.permission.GET_PACKAGE_SIZE android.permission.GET_TASKS android.permission.INTERNET android.permission.KILL_BACKGROUND_PROCESSES android.permission.MODIFY_AUDIO_SETTINGS android.permission.NFC android.permission.PERSISTENT_ACTIVITY android.permission.QUERY_ALL_PACKAGES android.permission.READ_BASIC_PHONE_STATE android.permission.READ_SYNC_SETTINGS android.permission.READ_SYNC_STATS android.permission.READ_USER_DICTIONARY android.permission.RECEIVE_BOOT_COMPLETED android.permission.REORDER_TASKS android.permission.REQUEST_DELETE_PACKAGES android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS android.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE android.permission.RESTART_PACKAGES android.permission.SET_WALLPAPER android.permission.SET_WALLPAPER_HINTS android.permission.TRANSMIT_IR android.permission.VIBRATE android.permission.WAKE_LOCK android.permission.WRITE_SYNC_SETTINGS android.permission.WRITE_USER_DICTIONARY
这些权限中大部分 Emacs 本身并不会使用,但声明它们是为了方便其他程序使用;例如,访问联系人的权限对 EUDC 可能有用。
H.6 Android 窗口系统
Android 的窗口系统比较特殊:所有窗口对应用来说都是最大化或全屏状态,通常同一时间只显示一个窗口。在大屏设备上,系统允许屏幕内同时平铺最多四个窗口;而在模拟器或配置为 “桌面” 模式的系统中,则和普通桌面窗口管理器一样支持自由调整大小的窗口。
窗口(在系统术语中称为Activity)在创建后不会永久存在。为节省内存,系统可能会暂停不可见的窗口,假定程序会将其内容保存到磁盘,等用户从任务切换器重新选中时再恢复。此外,系统会在 Emacs 启动时创建一个享有特殊待遇的窗口,Emacs 需要适配这一设计。
Emacs 对窗口管理的目标是:尽可能让帧(frame)的行为与普通窗口系统(如 X Window)暴露给 Lisp 的行为保持一致。这一目标的实现程度,取决于所安装 Android 版本的窗口管理能力,以及系统对非活动窗口的策略。当必须向系统策略妥协时,Emacs 更倾向于销毁无 Activity 可显示的帧,而不是保留它们;唯一例外是初始帧,它会显示在启动时创建的 Activity 中,只要 Emacs 还在运行,就可以打开并识别。
Android 5.0 及更高版本支持完善的窗口管理实现:帧与显示它们的 Activity 一一对应。在任务切换器中删除 Activity 会直接影响对应帧,反之亦然。只有两个例外:
- 当系统因无操作暂停某个仍留在任务切换器中的 Activity 时,如果在其暂停状态下从任务切换器移除它,并不会删除内部的帧,因为这种情况下 Emacs 收不到 Activity 被删除的通知。该帧会在下一次窗口管理操作检查窗口列表时被删除。同样,显示某个帧的非活动 Activity 不会随帧立即删除,除非从窗口列表选中它或下次检查窗口列表。
- 除初始帧外,任何帧如果在后台闲置 4~6 小时,可能会被系统以 “清理任务切换器” 为由删除。初始帧不受此影响,因为它可以不从任务切换器重新打开。但由于这种机制无法与用户正常移除 Activity 区分,初始帧在最后一次活动超过 4 小时后,也会忽略这类删除行为。
Android 4.4 及更早版本提供的接口远不足以实现完整的窗口管理。在这些系统上,Emacs 使用一套相当原始的机制:除初始帧外,其余帧所在的 Activity 被暂停时就会删除;同一时间只显示一个 Activity(不含启动时创建的那个);未绑定的帧会显示在第一个可用的空闲 Activity 中。
Emacs 在 Android 上只支持有限的图形界面功能,限制如下:
- 不支持滚动条,因为在 Android 设备上几乎无用。
- 不支持这些帧参数:alpha、alpha-background、z-group、override-redirect、mouse-color、title、wait-for-wm、sticky、undecorated。(见框架参数)
- Android 4.0 及更早版本中,顶层帧的 fullscreen 参数永远是最大化;更高版本可真正全屏。
Emacs 在 Android 平台上并未实现 X Window 系统所支持的全部与选择(selection)相关的功能。例如,仅支持 CLIPBOARD(剪贴板)和 PRIMARY(主选择区)两种选择(参见《图形界面下的剪切与粘贴操作》),且 Emacs 只能将选择内容设置为纯文本格式。
此外,Android 系统本身对 Emacs 能访问的选择数据有一定限制:
- Android 2.3 及更早:
gui-selection-owner-p对剪贴板选择永远返回nil。 - Android 3.0 到 9.0:Emacs 可随时访问剪贴板,
gui-selection-owner-p返回准确结果。 - Android 10.0 及更高:只有 Emacs 某个帧获得输入焦点时才能访问剪贴板,且
gui-selection-owner-p对剪贴板永远返回nil。
由于 Android 本身没有 主选择(primary selection) 概念,Emacs 会提供一层模拟。这意味着无法通过剪切粘贴将主选择内容传递给其他应用。
音量键默认被 Emacs 占用,用于在无实体键盘时退出 Emacs(参见:在虚拟键盘下使用 Emacs)。如果你想让音量键正常调节音量,可以将变量 android-pass-multimedia-buttons-to-system 设为非 nil。注意:这样就不能再用音量键退出 Emacs,通常更简单的做法是拉下通知栏或其他界面,让 Emacs 暂时失去焦点再按音量键。
在 Android 6.0 及更高版本中,当 Emacs 未获得输入焦点时,无法显示对话框(参见 “使用对话框”)。如果你需要此功能,可以通过授予 Emacs 显示在其他应用之上的权限来恢复。
System -> Apps -> Emacs -> More -> Display over other apps 系统 → 应用 → Emacs → 更多 → 显示在其他应用上层
物理修饰键与按键事件中 Emacs 上报的修饰键(参见 “修饰键”)是直接对应的,只有一个例外:如果按下键盘上的 Alt 键,Emacs 会将其当作 Meta 修饰键上报,反之亦然。之所以有这种特殊处理,是因为大多数键盘没有专门的 Meta 键,而 Alt 修饰键在 Emacs 中很少直接使用。
注意:Android 里 Super 修饰键有另一个名字:在 Android 键盘与设置的键盘映射菜单中叫做 SYM。
Android 输入法常会在事件过滤阶段悄悄丢弃包含 C-SPC 的按键序列,尽管它们通常用不到这类按键。默认情况下,Emacs 会在这些按键被输入法过滤前拦截它们。
如果不需要此功能(例如输入法将 C-SPC 用作切换语言快捷键),可以将变量 android-intercept-control-space 设为 nil 关闭。
Android 系统内的键盘响铃以振动形式实现:每次响铃都会振动若干毫秒。振动时长可通过变量 android-keyboard-bell-duration 自定义,范围 10~1000 毫秒。
Android 上无法自动检测显示器的色彩特性。如果要让 Emacs 按灰度或单色显示器的真实视觉属性显示 face 和图片,需要将变量 android-display-planes 设为合适的值:
- 灰度显示器设为 8(强制所有颜色用 256 级灰度渲染)
- 单色显示器设为 1
由于该变量在建立显示连接时就会处理,自定义必须放在 early-init.el 中才会生效。见The Early Init File
该变量的值不影响字体驱动中的抗锯齿,因为单色显示器仍然希望 Emacs 提供抗锯齿文本,再由显示驱动处理为位图。
H.7 Android 下的字体后端与选择
Emacs 在 Android 平台支持两种字体后端,分别命名为 sfnt-android 和 android 。
启动时,Emacs 会枚举以下目录中的所有 TrueType 格式字体: /system/fonts 、 /product/fonts ,以及 Emacs 主目录下的 fonts 目录(用户字体目录)。Emacs 假定系统中始终存在名为 “Droid Sans Mono” 的字体,并默认使用该字体。这些字体由 sfnt-android 字体驱动负责渲染。
该字体驱动目前暂不支持 OpenType 字体与彩色字体,因此 Emacs 只能使用系统已安装字体中的一部分。如果你希望解除这一限制,可邮件联系:[email protected]。
若 sfnt-android 字体驱动完全找不到任何字体,Emacs 会回退到 android 字体驱动。由于 Android 系统提供的字体度量信息存在限制与精度问题,该驱动效果较差。此时 Emacs 会使用系统配置的「等宽(Monospace)」字体,该字体通常仍为 Droid Sans Mono。
与 X 窗口系统类似,Emacs 在 Android 下也支持可变字体(也称作 TrueType GX 字体、变量字体、多主字体)。这类字体可在单个字体文件中提供多种样式(如粗体、斜体等)。
当检测到用户安装的可变字体时:
- 此前已识别的同样式字体会被停用;
- 若新可变字体提供的样式是旧可变字体的超集,则同名族的旧可变字体也会被忽略。
当检测到普通字体时:
- 同名族、同样式的旧普通字体会被移除;
- 同名族的可变字体将不再用于提供该样式。
Emacs 通常会依赖以下字体族:Monospace、Monospace Serif、Sans Serif、DejaVu Serif。由于 Android 系统并未提供这些名称的字体,Emacs 会自动将对这些字体的请求,替换为 Android 自带的对应字体族。
若要修改需要被替换的字体族或替换目标字体族,可修改变量 sfnt-default-family-alist ,然后重启 Emacs。注意:通常无需这样做,更推荐直接修改默认 face 或变宽 face 的定义来定制外观(参见「定制 Face」)。
H.8 Android 启动问题排查
由于 Android 没有命令行,启动 Emacs 时通常无法指定命令行参数。如果你在 Emacs 初始化文件中配置出错,导致 Emacs 完全无法启动,情况会很棘手,因为系统一般会禁止其他应用访问 Emacs 的主目录。参见「初始选项」。
不过,Emacs 可以通过一个专用设置界面,以等价于 --quick 或 --debug-init 参数的方式启动:
- Android 7.0 及以上版本:可在系统设置的 Emacs「应用信息」页面中找到该选项。
- 旧版本 Android:桌面会单独显示一个名为「Emacs options」的图标。
具体操作方式因设备而异,可查阅你的设备厂商说明。
关于转储文件(dump file)
Emacs 在设备上首次启动时,会花费一定时间加载自带的预编译 Lisp 文件,并在 files 目录生成一个转储文件(dump file)见初始化选项,其中包含该 Emacs 副本的唯一标识。
再次启动时,Emacs 会直接加载该转储文件中的数据,大幅缩短启动时间。
如果转储文件因意外损坏,可能导致 Emacs 崩溃。此时可通过上面提到的设置界面删除 Emacs files 目录中的转储文件来修复。
修复损坏的安装(Android 4.4 及以上)
Emacs 提供了另一种修复方案:它会向外暴露一个 documents provider(文档提供者),用于访问 Emacs 主目录内容,其他文件管理器均可访问。
如果你能在设备自带的文件管理器中打开该文档提供者,就可以直接:重命名、删除、编辑。你的初始化文件或转储文件,从而修复启动问题。
H.9 在 Android 上安装额外软件
Android 默认系统只附带极其有限的类 Unix 命令行工具。目前已有多个项目用于扩充这些工具,从对 Unix 命令行工具的改进复刻,到提供大量免费 GNU 与 Unix 软件的软件包仓库,选择丰富。
BusyBox 将 Unix 工具以及 wget 等部分常用 GNU 程序的精简实现,整合在单个静态链接的 Linux 二进制文件中,可在 Android 上运行。
Termux 提供了一套基于 Debian 项目 dpkg 系统的包管理器,以及包含大量 Unix 自由软件的软件包仓库,其中包括 C、C++、Java、Python、Common Lisp 等语言的编译器、调试器与运行环境。这些软件包通常在专用的终端模拟器应用内安装;但如果 Emacs 与该终端模拟器使用相同的应用签名密钥编译,并且二者的 “共享用户 ID” 设为相同的包名,Emacs 也可以访问这些软件包。Emacs 发行包中的 java/INSTALL 文件说明了如何以这种方式编译 Emacs。
termux-packages 提供了 Termux 用于生成其软件包仓库的包定义文件,这些文件也可以独立编译,并安装到 Emacs 的主目录下。
除上述项目外,大多数基于 Linux 内核的静态链接二进制程序,也可以在 Android 上运行。
Emacs 可通过 librsvg 库配置支持查看 SVG 图片文件。SVG 文件中可能会引用文件系统中的其他图片,而 librsvg 默认无法识别这些图片的格式;如果没有在主目录下的 .local/share/mime 目录中安装符合 XDG 规范的 MIME 数据库,这些引用的图片将会显示为空白方块。
由于生成该数据库需要 XDG 的 shared-mime-info 工具,建议先在 GNU/Linux 或其他 Unix 系统的临时目录中生成数据库,再将其传输到目标 Android 设备。安装最新版 shared-mime-info 后,按以下步骤生成 MIME 数据库:创建临时目录并复制默认的 freedesktop.org MIME 描述文件:
$ mkdir -p my-mime-database/packages $ cp /usr/share/mime/packages/freedesktop.org.xml my-mime-database/packages #执行命令生成数据库: $ update-mime-info my-mime-database
该命令可能会输出一系列提示,说明指定的 MIME 数据库不在宿主系统的搜索路径中,这些提示可以忽略。将生成的数据库传输到 Android 设备(例如传到 /sdcard/Download/my-mime-database.tar.gz ),然后在设备上解压并放置到正确位置:
$ cd; mkdir -p .local/share $ tar xfz /sdcard/Download/my-mime-database.tar.gz $ mv my-mime-database .local/share/mime $ rm /sdcard/Download/my-mime-database.tar.gz
如果 Emacs 会话之前已经尝试显示过包含内嵌资源的 SVG 图片,必须重启 Emacs,新的 MIME 数据库才会生效。
附录 I Emacs 与 Microsoft Windows/MS-DOS
本章介绍在微软 Windows 系统上使用 Emacs 的特殊事项。其中部分内容同样适用于微软的旧版操作系统 MS‑DOS。仅与 MS‑DOS 相关的 Emacs 功能会在单独章节中说明(参见 Emacs 与 MS‑DOS)。
Windows 是一款非自由操作系统:它剥夺了所有计算机用户本应享有的自由,这是一种不公。为了你的自由,我们强烈建议你改用自由操作系统。
我们之所以在专有操作系统上支持 GNU Emacs,是希望用户能借此体验到自由的滋味,进而主动摆脱这些系统的束缚。
Emacs 在 Windows 上的运行方式与手册其余部分所描述的基本一致,包括支持长文件名、多窗口、滚动条、鼠标菜单以及子进程等。不过仍有一些特殊事项需要注意,本章将对此进行说明。
I.1 在 MS-Windows 上启动 Emacs 的方法
在 Windows 上启动 Emacs 有多种方式:
- 通过桌面快捷方式:双击图标,或单击后按
RET回车键即可。快捷方式的「目标」(在属性里)应设为runemacs.exe的完整绝对路径,而不是emacs.exe。原因是:runemacs.exe会隐藏控制台窗口;如果用emacs.exe(Windows 认为它是控制台程序),就会弹出命令行窗口。用这种方式启动时,Emacs 的工作目录由快捷方式决定。你可以右键快捷方式 →「属性」→「快捷方式」标签,修改「起始位置」来自定义目录。 - 通过任务栏快捷方式:单击任务栏上的图标即可启动。Vista 及之后的 Windows 可以把正在运行的程序固定到任务栏。对 Emacs 也可以这么做,但之后必须修改快捷方式属性,确保运行的是
runemacs.exe,而不是emacs.exe。你也可以在开始菜单里右键 Emacs 图标,选择「固定到任务栏」。同样,务必确保程序是runemacs.exe。启动目录同样可以在快捷方式属性的「起始位置」中设置。 - 在命令提示符窗口中:输入
emacs并RET回车。执行后,该命令行窗口在 Emacs 退出前无法再输入其他命令。此时 Emacs 的启动目录为当前 Shell 所在目录。 - 在命令提示符窗口中(推荐):输入
runemacs并RET回车。执行后,命令行窗口会立刻恢复可用。启动目录同样是当前 Shell 所在目录。 - 通过 Windows「运行」对话框(通常从开始菜单打开):输入
runemacs并回车。Emacs 会在 Windows 对应用户 HOME 目录的上级目录启动,详见「Windows 上的 HOME 与启动目录」一节。 通过
emacsclient.exe或emacsclientw.exe:这两个工具允许从其他程序调用 Emacs,并复用已运行的 Emacs 进程处理编辑任务,详见「将 Emacs 作为服务器使用」。区别:emacsclient.exe:控制台程序;emacsclientw.exe:Windows 图形界面程序。两者都会等待 Emacs 完成编辑后才退出,并将控制权还给调用它的程序。使用哪个取决于调用方:
- 如果是控制台程序,用 emacsclient.exe,消息和提示会在同一个命令窗口显示。
- 如果是图形界面程序,用 emacsclientw.exe,否则会弹出命令行窗口。
典型场景:在文件资源管理器里右键文件 →「打开方式」时,建议用 emacsclientw.exe。
如果 Emacs 可能未运行(或未以服务器模式运行),可以加上 --alternate-editor= 或 -a 参数,确保总能打开一个编辑器。通过 emacsclient 启动时,Emacs 的工作目录为调用它的程序所在的当前目录。
注意:受 Windows 本身限制,Emacs 无法在同一个会话里同时拥有图形界面和文本模式窗口。它也不能在多个命令提示符窗口打开文本模式帧,因为 Windows 程序同一时间只能拥有一个控制台。因此:
如果你用 -c 参数调用 emacsclient,而 Emacs 服务器运行在文本模式会话中,只会在原命令行窗口新建文本帧;只有服务器在图形会话时,才会新建图形窗口。同理,使用 -t 参数时:服务器在图形会话则新建图形窗口;在文本模式会话则新建文本帧。详见 emacsclient 相关选项。
I.2 文本文件与二进制文件
GNU Emacs 使用 换行符(newline) 分隔文本行,这是 GNU、Unix 及其他符合 POSIX 标准系统的惯例。
相比之下,MS‑DOS 和微软 Windows 通常使用回车符后跟换行符这一双字符序列来分隔文本行。(换行符与新行符是同一个字符。)因此,要在 Emacs 中方便地编辑常见文件,就需要对这些行尾(EOL)序列进行转换。而 Emacs 默认正是这样做的:
- 读取文件时,将回车 + 换行转换为新行符;
- 写入文件时,将新行符转换回回车 + 换行。
负责处理国际字符编码转换的同一套机制,也会执行这种行尾转换(参见编码系统)。
对大多数文件进行这种特殊格式转换的一个后果是:Emacs 所显示的字符位置(参见「光标位置信息」)与操作系统所识别的文件大小信息并不一致。
此外,如果 Emacs 从文件内容中识别出该文件使用换行符(newline)而非回车 + 换行(carriage return + linefeed)作为行分隔符,那么在读写该文件时不会执行行尾(EOL)转换。因此,你可以在 MS‑DOS 上直接读取和编辑来自 GNU 与 Unix 系统的文件,无需额外操作,并且在编辑后,这些文件仍会保留其 Unix 风格的行尾格式。
模式行会指示当前缓冲区是否启用了行尾转换。如果缓冲区正在使用 MS‑DOS 风格的行尾转换,Windows 版本的 Emacs 会在模式行开头附近的编码系统缩写后面显示一个反斜杠 \ (参见「模式行」)。如果没有执行行尾(EOL)转换,则会显示字符串 (Unix) 来代替反斜杠,以提醒你该文件的行尾格式并非常见的回车 + 换行格式。
若要打开文件并显式指定使用 DOS 风格或 Unix 风格的行尾,可以指定编码系统(参见「为文件文本指定编码系统」)。例如: C-x RET c unix RET C-x C-f foobar.txt 会以不转换行尾的方式打开文件 foobar.txt;如果某一行以回车 + 换行结尾,Emacs 会在该行末尾显示 ^M 。同样地,你可以使用命令 C-x RET f 让 Emacs 以指定的行尾格式保存缓冲区。例如,要以 Unix 行尾格式保存缓冲区,输入: C-x RET f unix RET C-x C-s 。如果你先用 DOS 行尾转换打开文件,再以 Unix 行尾格式保存,这就相当于 dos2unix 工具的效果,能直接将文件转换为 Unix 行尾风格。
当你通过 NFS、Samba 等方式访问运行 GNU 或 Unix 系统的计算机上的文件系统时,Emacs 不应该对这些文件系统里的任何文件执行行尾转换,即使是新建文件也一样。要实现这一点,可以调用函数 w32-add-untranslated-filesystem ,将这些文件系统标记为不转换行尾的文件系统。它接受一个参数:文件系统名称,包含盘符,可附带目录。例如:
(w32-add-untranslated-filesystem "Z:")
将 Z 盘设为不转换行尾的文件系统。
(w32-add-untranslated-filesystem "Z:\\foo")
将 Z 盘下的 \foo 目录设为不转换行尾的文件系统。
你通常会把 w32-add-untranslated-filesystem 写在 .emacs 或 init.el 初始化文件,或 site-start.el 中,让站点所有用户都生效。
要取消 w32-add-untranslated-filesystem 的效果,使用函数 w32-remove-untranslated-filesystem 。该函数同样接受一个字符串参数,格式与添加时一致。
将文件系统设为不转换,只影响行尾转换,不影响字符集转换。简单说,它让 Emacs 默认以 Unix 风格(只用换行符)新建文件。参见编码系统。
I.3 MS-Windows 上的文件名
微软视窗(MS‑Windows)与 MS‑DOS 通常使用反斜杠 '\' 分隔文件名中的路径部分,而非其他系统所用的正斜杠。运行在 MS‑DOS / 微软视窗上的 Emacs 既支持正斜杠,也支持反斜杠,同时能识别文件名中的盘符。
在 MS‑DOS / 微软视窗中,文件名不区分大小写,因此 Emacs 默认在补全时忽略文件名的大小写。为此,在 MS‑DOS / 微软视窗系统中, read-file-name-completion-ignore-case 变量的默认值为非空(非 nil)。详见「补全选项」。
变量 w32-get-true-file-attributes 用于控制 Emacs 是否发起额外的系统调用,以便在 file-attributes 、 directory-files-and-attributes 等基础函数中更精确地获取文件属性。这些额外调用是正确获取文件所有者、链接计数,以及管道等特殊文件类型所必需的。若不启用这些系统调用:
- 文件所有者会被显示为当前用户
- 链接数始终显示为 1
- 特殊文件会被当作普通文件
该变量默认值为 local ,表示 Emacs 仅对本地固定磁盘发起这些额外系统调用。其他非空值表示:即使对可移动磁盘与远程卷也执行这些调用(可能会降低 Dired 及相关功能的速度)。值为 nil 表示:从不发起这些系统调用。在支持硬链接与文件安全权限的 NTFS 卷上,非空值比在 FAT、FAT32、exFAT 卷上更有用。
与类 Unix 系统不同,微软视窗文件系统对文件名可用字符有严格限制,以下字符不可用于文件名:
- shell 重定向符号:<、>、|
- 冒号 :(盘符后的冒号除外)
- 正斜杠
/与反斜杠\(用作路径分隔符时除外) - 通配符:*、?
- ASCII 码 1–31 的控制字符(文件名中不允许换行)
- 空字符(ASCII 0,Unix 文件系统同样禁止)
此外,任何与 DOS 设备名(如 NUL、LPT1、PRN、CON)完全匹配的文件名(无论是否带后缀),在任意目录下都会被解析为对应设备。因此,仅在需要使用对应设备时才使用这类名称。
I.4 MS-Windows 上的 ls 命令模拟
Dired 通常会调用外部程序 ls 来生成目录列表并显示在 Dired 缓冲区中(见《目录编辑器 Dired》)。但 MS-Windows 和 MS-DOS 系统本身并不自带该程序,尽管有一些移植版的 GNU ls 可用。因此,这些系统上的 Emacs 会通过 ls-lisp.el 包,以纯 Lisp 方式模拟 ls 。虽然 ls-lisp.el 提供了相当完整的 ls 模拟,但该模拟实现有一些特有的选项和功能,本节将对其进行说明。
该 ls 模拟支持很多 ls 开关,但并非全部。它支持的开关列表如下:-A, -a, -B, -C, -c, -G, -g, -h, -i, -n, -R, -r, -S, -s, -t, -U, -u, -v, -X。-F 开关为部分支持(会追加标识文件类型的字符,但不会阻止跟随符号链接)。
在 MS-Windows 和 MS-DOS 上,Emacs 编译时已预加载 ls-lisp.el ,因此这些平台上始终使用 Lisp 版的 ls 模拟。如果你有移植版的 ls ,可以将 ls-lisp-use-insert-directory-program 设为非 nil 值,从而改用由变量 insert-directory-program 指定的外部程序。
ls-lisp.el 对文件的排序规则取决于下面几个可自定义选项。
默认排序顺序遵循从系统区域设置派生而来的、与区域相关的规则。你可以将 ls-lisp-use-string-collate 设为 nil ,使其排序与区域无关。
在 GNU 和类 Unix 系统中,当区域编码为 UTF‑8 时,排序遵循Unicode 排序算法(UCA)。要在 MS-Windows 上实现类似效果,应将变量 ls-lisp-UCA-like-collation 设为非 nil 值(这是默认值)。这样得到的排序会忽略标点、符号字符和空白字符,因此 .foobar 、 foobar 和 foo bar 会排在一起,而不是分散各处。
默认情况下,ls-lisp.el 对生成的目录列表使用区分大小写的排序,以便与其他平台显示一致。如果你希望不区分大小写排序,可将变量 ls-lisp-ignore-case 设为非 nil 值。
默认情况下,文件和子目录混合排序,以模拟 ls 的行为。但原生 MS-Windows/MS-DOS 文件管理器会先列目录、再列文件。如果你想要这种效果,可将选项 ls-lisp-dirs-first 设为非 nil 值。
变量 ls-lisp-verbosity 控制 ls-lisp.el 显示哪些文件属性。其值应为 nil 或包含以下符号中一个或多个的列表:links、uid、gid。
- links:显示指向该文件数据的不同文件名数量(即链接数),仅在 NTFS 卷上有用。
- uid:显示文件所有者的数字用户 ID。
- gid:显示文件所有者所在组的数字组 ID。
- 默认值为 (links uid gid),即显示全部 3 个可选属性。值为 nil 表示不显示这些属性。
变量 ls-lisp-emulation 通过设置上面 3 个选项的默认值,来控制 ls 模拟的风格: ls-lisp-ignore-case 、 ls-lisp-dirs-first 、 ls-lisp-verbosity 。该选项的值可以是以下符号之一:
GNUnil- 模拟 GNU 系统(默认)。将 ls-lisp-ignore-case、ls-lisp-dirs-first 设为 nil,ls-lisp-verbosity 设为 (links uid gid modes)。
UNIX- 模拟 Unix 系统。与 GNU 类似,但 ls-lisp-verbosity 设为 (links uid modes)。
MacOS- 模拟 macOS。将 ls-lisp-ignore-case 设为 t,ls-lisp-dirs-first 和 ls-lisp-verbosity 设为 nil。
MS-Windows模拟 MS-Windows。将 ls-lisp-ignore-case 和 ls-lisp-dirs-first 设为 t;在 Windows 9X 上 ls-lisp-verbosity 为 nil,在新版 Windows 上为 t。
注意:即使在 Windows 上,默认模拟风格也不是 MS-Windows,因为该平台上很多 Emacs 用户更偏好 GNU 默认行为。
ls-lisp-emulation 的其他任何值都等同于 GNU。自定义该选项会调用 ls-lisp-set-options 函数,按需更新上述 3 个依赖选项。如果你在 ls-lisp.el 加载后(注意它在 MS-Windows/MS-DOS 上是预加载的)没有通过 customize 直接修改该变量,可以手动调用该函数以达到同样效果。
变量 ls-lisp-support-shell-wildcards 控制如何处理文件名模式:若为非 nil(默认),则按Shell 风格通配符处理;否则按 Emacs 正则表达式处理。
变量 ls-lisp-format-time-list 定义文件日期与时间的显示格式。仅当 Emacs 无法确定当前区域时,才会使用该变量的值。(但如果 ls-lisp-use-localized-time-format 为非 nil,则即使当前区域可用,Emacs 也会遵循 ls-lisp-format-time-list ;见下文。)
ls-lisp-format-time-list 的值是两个字符串组成的列表:第一个用于文件在本年以内修改的情况,第二个用于更早的文件。在这两个字符串中,你可以使用 '%' 序列来替换时间部分。例如:
("%b %e %H:%M" "%b %e %Y")
注意:这些 '%' 序列替换后的字符串取决于当前区域。更多时间格式规范,见《Emacs Lisp 参考手册》中的时间解析部分。
通常,Emacs 会以传统格式或 ISO 风格格式显示文件时间戳。但如果变量 ls-lisp-use-localized-time-format 为非 nil,Emacs 会按照 ls-lisp-format-time-list 的指定来格式化时间。 ls-lisp-format-time-list 中的 '%' 序列会生成与区域相关的月份和日期名称,这可能导致 Dired 显示中的列不对齐。 ls-lisp-use-localized-time-format 的默认值为 nil 。
I.5 MS-Windows 上的 HOME 目录与启动目录
在 Windows 中,与 HOME 对应的是用户专属的应用数据目录。其实际路径取决于 Windows 版本:
- Windows 2000 到 XP 典型路径:
C:\Documents and Settings\用户名\Application Data - Windows Vista 及更高版本:
C:\Users\用户名\AppData\Roaming - Windows 9X/ME:
C:\WINDOWS\Application Data或C:\WINDOWS\Profiles\用户名\Application Data
如果该目录不存在或无法访问,Emacs 会将 C:\ 作为 HOME 的默认值。
你可以显式设置环境变量 HOME 来覆盖这一默认值,将其指向系统中的任意目录。 HOME 既可以在命令行提示符中设置,也可以通过「我的电脑」→「属性」对话框设置,还可以在系统注册表中设置,详见《MS-Windows 系统注册表》。
为兼容旧版 Emacs31,如果在 C 盘根目录 C:\ 下存在名为 .emacs 的文件,且环境变量与注册表均未设置 HOME ,Emacs 会将 C:\ 当作默认的 HOME 目录,不再去应用数据目录查找(即使该目录存在)。注意:仅会在 C:\ 下查找 .emacs ,不会查找旧名称 _emacs (见下文)。通过 C:\.emacs 来定义 HOME 的方式已不推荐使用,Emacs 启动时会对此发出警告。
无论最终确定的位置是哪一个,Emacs 都会将内部的 HOME 环境变量指向它,并使用该位置存放或查找原本应在主目录下的其他文件与目录。
你可以随时用下面的命令查看 Emacs 认定的主目录: C-x d ~/ RET 该命令会显示主目录下的文件列表,并在第一行显示完整路径。同样,要打开你的初始化文件,可输入: C-x C-f ~/.emacs RET (假设文件名为 .emacs )。
你的初始化文件可以使用《Emacs 初始化文件》中提到的任意名称。
由于 MS-DOS 不允许以点开头的文件名,旧版 Windows 也难以创建此类文件,因此 Windows 版 Emacs 同时支持初始化文件名 _emacs :如果主目录中存在 _emacs 且不存在 .emacs ,就会使用 _emacs 。该名称已被视为过时,使用时 Emacs 会显示警告。
I.6 MS-Windows 上的键盘使用
本节介绍 Emacs 中与 Windows 平台键盘输入相关的特有功能。
很多在 MS-Windows 程序中常用的组合键(即 “快捷键”),会与传统 Emacs 按键绑定冲突。(这些 Emacs 按键绑定在微软成立之前就已存在。)典型冲突包括: C-c 、 C-x 、 C-z 、 C-a 。你可以启用 CUA 模式,将其中一部分按键重新定义为更接近 Windows 的含义(见 CUA 绑定)。另一个能让 Emacs 行为更像普通 Windows 应用的可选功能是 删除选择模式(Delete Selection mode,见对选区的操作)。
默认情况下,键盘上标有 Alt 的键被映射为 Meta 键。如果你希望它只作为 Alt 修饰键使用,可将变量 w32-alt-is-meta 设置为 nil。
MS-Windows 会保留某些组合键供系统自身使用,例如 Alt-TAB 以及一系列 Win 键组合。这些组合键会在 Emacs 接收到之前就被系统拦截。此外,在 Windows 10 中,所有 Win 键组合都由系统保留,不会传递给应用程序,即使系统当前并未为该组合绑定热键。你可以使用函数 w32-register-hot-key ,让指定按键序列被 Emacs 接收,而不是被 Windows 抢占。注册为热键后,该组合键会在 Windows 处理之前从系统输入队列中取出,从而在 Emacs 活动时覆盖掉 Windows 对该按键的特殊含义。该覆盖仅在 Emacs 处于活动窗口时有效;前台为其他应用时,按键恢复正常行为。
w32-register-hot-key 的参数必须是单个按键 + 单个修饰键,采用可用于 define-key 的向量形式。Control 和 Shift 修饰键对参数无效。如果 w32-alt-is-meta 为 t (默认),Meta 修饰键会被解释为 Alt 键;Super、Hyper 修饰键则根据 w32-lwindow-modifier 和 w32-rwindow-modifier 的绑定来解释。
此外,只写修饰键并带后缀短横线但不指定具体按键(如 [s-] ),表示让 Emacs 接管该修饰键对应的所有 Windows 系统热键。
示例:
(w32-register-hot-key [M-tab])允许你在 Emacs 中正常使用M-TAB,例如用于补全光标处的单词 / 符号,或在增量搜索中补全历史搜索字符串。(w32-register-hot-key [s-])当w32-lwindow-modifier绑定到super时,会禁用 Windows 自身的所有 Win 键快捷键32。
注意: w32-register-hot-key 在调用时会读取当前 w32-[lr]window-modifier 的值。因此你可以:
- 先将左 Win 键设为 super
- 调用
(w32-register-hot-key [s-r]) - 再将右 Win 键设为 super
效果:
- 左
Win + R执行你在 Emacs 中绑定的功能 - 右
Win + R仍打开系统的 “运行” 对话框
热键注册会自动包含该热键对应的所有 Shift、Control 组合。也就是说,注册 s-a 后,S-s-a、C-s-a、C-S-s-a 也会同时生效。
在 Windows 98 和 ME 中,热键注册限制更多:必须完整指定目标热键,并可通过自定义 w32-phantom-key-code 达到预期效果。
函数 w32-unregister-hot-key 用于取消 w32-register-hot-key 对指定按键序列的效果。
大小写锁定、数字锁定、菜单键、Win 键、滚动锁定
默认情况下,CapsLock 键只对普通字符键生效(将小写转为大写)。但如果你将变量 w32-capslock-is-shiftlock 设为非 nil,CapsLock 也会对非字符键生效,效果等同于按住 Shift 键。
如果变量 w32-enable-caps-lock 设为 nil ,CapsLock 键会产生符号 capslock,而不是切换大小写。默认值为 t 。
类似地,如果 w32-enable-num-lock 为 nil ,NumLock 键会产生符号 kp-numlock。默认值为 t ,即 NumLock 正常切换小键盘功能。
变量 w32-apps-modifier 控制 菜单键(Apps 键,通常位于右 Alt 和右 Ctrl 之间) 的作用。可取的值:hyper、super、meta、alt、control、shift,或 nil(直接作为 apps 键)。默认为 nil 。
变量 w32-lwindow-modifier 决定左 Win 键的作用。默认 nil ,按键产生符号 lwindow;设为 hyper/super/meta/alt/control/shift 则作为对应修饰键。
类似变量:
- w32-rwindow-modifier:控制右 Win 键
- w32-scroll-lock-modifier:控制 ScrollLock 键
若设为 nil:
- 右 Win 键产生 rwindow
- ScrollLock 产生 scroll
如果你希望 ScrollLock 像其他应用一样只开关键盘指示灯,可将 w32-scroll-lock-modifier 设为 t 或上述修饰键以外的任意非 nil 值。
Alt 键与系统菜单、Win 键传递
作为原生 Windows 应用编译的 Emacs,默认会关闭 “轻按 Alt 弹出系统菜单” 的功能。原因是:Alt 在 Emacs 中充当 Meta 键。用户经常临时按下 Meta 又取消,如果此时弹出系统菜单,会干扰后续命令,很多用户觉得困扰。
你可以通过将 w32-pass-alt-to-system 设为非 nil,重新启用系统对轻按 Alt 的默认处理。
变量:
- w32-pass-lwindow-to-system
- w32-pass-rwindow-to-system
决定对应按键是传递给 Windows,还是被 Emacs 吃掉。值为 nil 时被 Emacs 静默吃掉;否则传递给系统。两者默认均为 t。传递给系统则保持原有功能:例如左 Win 键打开开始菜单。
变量 w32-recognize-altgr 控制是否识别 AltGr 键(或等效的右 Alt + 左 Ctrl)。默认 t,即识别为 AltGr;设为 nil 则会将其解释为 Ctrl + Meta 组合。
输入法管理器(IME)
某些版本的 MS-Windows(通常是东亚语言版)会启用输入法管理器(IMM),用于让应用与系统输入法编辑器(IME)通信。Emacs 在可用时会使用 IME 来输入东亚非 ASCII 字符,用法类似 Emacs 内置输入法(见输入法)。但在某些场景下,IME 可能干扰操作:它会把你输入的普通 ASCII 键当作非 ASCII 字符的编码片段处理。你可以使用函数 w32-set-ime-open-status 临时关闭 / 重新打开 IME。
I.7 MS-Windows 上的鼠标使用
本节介绍与鼠标相关、Windows 平台特有的 Emacs 变量。
变量 w32-mouse-button-tolerance 指定一个时间间隔(单位:毫秒),用于在双键鼠标上模拟鼠标中键按下。如果两个鼠标按键在此时间间隔内同时按下,Emacs 会生成一个鼠标中键点击事件,而不是对其中一个按键产生双击事件。
如果变量 w32-pass-extra-mouse-buttons-to-system 为非 nil 值,Emacs 会将鼠标第四、第五键传递给 Windows 系统。
变量 w32-swap-mouse-buttons 用于控制三个鼠标按键中,哪一个产生 mouse-2 事件。
- 当它为 nil(默认值)时:中键产生
mouse-2事件,右键产生mouse-3事件。 - 如果该变量为非 nil 值:这两个按键的功能会互换。
I.8 Windows 9X/ME 与 Windows NT/2K/XP/Vista/7/8/10 上的子进程
作为原生 Windows 应用编译的 Emacs(区别于 DOS 版本),完整支持异步子进程。在 Windows 版本中,只要只运行 32 位或 64 位 Windows 应用程序,同步与异步子进程在所有版本的 MS-Windows 上都能正常工作。
但是,当你在子进程中运行 DOS 应用时,可能会遇到问题,甚至完全无法运行;如果在两个子进程中同时运行两个 DOS 应用,你可能必须重启系统。
由于 Windows 9X 上的标准命令解释器(以及大多数命令行工具)都是 DOS 应用,因此在该系统上使用时,这类问题会比较突出。但我们对此无能为力,只有微软才能修复这些问题。
如果你只运行一个 DOS 应用子进程,只要该程序 “行为规范”,不进行直接屏幕访问或其他非常规操作,子进程应该能按预期工作。如果你有 CPU 监控程序,即使 DOS 应用处于空闲状态,机器也会显示占用率 100%,但这只是 CPU 监控软件统计负载方式带来的假象。
在另一个子进程中启动任何其他 DOS 应用之前,必须先终止当前的 DOS 应用。Emacs 无法中断或终止一个 DOS 子进程,终止这类子进程的唯一方法是向该程序发送使其退出的命令。
如果你尝试在不同子进程中同时运行两个 DOS 应用,后启动的那个会被挂起,直到第一个运行结束,即使其中一个或两个都是异步进程也不例外。
如果你能切换到第一个子进程并让它退出,第二个子进程通常会恢复正常。但如果第二个子进程是同步的,Emacs 本身会被卡住,直到第一个子进程结束。如果该子进程不接收用户输入就无法结束,那么在 Windows 9X 上你只能重启电脑。在 Windows NT 及更高版本中,你可以用进程查看工具结束对应的 NTVDM 进程(这会同时终止两个 DOS 子进程)。
如果在这种情况下必须重启 Windows 9X,不要使用开始菜单里的关机命令,那样通常会卡死系统。可以按 Ctrl-Alt-Del ,然后选择关机。这种方式通常有效,尽管可能需要几分钟才能完成。
变量 w32-quote-process-args 控制 Emacs 如何对子进程参数进行转义。非 nil 表示使用双引号 " 进行引用。如果值是一个字符,Emacs 会用该字符转义出现的引号;否则会根据程序类型选择合适的转义字符。
变量 w32-pipe-buffer-size 控制 Emacs 在创建与子进程通信的管道时,向系统申请的缓冲区大小。默认值为 0,表示由操作系统决定大小。任何合法的正整数值都会按字节数申请对应大小的缓冲区。这可用于微调与某些对管道 I/O 缓冲行为异常的程序之间的通信。
如果你需要在 Emacs 子进程中调用 MS-DOS 程序,可能会发现从这类程序读取数据的速率很低。将变量 w32-pipe-read-delay 设置为非零值可以改善吞吐量,建议设为 50。默认值为 0。
函数 w32-shell-execute 可用于编写自定义命令,以运行那些已在系统中注册、用于处理某类文档或文件的标准 Windows 操作的应用程序。该函数是对 Windows ShellExecute API 的封装,更多细节参见微软视窗系统 API 文档。
I.9 MS-Windows 上的打印功能
当系统中没有 POSIX 风格的 lpr 程序时,lpr-buffer(见 “打印硬拷贝”)和 ps-print-buffer(见 “PostScript 硬拷贝”)等打印命令会在 MS‑DOS 和 MS‑Windows 下通过将输出发送到某个打印机端口来工作。所有系统上的打印都由相同的 Emacs 变量控制,但在某些情况下,它们在 MS‑DOS 和 MS‑Windows 上的默认值不同。
Windows 上的 Emacs 会尝试自动识别默认打印机(通过函数 default-printer-name )。但在极少数情况下,这可能会失败,或者你希望在 Emacs 中使用另一台打印机。本节剩余部分将说明如何告诉 Emacs 使用哪台打印机。
如果你想使用本地打印机:
- 将 Lisp 变量
lpr-command设为空字符串 ""(这是 Windows 上的默认值) - 将
printer-name设为打印机端口名,例如:"PRN":标准本地打印机端口、"LPT2"、"COM1":串口打印机。
你也可以把 printer-name 设置为文件名,此时 “打印” 的内容会追加到该文件中。如果将 printer-name 设为 "NUL",打印内容会被直接丢弃(发送到系统空设备)。
你还可以使用网络共享打印机,方法是将 printer-name 设为该打印机的 UNC 共享名,例如: "//joes_pc/hp4si" (这里使用正斜杠或反斜杠都可以)。要查看共享打印机名称:
- 在命令提示符运行 ‘net view’ 获取服务器列表
- 用 net view 服务器名 查看该服务器共享的打印机(和目录)
- 或在桌面打开 “网上邻居”,查找共享了打印机的计算机
如果打印机没有出现在 ‘net view’ 结果中,或者设置 UNC 名后无法打印,可以使用 net use 命令将本地打印端口(如 "LPT2")映射到网络打印机。
示例: net use LPT2 : \\joes_pc\hp4si 33
这会让 Windows 捕获 LPT2 端口,并将打印内容重定向到 joes_pc 上的打印机。之后在 Emacs 里把 printer-name 设为 "LPT2" 即可在网络打印机上打印。
某些 Windows 网络软件中,你也可以通过控制面板 → 打印机,把某个打印端口(如 LPT2)重定向到网络打印机,代替 net use 命令。
如果把 printer-name 设为文件名,强烈建议使用绝对路径。因为 Emacs 会根据当前缓冲区的默认目录切换工作目录,如果使用相对路径,会在每个执行打印的缓冲区所在目录下都生成一个文件。
如果 printer-name 设置正确但仍然打印不出内容,可能是你的打印机不支持纯文本打印(一些廉价打印机缺少此功能)。这种情况下可以尝试下面介绍的 PostScript 打印命令。
print-buffer 和 print-region 命令会调用 pr 程序,或向 lpr 传递特殊参数,以便在每页打印页眉。由于 MS‑DOS/MS‑Windows 通常没有这些工具,默认情况下变量 lpr-headers-switches 被设置为忽略页眉请求。因此:
- print-buffer 效果等同于 lpr-buffer
- print-region 效果等同于 lpr-region
如果你安装了可用的 pr(例如来自 GNU Coreutils),可以把 lpr-headers-switches 设为 nil,Emacs 就会调用 pr 生成页眉,并按 printer-name 指定的目标打印。
最后,如果你确实有可用的 lpr 程序,可以把变量 lpr-command 设为 "lpr"。这样 Emacs 就会像其他系统一样使用 lpr 打印。(如果程序名不是 lpr,就把 lpr-command 设为对应名称。)。当 lpr-command 不为空时,lpr-switches 按标准含义工作。如果 printer-name 是字符串,它会作为 lpr 的 -P 选项值,与 Unix 一致。
另有一组平行的变量用于 PostScript 打印:ps-lpr-command、ps-lpr-switches、ps-printer-name(见 PostScript 硬拷贝变量)。它们的用法与上面普通打印的对应变量完全相同。
简单说:
- ps-printer-name:指定 PostScript 输出的设备(或文件)
- printer-name:指定普通文本打印的设备
(两套变量是为了应对你可能接了两台打印机,且只有一台支持 PostScript 的情况。)
ps-lpr-command 的默认值为 "",表示 PostScript 输出会发送到 ps-printer-name 指定的打印机端口。你也可以把它设为一个能接收 PostScript 文件的程序名。因此,如果你使用非 PostScript 打印机,可以将此变量设为 PostScript 解释器(如 Ghostscript),并通过 ps-lpr-switches 传递参数。
示例:在配置文件 .emacs 中使用 Ghostscript 打印到系统默认打印机:
(setq ps-printer-name t) (setq ps-lpr-command "D:/gs6.01/bin/gswin32c.exe") (setq ps-lpr-switches '("-q" "-dNOPAUSE" "-dBATCH" "-sDEVICE=mswinpr2" "-sPAPERSIZE=a4"))
(假设 Ghostscript 安装在 D:/gs6.01)
I.10 MS-Windows 上的字体指定
字体通过名称、大小及可选属性来指定。指定字体的格式源自现代自由桌面环境中使用的 fontconfig 库:
[Family[-PointSize]][:Option1=Value1[:Option2=Value2[...]]] [字体族[-磅大小]][:选项1=值1[:选项2=值2[...]]]
为保持向后兼容,同时也支持旧的基于 XLFD 的格式。
Windows 平台上的 Emacs 支持多种字体后端。目前可用的有 gdi、uniscribe 和 harfbuzz 后端。
- gdi 字体后端:在所有 Windows 版本上均可使用,支持 Windows 原生支持的所有字体。
- uniscribe 字体后端:在 Windows 2000 及更高版本可用,支持 TrueType 和 OpenType 字体。
- harfbuzz 字体后端:仅当 Emacs 编译时启用了 HarfBuzz 支持,且系统中安装了 HarfBuzz DLL 时可用;与 Uniscribe 类似,该后端仅支持 TrueType 和 OpenType 字体。
某些需要复杂排版的语言,只能由 Uniscribe 或 HarfBuzz 后端正确支持。默认情况下,每个窗口会启用两个后端:gdi,以及 harfbuzz 或 uniscribe 中的一个(取决于哪个可用;若两者都存在,则默认只启用 harfbuzz)。Emacs 查找合适字体时,harfbuzz 和 uniscribe 后端优先级高于 gdi。
若要覆盖该行为,即使 Uniscribe 可用也强制使用 GDI 后端:
- 在命令行启动 Emacs 时加入参数:xrm Emacs.fontBackend:gdi
- 或在注册表的以下路径中添加值为 gdi 的 Emacs.fontBackend 资源:
HKEY_CURRENT_USER\SOFTWARE\GNU\Emacs或HKEY_LOCAL_MACHINE\SOFTWARE\GNU\Emacs(参见 X 资源)
同理,即使 HarfBuzz 可用也强制使用 Uniscribe 后端,可在启动命令中使用: -xrm Emacs.fontBackend:uniscribe 。你也可以通过 font-backend 窗口参数同时启用全部 3 个后端,但请注意:此时 Emacs 为系统中无对应字体的字符查找字体时,耗时会变长。
另外,你可以通过 modify-frame-parameters 函数,使用 font-backend 框架参数为某个窗口指定字体后端(参见《Emacs Lisp 参考手册》中的参数访问)。还可以通过 default-frame-alist 和 initial-frame-alist 为所有dalk指定字体后端(参见框架参数)。注意: font-backend 参数的值应为符号列表,例如 (uniscribe) 或 (harfbuzz uniscribe gdi)。
Windows 支持的可选字体属性
weight- 指定字体粗细。特殊值 light、medium、demibold、bold、black 可直接写在后面而不用 weight=,例如:Courier New-12:bold否则应使用 100–900 之间的数值,或 font-weight-table 中的命名粗细。未指定时默认使用常规粗细。
slant- 指定是否为斜体。特殊值 roman、italic、oblique 可直接使用而不用 slant=,例如:Courier New-12:italic否则应为数值或 font-slant-table 中的命名倾斜样式。在 Windows 中,倾斜值大于 150 视为斜体,否则视为正体。
family- 指定字体族,通常直接写在字体名称开头即可。
pixelsize- 以像素为单位指定字体大小,可替代字体族后面的磅大小。
adstyle- 指定字体的附加风格信息。在 Windows 上可识别:mono、sans、serif、script、decorative。在未指定字体族时,用作 fallback 非常有用。
registry- 指定字体应覆盖的字符集注册表。大多数 TrueType/OpenType 字体是 Unicode 字体,支持多国字符集;你可以通过 w32-charset-info-alist 中的特定注册表,将字体筛选限定为支持某一字符集。
spacing- 指定字体间距方式:p:比例间距字体、m 或 c:等宽字体
foundry- Windows 中不使用,但为兼容依赖此字段的代码,内部会自动设置:位图字体:raster、可缩放字体:outline、无法识别:unknown
script- 指定字体应支持的 Unicode 子范围。Emacs 已知的所有书写体系(基本对应最新 Unicode 标准)在 Windows 上均可识别。但 GDI 字体仅支持其中一部分:greek、hangul、kana、kanbun、bopomofo、tibetan、yi、mongolian、hebrew、arabic、thai。
antialias- 指定抗锯齿方式:none:无抗锯齿、standard:标准抗锯齿、subpixel:子像素抗锯齿(Windows 上称为 ClearType)、natural:带字间距调整的子像素抗锯齿。未指定时使用系统默认抗锯齿设置。
关于罕见文字与字体查找
在 Windows 上,Emacs 用于查找适合显示某非 ASCII 字符的字体的逻辑,对一些较新加入 Unicode 的罕见文字可能失效,即便你已安装对应字体。原因是这些文字在 Emacs 使用的字体查找信息中没有定义对应的 Unicode 子范围位(USBs)。
你可以使用函数 w32-find-non-USB-fonts 解决此问题:
- 需在 Emacs 会话开始时运行一次
- 安装新字体后需再次运行
可在初始化文件中加入:
(w32-find-non-USB-fonts)
或随时通过 M-: 手动执行(参见执行 Emacs Lisp 表达式)。若安装字体很多,该函数可能耗时数秒。如果你不希望启动时等待,且很少安装新字体:
- 先用 M-: 手动运行一次
- 若返回非 nil,将结果赋值给变量 w32-non-USB-fonts 并写入配置文件
(若返回 nil,说明你没有支持这些特殊文字的字体)。
字体选择对话框
变量 w32-use-w32-font-dialog 控制通过 S-mouse-1 (鼠标外观菜单)选择字体的方式:值为 t(默认):使用 Windows 标准字体选择对话框;值为 nil:弹出固定字体列表菜单。菜单中显示的字体由 w32-fixed-font-alist 决定。
I.11 其他 Windows 专用功能
本节介绍不便归类到其他章节的 Windows 专用功能。
变量 w32-use-visible-system-caret 是一个开关,用于决定是否显示系统光标(caret)。
- 未使用屏幕阅读器时,默认值为 nil,表示 Emacs 自行绘制光标来指示光标位置(point)。
- 非 nil 表示 Emacs 使用系统光标来指示位置;这便于配合屏幕阅读器使用,当 Emacs 检测到屏幕阅读器时会默认启用。当此变量为非 nil 时,其他影响光标显示的变量将不再生效。
变量 w32-grab-focus-on-raise :若设为非 nil,则窗口被置顶时自动获取焦点。默认值为 t,与 Windows 默认的 “点击获取焦点” 策略一致。
在 Windows 10(1809 及更高版本)和 Windows 11 中,Emacs 的标题栏和滚动条默认会跟随系统的浅色 / 深色模式,与资源管理器、命令提示符等程序行为一致。如需修改颜色模式:
- 打开 Windows 设置 → 个性化 → 颜色
- 选择选择你的颜色(或 “选择默认应用模式”“选择模式”)
- 重启 Emacs
在 Windows 11 中,可分别为 Windows 系统和应用程序设置不同的默认模式。
如果你不希望 Emacs 跟随系统深色模式,可将变量 w32-follow-system-dark-mode 设为 nil ;之后无论系统全局设置如何,Emacs 都会使用默认浅色模式。修改此变量只对修改后新建的 Emacs 窗口生效,因此建议在初始化文件中设置:
I.12 Emacs 与 MS-DOS
本节简要介绍在 MS-DOS 上使用 Emacs 的特殊事项。有关 MS-DOS 与微软现行操作系统 Windows 共通的特性说明,参见《Emacs 与 Microsoft Windows/MS-DOS》一章。 如果你编译了面向 MS-DOS 的 Emacs,该二进制程序也可以作为 DOS 应用程序,在 Windows 3.X、Windows NT、Windows 9X/ME 或 Windows 2000/XP 上运行;只要使用的是为 MS-DOS 编译的 Emacs,本章全部内容均适用于上述所有系统。
关于 Emacs 在 MS-DOS(以及 Windows)下对文本文件的特殊处理方式,参见《文本文件与二进制文件》一节。
I.12.1 MS-DOS 上的键盘使用
在 Emacs 中被称为 DEL 的按键(大多数工作站都是如此命名),在 PC 上对应的是 BS (backspace退格键)。因此,针对 PC 的终端初始化会将 BS 键重新映射为 DEL ;出于同样原因, Delete 键会被重新映射为 C‑d 。
为 MS‑DOS 编译的 Emacs 会将 C‑Break 识别为退出按键,作用与 C‑g 相同。这是因为 Emacs 在准备好接收新输入之前,无法检测到你按下了 C‑g ,因此不能用 C‑g 中止正在运行的命令(参见 “退出与中止”)。与之相对, C‑Break 会在按下时立即被检测到(如同其他系统上的 C‑g ),因此可用于中止运行中的命令以及紧急退出(参见 “紧急退出”)。
PC 键盘映射将左 Alt 键作为 Meta 键。你可以用以下两种方式模拟 SUPER 键和 Hyper 键:分别将变量 dos-hyper-key 和 dos-super-key 设置为 1 或 2 ,来选择右 Ctrl 键或右 Alt 键。
如果 dos-super-key 和 dos-hyper-key 都没有设为 1 ,那么默认情况下右 Alt 键也会被映射为 Meta 键。但是,如果安装了 MS‑DOS 国际键盘支持程序 KEYB.COM,Emacs 就不会把右 Alt 映射为 Meta —— 因为在非美式键盘布局中,它需要用来输入 ~ 、 @ 等字符;这种情况下,你只能使用左 Alt 作为 Meta 键。
变量 dos-keypad-mode 是一个开关变量,用于控制数字小键盘按键返回的编码。你也可以将小键盘上的 ENTER 键定义为 C‑j ,只需在你的 _emacs 配置文件中加入如下一行:
;; 让 numeric keypad(数字小键盘)的回车键等效于 C-j (keymap-set function-key-map "<kp-enter>" "C-j")
I.12.2 MS-DOS 上的鼠标使用
MS‑DOS 上的 Emacs 支持鼠标(仅在默认终端下有效)。鼠标命令按文档说明工作,包括使用菜单和菜单栏的命令(参见 “菜单栏”)。滚动条在 MS‑DOS 版 Emacs 中不可用。PC 鼠标通常只有两个按键,分别对应 mouse‑1 和 mouse‑2 ;如果同时按下两个按键,效果等同于 mouse‑3 。如果鼠标确实有 3 个按键,Emacs 会在启动时检测到,三个按键均可正常工作,与 X 窗口系统一致。
当鼠标指针移过菜单项时,菜单栏和弹出菜单的帮助文本会显示在回显区。同时也支持对鼠标敏感文本的高亮显示(参见 “使用鼠标跟随引用”)。
某些版本的鼠标驱动程序无法正确报告鼠标按键数量。例如,带滚轮的鼠标会报告有 3 个按键,但只有 2 个按键的事件会传给 Emacs;作为中键使用的滚轮点击不会被传递。在这些情况下,你可以使用命令 M‑x msdos‑set‑mouse‑buttons 告诉 Emacs 预期的鼠标按键数。你可以将下面的代码片段加入 _emacs 初始化文件,使该设置永久生效:
;; 将鼠标当作双键鼠标处理 (msdos-set-mouse-buttons 2)
为 MS‑DOS 编译的 Emacs 在 Windows 上运行时支持剪贴板操作。将文本放入删除环,或从删除环中提取文本的命令,会优先检查 Windows 剪贴板,就像 Emacs 在 X 窗口系统上的行为一样(参见 “编辑用鼠标命令”)。在 Windows 上运行的 MS‑DOS 版 Emacs 仅支持主选择区和剪切缓冲区;次选择区始终为空。
由于 Windows 实现剪贴板访问的方式,你能放入剪贴板的文本长度受限于 Emacs 可用的空闲 DOS 内存。通常最多可放入约 620KB 的文本,但该限制取决于系统配置;如果将 Emacs 作为其他程序的子进程运行,限制会更小。如果被删除的文本超出容量,Emacs 会输出提示信息,并且不会将文本放入剪贴板。
空字符(Null character)同样无法放入 Windows 剪贴板。如果被删除的文本包含空字符,Emacs 不会将其放入剪贴板,并在回显区显示相应提示。
变量 dos‑display‑scancodes 非空时,会让 Emacs 显示每个按键的 ASCII 值与键盘扫描码;该功能可作为 view‑lossage 命令的补充,用于调试。
I.12.3 MS-DOS 上的显示
MS‑DOS 下的显示 不支持粗体、斜体 这类字体变体,但支持多种face(显示样式),每种 face 都可以指定前景色与背景色。因此,通过为相关 face 定义不同颜色,你可以完整使用依赖字体的 Emacs 包(如 font-lock 、Enriched Text 模式等)。使用命令 list-colors-display (参见 “Face 的颜色”)和 list-faces-display (参见 “文本 Face”)可以查看可用的颜色与 face 及其显示效果。
关于 Emacs 如何显示 DOS 显示内置原生字体不支持的字形与字符,参见本章稍后的MS‑DOS 下的国际化支持。
Emacs 启动时会将光标形状改为 实心方块 ,这是为了与其他系统兼容(在那些系统中,方块光标是 Emacs 默认样式)。可以通过在变量 default-frame-alist 中指定 cursor-type 参数,将默认形状改为横条(参见 “创建 Frame”)。MS‑DOS 终端不支持垂直竖线光标,因此 “条型光标” 实际是水平横条;如果 Frame 参数中指定了宽度(width),它实际决定的是高度。因此在 MS‑DOS 上,bar 与 hbar 两种光标类型效果相同。
作为扩展,bar 光标可以同时指定光标起始扫描行与宽度,写法如下:
'(cursor-type bar width . start)
此外,如果 width宽度 参数为负数,光标横条会从字符单元的顶部开始。
MS‑DOS 终端同一时间只能显示一个 Frame。MS‑DOS 上的 Emacs Frame 机制与文本终端基本一致(参见 “Frame 与图形显示”)。在 Windows 的 DOS 窗口中运行 Emacs 时,你可以让可见 Frame 小于全屏,但同一时间仍然只能显示一个 Frame。
dos-mode4350命令:根据硬件将显示切换为 43 行或 50 行;dos-mode25命令:切换回默认的 80×25 屏幕尺寸。
默认情况下,Emacs 只支持设置以下屏幕尺寸:80 列 × 25、28、35、40、43、50 行。
如果你的显卡有特殊视频模式,可以切换到其他尺寸,也可以让 Emacs 支持。当你要求 Emacs 将 Frame 改为 n 行 × m 列 时,它会检查是否存在名为 screen-dimensions-nxm 的变量;如果有,就使用该变量的值(必须是整数)作为要切换的视频模式。(Emacs 会通过在 AL 寄存器中传入 screen-dimensions-nxm 的值,调用 BIOS Set Video Mode 函数来切换。)
例如:假设你的显卡在视频模式 85 下会切换到 66×80,那么可以在 _emacs 中加入:
(setq screen-dimensions-66x80 85)
由于 MS‑DOS 上的 Emacs 只能将 Frame 尺寸设为特定支持的分辨率,并非所有调整大小请求都能满足。当请求不支持的尺寸时,Emacs 会选择比指定尺寸更大的下一个支持尺寸。例如:请求 36×80,实际会得到 40×80。
screen-dimensions-nxm 变量只在与请求尺寸完全匹配时生效;寻找 “更大的下一个尺寸” 时不会考虑这些变量。上面的例子中,即使你的 VGA 支持 38×80 并定义了 screen-dimensions-38x80,请求 36×80 时仍然会得到 40×80。如果你希望这种情况下得到 38×80,可以把 screen-dimensions-36x80 设为与 screen-dimensions-38x80 相同的视频模式值。
在 MS‑DOS 下修改 Frame 尺寸,会把所有其他 Frame 也改成新尺寸。
I.12.4 MS-DOS 上的文件名
在 MS‑DOS 中,文件名不区分大小写,且长度限制为:主文件名 8 个字符 + 可选点号 + 扩展名 3 个字符。Emacs 充分了解这些限制,能够处理原本为其他操作系统设计的文件名。
例如:文件名以点开头在 MS‑DOS 中是非法的,因此 Emacs 会 自动透明地将开头的点转换为下划线 '_' ;所以你的默认初始化文件(参见《Emacs 初始化文件》)在 MS‑DOS 下名为 _emacs 。超出 8+3 长度限制的字符,通常会被 MS‑DOS 直接忽略:比如你打开 LongFileName.EvenLongerExtension ,系统实际会生成 longfile.eve ,但 Emacs 仍会在模式行上显示完整的长文件名。除此之外,你需要自己保证输入的文件名在 MS‑DOS 下合法;上面所说的自动转换,只对 Emacs 内置的文件名生效。
MS‑DOS 对文件名的上述限制,使得备份文件几乎一定会丢失原文件名的部分字符(参见单备份或编号备份)。例如: docs.txt 的备份文件,即使只使用单备份,文件名也会变成 docs.tx~ 。
如果你在 Windows 9X、ME、2000/XP 下以 DOS 应用程序的方式运行 Emacs,可以开启长文件名支持。开启后,Emacs 不会截断文件名,也不会自动转小写,而是原样使用你输入的文件名。开启方法:在启动 Emacs 前,将环境变量 LFN 设置为 'y'。遗憾的是,Windows NT 不允许 DOS 程序访问长文件名,因此 MS‑DOS 版 Emacs 在该系统上只能看到短 8+3 别名。
MS‑DOS 没有家目录(home directory) 的概念,因此 MS‑DOS 版 Emacs 会把自身安装目录当作 HOME 环境变量的值。例如:如果 emacs.exe 在 c:/utils/emacs/bin 目录下,Emacs 就会把 HOME 视为 c:/utils/emacs ,并会在这个目录下寻找初始化文件 _emacs 。有了这个规则,你就可以像在 GNU/Unix 系统上一样,在文件名中使用 '~' 来表示家目录。你也可以在启动 Emacs 前手动设置 HOME 环境变量,它会覆盖上面的默认行为。
MS‑DOS 版 Emacs 会特殊处理 /dev 这个名称,因为 DJGPP 模拟库会把 I/O 设备伪装成该目录下的文件。我们建议你不要在任何磁盘上创建真实名为 /dev 的目录。
I.12.5 MS-DOS 上的打印功能
在没有 POSIX 风格的 lpr 程序时,诸如 lpr-buffer (参见 “打印硬拷贝”)和 ps-print-buffer (参见 “PostScript 硬拷贝”)等打印命令,也可以在 MS‑DOS 下工作,方法是将输出发送到某个打印机端口。所有系统上的打印行为都由相同的 Emacs 变量控制,但在某些情况下,这些变量在 MS‑DOS 下的默认值会有所不同。
有关设置网络打印机打印的详细信息,请参见 “MS‑Windows 下的打印”。
有些打印机期望非 ASCII 文本使用 DOS 代码页编码,即使它们连接到使用同一语言环境下不同编码的 Windows 机器。例如,在 Latin‑1 语言环境中,DOS 使用代码页 850,而 Windows 使用代码页 1252。参见 “MS‑DOS 下的国际化支持”。当你从 Windows 向这类打印机打印时,可以在执行 M-x lpr-buffer 之前使用命令 C-x RET c (universal-coding-system-argument);这样 Emacs 会将文本转换为你指定的 DOS 代码页。例如: C-x RET c cp850-dos RET M-x lpr-region RET 会将选中区域转换为代码页 850 编码后打印。
为了向后兼容,在 MS‑DOS 下,如果 dos-printer (dos-ps-printer)有值,它会覆盖 printer-name (ps-printer-name) 的值。
I.12.6 MS-DOS 上的国际语言支持
MS‑DOS 版 Emacs 支持与 GNU、Unix 等平台相同的国际字符集(参见「国际字符集支持」),包括用于在不同字符集间转换的编码系统。但由于 MS‑DOS/MS‑Windows 与其他系统之间存在不兼容,你需要了解这项支持中与 DOS 相关的特定细节。本节将说明这些内容。
下面的描述主要针对 MS‑DOS 移植版 Emacs,尤其是对用户实际使用有影响的部分。
M-x dos-codepage-setup- 根据当前 DOS 代码页,自动配置 Emacs 的显示与编码系统。
MS‑DOS 设计为同一时刻只支持一套 256 个字符的字符集,但提供多种字符集供选择。这些备选字符集称为 DOS 代码页(codepage)。每个代码页都包含全部 128 个 ASCII 字符,但另外 128 个字符(编码 128–255)在不同代码页中各不相同。每个 DOS 代码页用一个 3 位数字标识,如 850、862 等。
与可以同时使用多种字体的 X 窗口不同,MS‑DOS 通常不允许在单个会话中使用多个代码页。MS‑DOS 设计为在系统启动时加载一个代码页,要更换通常需要重启系统34。在 Windows 等系统中运行 DOS 程序时,也基本受同样限制。
为在 MS‑DOS 上支持多字节字符,Emacs 需要知道当前选中的 DOS 代码页能显示哪些字符。因此它会在启动后查询系统,获取当前代码页编号,并保存在变量 dos-codepage 中。有些系统即使实际代码页不同,也会返回默认值 437(常见于使用显示硬件内置代码页的情况)。你可以在初始化文件中设置 dos-codepage 变量,为 Emacs 指定正确的代码页。
多字节版本的 Emacs 只支持特定的 DOS 代码页:
- 能显示远东文字的代码页,如日文代码页 932
- 编码单个 ISO 8859 字符集的代码页
远东代码页可以直接显示对应国家的 MULE 字符集,因此 Emacs 只需配置为使用该代码页支持的终端编码系统即可。本节其余部分描述的特殊功能,主要适用于对应 ISO 8859 字符集的代码页。
对于对应某个 ISO 字符集的代码页,Emacs 会根据代码页号识别字符集,并自动创建编码系统,用于读写使用当前代码页的文件,并默认使用该编码。该编码系统的名称为 cpnnn ,其中 nnn 是代码页号35。
所有 cpnnn 编码系统在模式行上的缩写为 D(表示 DOS)。由于启动时终端编码与默认文件 I/O 编码都会设为对应的 cpnnn,MS‑DOS 下模式行通常以 -DD- 开头。见模式行。远东 DOS 终端不使用 cpnnn 编码,因此初始模式行与标准 Emacs 一致。
由于代码页号也表明了你使用的文字体系,Emacs 会自动执行 set-language-environment 来选择对应语言环境。见语言环境
如果缓冲区中包含不属于当前 DOS 代码页支持的其他 ISO 8859 字符,Emacs 会用一串 ASCII 字符来显示它。例如:若当前代码页没有 ‘ò’(带重音符的小写 ‘o’),会显示为:‘{`o}’花括号用于视觉提示这是单个字符。虽然在屏幕上占多列,但它本质仍是单个字符,所有 Emacs 命令都会按单个字符处理。
MS‑Windows 有自己的代码页,同一语言环境下与 DOS 代码页不同。例如:
- DOS 代码页 850 ≈ Windows 代码页 1252
- DOS 代码页 855 ≈ Windows 代码页 1251
- Windows 版 Emacs 使用 '-nw' 参数启动时,会用 Windows 当前代码页显示。
I.12.7 MS-DOS 上的子进程
由于 MS‑DOS 是单进程 “操作系统”,异步子进程不可用。特别是 Shell 模式及其变体无法运行。Emacs 中绝大多数使用异步子进程的功能在 MS‑DOS 下也都无法工作,包括 Shell 模式和 GUD。如有疑问,可直接尝试;无法运行的命令会输出错误信息,提示不支持异步进程。
在 Emacs 中通过 M-x compile 编译、 M-x grep 搜索文件、 M-x diff 显示文件差异等功能可以正常使用,这些会以同步方式运行子进程。这意味着在子进程执行完毕前,你无法继续进行其他编辑操作。
拼写检查同样可用,通过专门支持同步调用 ispell 程序实现。这比其他平台上的异步调用速度更慢。
在 MS‑DOS 下无法使用 Shell 模式,你可以改用 M-x eshell 命令。该命令会启动 Eshell 包,它完全基于 Emacs Lisp 实现了类 POSIX 风格的 Shell。
与之相对,编译为原生 Windows 应用程序的 Emacs 支持异步子进程。详见《Windows 9X/ME 与 Windows NT/2K/XP/Vista/7/8/10 下的子进程》。
打印命令(如 lpr-buffer (参见 打印硬拷贝)和 ps-print-buffer (参见 PostScript 硬拷贝))在 MS‑DOS 下可正常工作,会将输出发送到某个打印机端口。详见《打印与 MS‑DOS》。
在 MS‑DOS 下同步运行子进程时,请确保程序会正常退出,且不会尝试读取键盘输入。如果程序无法自行终止,你将无法结束它,因为 MS‑DOS 没有通用的进程终止机制。在这种情况下,按下 C-c 或 C-Break 有时可能有效。
MS‑DOS 不支持访问其他机器上的文件。其他面向网络的命令(如发送邮件、网页浏览、远程登录等)也无法使用,除非通过某些网络重定向程序为 MS‑DOS 内置了网络访问能力。
MS‑DOS 下的 Dired 使用 ls-lisp 包(参见 MS-Windows 下的 ls 模拟)。因此,MS‑DOS 下的 Dired 仅支持 dired-listing-switches 变量中部分可选参数。可用的参数有:-A、-a、-c、-i、-r、-S、-s、-t 和 -u。
GNU 宣言
下面这篇《GNU 宣言》由理查德・斯托曼在 GNU 项目启动之初撰写,旨在呼吁人们参与并支持该项目。最初几年,它曾根据项目进展做过小幅更新,但如今,鉴于大多数人都已读过这份宣言,保持原文不变似乎是最好的选择。
从那时起,我们发现一些常见的误解,若换种表述方式本可避免。1993 年添加的脚注有助于澄清这些问题。
有关可用 GNU 软件的最新信息,请访问我们的网站:https://www.gnu.org。如需了解软件任务及其他贡献方式,请参阅:https://www.gnu.org/help。
什么是 GNU?GNU 不是 Unix!
GNU 是 “GNU's Not Unix” 的递归缩写,指的是一套与 Unix 完全兼容的自由软件系统。我正在编写这套系统,以便能免费向所有可以使用它的人开放使用。36已有多名志愿者加入我的工作,我们非常需要时间、资金、程序与设备方面的捐助。
到目前为止,我们已经完成:支持 Lisp 以编写编辑器命令的 Emacs 文本编辑器、源码级调试器、与 yacc 兼容的语法分析器生成器、链接器,以及大约 35 个实用工具。Shell(命令解释器)已接近完成。一款全新的可移植优化 C 编译器已实现自举,有望在今年发布。初始内核已经存在,但要完整模拟 Unix 还需要更多功能。内核与编译器完成后,我们就可以发布一套适用于程序开发的 GNU 系统。我们将使用 TeX 作为文本格式化工具,同时也在开发 nroff。我们还会使用免费、可移植的 X 窗口系统。在此之后,我们将添加可移植的 Common Lisp、Empire 游戏、电子表格、数百种其他工具,以及在线文档。我们希望最终能提供一套 Unix 系统通常附带的所有有用功能,甚至更多。
GNU 能够运行 Unix 程序,但不会与 Unix 完全相同。我们会基于在其他操作系统上的经验,在方便的前提下做出一切改进。具体来说,我们计划支持更长的文件名、文件版本号、防崩溃文件系统、可能的文件名补全、与终端无关的显示支持,最终或许还会实现一套基于 Lisp 的窗口系统,让多个 Lisp 程序与普通 Unix 程序共享屏幕。C 与 Lisp 都将作为系统编程语言提供。我们会尽力支持 UUCP、MIT Chaosnet 与互联网通信协议。
GNU 最初面向带虚拟内存的 68000/16000 系列机器,因为这类平台最容易移植。让它在更小的机器上运行的额外工作,将留给有需要的人去完成。
为避免严重混淆,当 “GNU” 作为项目名称时,请务必读出单词中的字母 “G”。
我为何必须编写 GNU
我认为,根据黄金法则,如果我喜欢一个程序,就必须与其他同样喜欢它的人共享。软件商试图分化并征服用户,让每个用户都承诺不与他人共享。我拒绝以这种方式破坏与其他用户的团结。凭良心,我无法签署任何保密协议或软件许可协议。多年来,我在人工智能实验室工作,一直抵制这类倾向与其他不友好的做法,但最终这些行为愈演愈烈:我无法再留在一个违背我意愿、强迫我接受这些事的机构。
为了能继续体面地使用计算机,我决定打造一套足够完备的自由软件体系,让自己可以完全不依赖非自由软件。我已从人工智能实验室辞职,让麻省理工学院无法以任何法律借口阻止我发布 GNU。
为何 GNU 要与 Unix 兼容
Unix 并非我理想中的系统,但也不算太差。Unix 的核心特性相当不错,我认为可以在不破坏这些特性的前提下补足它的短板。而且一套与 Unix 兼容的系统,也便于更多人接纳使用。
GNU 的获取方式
GNU 不属于公有领域。任何人都可以修改和再分发 GNU,但任何发布者都无权限制他人进一步再分发。也就是说,不允许专有化修改。我要确保 GNU 的所有版本始终保持自由。
为何众多程序员愿意提供帮助
我发现许多程序员对 GNU 充满热情,并愿意提供帮助。
很多程序员对系统软件的商业化感到不满。商业化或许能让他们赚更多钱,却让他们与其他程序员普遍处于对立关系,而非同伴关系。程序员之间友谊的根本体现是共享程序;而如今通行的商业模式,本质上禁止程序员以朋友相待。软件购买者必须在友谊与守法之间二选一。自然,很多人会认为友谊更重要。但那些信奉法律的人,无论选择哪一边都不会心安。他们变得愤世嫉俗,认为编程不过是一种赚钱手段。
通过开发和使用 GNU 而非专有软件,我们既能对所有人友善,又能遵守法律。此外,GNU 可以作为一面旗帜,激励并号召更多人加入共享的行列。这能带来一种使用非自由软件时无法获得的和谐感。对我接触过的大约半数程序员而言,这是一种金钱无法替代的重要幸福。
你如何能做出贡献
我正在向计算机厂商寻求设备与资金捐赠,向个人寻求程序与劳动捐赠。
如果你捐赠设备,可以预期 GNU 会尽早在该设备上运行。这些设备应当是完整、可直接使用的系统,允许在居民区使用,无需复杂的冷却与供电设备。
我发现大量程序员愿意为 GNU 贡献兼职工作。对大多数项目而言,这类分布式兼职工作极难协调,独立编写的模块往往无法协同工作。但在替换 Unix 这一特定任务中,这个问题并不存在。一套完整的 Unix 系统包含数百个独立文档化的实用工具,大多数接口规范由 Unix 兼容性固定。只要每位贡献者能为单个 Unix 工具编写一个兼容的替代品,并能在 Unix 系统上正常替换原工具运行,那么把这些工具组合起来就可以正常工作。即便墨菲定律会带来一些意外问题,组装这些组件依然是可行的任务。(内核需要更紧密的协作,将由一个小而精的团队开发。)
如果获得资金捐赠,我可能会雇佣一些全职或兼职人员。薪资按程序员标准不算高,但我寻找的是那些把构建社区精神看得与赚钱同样重要的人。我希望以此让专注的开发者不必为生计分心,能全身心投入 GNU 开发。
为何所有计算机用户都将受益
为何所有计算机用户都将从中受益
GNU 完成之后,每个人都能免费获得优质的系统软件,就像空气一样。37
这远不止是省下一套 Unix 许可的费用。它意味着可以避免大量重复、浪费的系统开发工作,让这些精力转而用于推动技术前沿。
完整的系统源码将向所有人开放。因此,需要修改系统的用户可以自由自行修改,或雇佣任何程序员 / 公司代为修改。用户将不再受制于拥有源码、唯一有权修改的程序员或公司。
学校可以鼓励所有学生学习和改进系统代码,从而提供更具教育意义的环境。哈佛大学计算机实验室曾经规定:任何程序若不公开源码,就不允许安装在系统上,并确实据此拒绝安装某些软件。我深受这一理念启发。
最终,我们将不再需要费心考虑系统软件归谁所有、自己有权或无权做什么。
要求用户为使用程序付费的机制(包括复制许可),总要通过繁琐手段核算每个人该为哪些程序付费,给社会带来巨大成本。而只有极权国家才能强迫所有人遵守。想象一座空间站,空气必须以高昂成本制造:按每升向呼吸者收费看似公平,但全天候戴着计量呼吸面罩,即便人人付得起费用也令人无法忍受。到处布置监控摄像头看你是否摘下面罩更是离谱。更好的方式是用人头税支持空气制造,扔掉面罩。
对程序员来说,复制一个程序的全部或部分,就像呼吸一样自然、一样有价值。它本应是自由的。
对 GNU 目标的一些易反驳的反对意见
“如果免费,就没人会用,因为无法依赖任何支持。”
“必须为程序收费,才能支付支持服务的成本。”
如果人们宁愿为 GNU 付费并获得服务,也不愿免费使用无服务的 GNU,那么一家只为免费获得 GNU 的用户提供服务的公司,理应可以盈利。38
我们必须区分真正编程工作形式的支持与单纯的手把手指导。前者无法指望软件商提供。如果你的问题没有足够多人遇到,厂商只会让你自生自灭。
如果你的业务需要可靠支持,唯一的办法是拥有所有必需的源码与工具。然后你可以雇佣任何有能力的人解决问题,而不受制于任何人。对 Unix 而言,源码的价格让大多数企业望而却步;对 GNU 而言,这将轻而易举。当然,仍可能出现没有合格人员的情况,但这不能归咎于发布方式。GNU 不能解决世界上所有问题,只能解决一部分。
与此同时,对计算机一无所知的用户需要手把手指导:替他们完成那些他们本可轻松做到却不知道怎么做的事。
这类服务可以由专门提供指导与维修的公司提供。如果用户真的愿意花钱购买带服务的产品,他们也会愿意在免费获得产品后购买服务。服务公司会在质量与价格上竞争,用户不会被绑定到某一家。而我们这些不需要服务的人,也可以不付费就使用程序。
“不打广告就触达不了多少人,而你必须为程序收费来支撑广告。”
“给免费程序打广告没用。”
有各种免费或极低成本的宣传方式,可以让大量计算机用户了解 GNU 这类项目。但广告或许确实能触达更多微型计算机用户。如果真是这样,一家通过广告提供收费复制与邮寄 GNU 服务的公司,理应足以盈利并覆盖广告成本。这样一来,只有从广告中受益的用户才会为此付费。
另一方面,如果很多人从朋友那里获取 GNU,这类公司无法成功,那就说明传播 GNU 其实并不需要广告。为什么自由市场的拥护者不愿意让自由市场来决定这一点呢?39
“我的公司需要专有操作系统才能获得竞争优势。”
GNU 将把操作系统软件移出竞争领域。你无法在这一领域获得优势,但你的竞争对手也无法胜过你。你们可以在其他领域竞争,同时在这一领域互惠互利。如果你的业务是销售操作系统,你可能不会喜欢 GNU,但那是你自己的问题。如果你的业务并非如此,GNU 可以让你避免被迫进入昂贵的操作系统销售业务。
我希望 GNU 的开发能得到众多厂商与用户的捐赠支持,从而降低每个人的成本。40
“程序员的创造力难道不配得到回报吗?”
如果说有什么值得回报,那是对社会的贡献。创造力可以是一种社会贡献,但前提是社会可以自由使用其成果。如果程序员因创造创新程序而理应获得回报,那么同理,当他们限制这些程序的使用时,也理应受到惩罚。
“程序员难道不能为自己的创造力要求回报吗?”
为劳动索取报酬、追求收入最大化本身并无过错,只要不使用破坏性手段。但如今软件领域的通行手段,正是建立在破坏之上。
通过限制用户使用程序来榨取金钱是破坏性的,因为这些限制减少了程序可用的范围与方式,降低了人类从该程序中获得的财富总量。当有人刻意选择限制时,其造成的有害后果就是蓄意破坏。
一个好公民不使用这类破坏性手段致富的原因是:如果人人都这么做,我们都会因相互破坏而变得更贫穷。这是康德伦理学,也是黄金法则。既然我不喜欢人人囤积信息所带来的后果,我就必须认为这种行为是错误的。具体来说,渴望为创造力获得回报,并不能成为剥夺全世界享用这一创造力全部或部分成果的理由。
“程序员难道不会饿死吗?”
我可以回答:没人强迫你当程序员。我们大多数人站在街上做鬼脸也赚不到钱,但这并不意味着我们注定要一辈子站在街上做鬼脸、饿肚子。我们会去做别的事。
但这是错误的回答,因为它接受了提问者隐含的假设:没有软件所有权,程序员就一分钱都赚不到。仿佛这是非此即彼的极端。
程序员不会饿死的真正原因是:他们仍然可以靠编程获得报酬,只是不像现在这么多。
限制复制并非软件行业唯一的商业模式。它之所以最普遍,只是因为最赚钱。如果这种模式被禁止或被用户拒绝,软件行业会转向如今较少使用的其他组织形式。任何行业都总有无数种组织方式。
在新模式下,编程可能不像现在这么暴利。但这并不能成为反对变革的理由。售货员现在的薪资并不被视为不公;如果程序员收入与之相当,也同样不算不公。(实际上他们的收入仍会高得多。)
“人们难道无权控制自己创造力的使用方式吗?”
“控制自己思想的使用”,实际上就是控制他人的生活,而且通常只会让他人的生活更艰难。
仔细研究过知识产权问题的人41(如律师)表示,知识产权并不存在天然权利。政府承认的所谓知识产权,是为特定目的通过具体立法创设的。
例如,专利制度的设立是为了鼓励发明人公开其发明细节,目的是造福社会,而非帮助发明人。当时,17 年的专利期限相对于技术进步速度而言并不算长。由于专利只在厂商之间产生影响,对他们来说,许可协议的成本与精力相比投产而言微不足道,因此专利通常危害不大,也不会妨碍大多数使用专利产品的个人。
版权概念在古代并不存在,当时非虚构作品中作者经常大段摘抄他人作品。这种做法是有益的,也是许多作者作品得以部分留存的唯一途径。版权制度专为鼓励创作而设立。在它诞生的领域 —— 只能通过印刷机低成本复制的书籍 —— 它危害很小,也不会妨碍大多数读书的人。
所有知识产权都只是社会授予的许可,无论对错,人们认为这样做整体上对社会有利。但在任何具体情境下,我们都必须问:授予这类许可真的让我们更好吗?我们许可一个人做什么样的行为?
如今的程序与百年前的书籍截然不同。复制程序最简单的方式是邻里之间互相拷贝;程序既有源码又有目标码,彼此独立;程序是用来使用的,而非阅读欣赏。这些事实共同造成了一种局面:强制执行版权的人,在物质与精神上都在损害整个社会;无论法律是否允许,人们都不应该这么做。
“竞争能把事情做得更好。”
竞争的典型范式是赛跑:奖励胜者,鼓励所有人跑得更快。当资本主义真的以这种方式运行时,效果不错;但它的捍卫者错误地认为它永远如此。如果赛跑者忘记奖励的初衷,一心只想不择手段获胜,他们可能会采取其他策略 —— 比如攻击其他选手。如果选手们大打出手,所有人都会晚到终点。
专有与保密软件,在道德上等同于赛跑中的斗殴。遗憾的是,我们唯一的裁判似乎并不反对斗殴,只是加以管制(“每跑十码可以开一枪”)。他真正应该做的是制止斗殴,并惩罚任何试图斗殴的选手。
“没有金钱激励,人人都会停止编程吗?”
事实上,很多人会在完全没有金钱激励的情况下编程。对某些人(通常也是最擅长编程的人)来说,编程有着无法抗拒的魅力。就像职业乐手,即便无望以此为生,依然坚持演奏。
但这个问题虽然常见,却并不适用于当前情况。程序员的报酬不会消失,只是会减少。所以正确的问题是:金钱激励降低后,还会有人编程吗?我的经验表明:会的。
十多年来,世界上许多最优秀的程序员在人工智能实验室工作,薪资远低于他们在其他地方能获得的水平。他们得到了各种非金钱回报:例如声誉与认可。而创造本身也是一种乐趣,一种内在回报。
后来,当有机会做同样有趣的工作却赚更多钱时,他们大多数人都离开了。
事实表明:人们会为财富以外的原因编程;但如果有机会同时赚大钱,他们就会开始期待并要求更高收入。低薪组织在与高薪组织的竞争中处于劣势,但如果高薪模式被禁止,低薪组织也未必表现糟糕。
“我们极度需要程序员。如果他们要求我们停止帮助邻居,我们就必须服从。”
你永远不会绝望到必须服从这种要求。记住:百万军费可出,一分贡金不纳!
“程序员总得想办法谋生。”
短期来看,这是事实。但程序员有很多不靠出售程序使用权谋生的方式。现在这种方式通行,只是因为它给程序员与商人带来最多利润,而非唯一谋生手段。只要愿意,很容易找到其他方式。以下是一些例子:
推出新计算机的厂商会付费将操作系统移植到新硬件上。
教学、指导与维护服务的销售也可以雇佣程序员。
有新想法的人可以将程序作为免费软件发布42,向满意的用户寻求捐赠,或出售指导服务。我已经遇到成功以这种方式工作的人。
有相关需求的用户可以组成用户组并缴纳会费。小组可以与编程公司签约,编写小组成员需要使用的程序。
各类开发都可以通过软件税资助:
- 假设每台购买计算机的人都必须按价格的一定比例缴纳软件税。政府将这笔钱交给国家科学基金会之类的机构,用于软件开发。
- 但如果计算机购买者自己向软件开发项目捐赠,就可以抵扣税款。他可以捐赠给自己选择的项目 —— 通常是因为希望使用其最终成果。捐赠额度最高可抵扣全部应缴税款。
- 总税率可由纳税人投票决定,按纳税金额加权。
- 其结果是:
- 计算机使用群体支持软件开发。
- 该群体决定需要何种水平的支持。
- 关心自己份额用于哪些项目的用户可以自主选择。
从长远来看,让程序自由是迈向后稀缺世界的一步:那时没人需要为生计拼命工作。人们在每周完成必要工作(如立法、家庭咨询、机器人维修、小行星勘探)的十小时后,可以自由投入自己喜欢的活动,比如编程。人们将不再需要靠编程谋生。
我们已经大幅降低了整个社会为实际生产力所需付出的工作量,但其中只有一小部分转化为劳动者的闲暇,因为生产活动伴随大量非生产性消耗。主要原因是官僚主义与零和竞争。自由软件将大幅减少软件开发领域的这类消耗。我们必须这样做,才能让生产力的技术进步真正转化为我们更少的劳动。
术语表
缩写(Abbrev)- 缩写是一段文本字符串,在缓冲区中出现时会展开成另一段文本。例如,你可以把几个字母定义为缩写,用来快速插入常用的长文本。参见:缩写(Abbrevs)。
中止(Aborting)- 中止指退出递归编辑(参见相关词条)。可使用命令
C-]和M-x top-level。参见:退出与中止(Quitting and Aborting)。 激活区域(Active Region)- 在文本中设置标记(参见相关词条)的同时会激活它。标记处于激活状态时,我们称该区域为激活区域。参见:标记与区域(The Mark and the Region)。
Alt 键(Alt)- Alt 是键盘输入字符可带有的一个修饰位名称。输入带 Alt 的字符时,需按住 Alt 键再按对应字符。这类字符以 Alt- 开头(通常简写为 A-)。(注意:许多终端标为 Alt 的键实际是 Meta 键。)参见:Alt 键。
参数(Argument)- 参见:术语表 —— 数字参数(Numeric Argument)。
ASCII 字符(ASCII character)- ASCII 字符包括 ASCII 控制字符和 ASCII 可打印字符。参见:用户输入类型(Kinds of User Input)。
ASCII 控制字符(ASCII control character)- ASCII 控制字符是大写字母的 Control 组合,或是
@[\]^_?中某个字符的 Control 组合。 ASCII 可打印字符(ASCII printing character)- ASCII 字母、数字、空格,以及以下标点符号:
!@#$%^&*()_-+=|\~`{}[]:;"'<>,.?/’ 自动换行模式(Auto Fill Mode)- 自动换行模式是一种次要模式(参见相关词条),插入的文本会自动按指定最大宽度分行。参见:文本填充(Filling Text)。
自动保存(Auto Saving)- 自动保存是定期将 Emacs 缓冲区内容存入特殊命名文件的机制,以防系统或用户错误导致缓冲区内容丢失。参见:自动保存:灾难防护(Auto-Saving: Protection Against Disasters)。
自动加载(Autoloading)- 当 Lisp 程序需要某个库中的函数时,Emacs 可自动加载对应的 Lisp 库,这称为自动加载。参见:Emacs Lisp 代码库(Libraries of Lisp Code for Emacs)。
调用回溯(Backtrace)- 调用回溯是一系列函数调用的轨迹,用于显示程序执行到某点的过程,主要用于查找和修复错误(参见相关词条)。Emacs 出错或按下 C-g (参见术语表–Quitting)时可显示回溯。参见:错误报告清单(Checklist for Bug Reports)。
备份文件(Backup File)- 备份文件记录文件在当前编辑会话之前的内容。Emacs 会自动生成备份文件,方便回退或撤销后悔的修改。参见:备份文件(Backup Files)。
括号匹配(Balancing Parentheses)- Emacs 可手动或自动匹配括号(或其他配对分隔符)。手动匹配:使用在括号结构中移动的命令。自动匹配:插入分隔符时闪烁 / 高亮配对分隔符,或自动插入配对分隔符。参见:括号结构移动(Moving in the Parenthesis Structure)、配对括号(Matching Parens)。
平衡表达式(Balanced Expressions)- 平衡表达式是语法上可识别的表达式,例如符号(参见相关词条)、数字、字符串常量、代码块、C 语言带括号表达式等。参见:平衡表达式(Balanced Expressions)。
气泡帮助(Balloon Help)- 参见:术语表 —— 工具提示(Tooltips)。
基缓冲区(Base Buffer)- 基缓冲区是被间接缓冲区(参见相关词条)共享文本的缓冲区。
双向文本(Bidirectional Text)- 部分语言(如英文)从左到右书写,另一些(如阿拉伯文)从右到左。Emacs 同时支持这两种方向及其混合,称为双向文本。参见:双向编辑(Bidirectional Editing)。
绑定(Bind)- 为一个按键序列指定绑定(参见相关词条)称为绑定。参见:交互式修改按键绑定(Changing Key Bindings Interactively)。
绑定(Binding)- 按键序列在 Emacs 中的含义由绑定决定:绑定是一个命令(参见相关词条),即按下该序列时运行的 Lisp 函数。自定义常涉及将字符重新绑定到其他命令函数。所有按键序列的绑定都记录在按键映射(参见相关词条)中。参见:绑定(Binding)、按键映射(Keymaps)。
空行(Blank Lines)- 空行是只包含空白字符的行。Emacs 提供多个操作缓冲区空行的命令。参见:空行(Blank Lines)。
书签(Bookmark)- 书签与寄存器(参见相关词条)类似,用于记录缓冲区位置以便后续返回。与寄存器不同,书签在 Emacs 会话间持久保存。参见:书签(Bookmarks)。
边框(Border)- 边框是窗口边缘的细边,仅用于间距,不显示内容。Emacs 窗口有外部边框(包含菜单栏等所有内容之外)和内部边框(围绕文本窗口、滚动条、边缘区,与菜单栏 / 工具栏分隔)。可通过选项和资源自定义内外边框。边框与边缘区(参见相关词条)不同。参见:内部与外部边框(Internal and Outer Borders)。
缓冲区(Buffer)- 缓冲区是 Emacs 的基本编辑单元,一个缓冲区对应一份正在编辑的文本。通常同时存在多个缓冲区,但任意时刻只编辑一个当前缓冲区;多窗口 / 多帧(参见相关词条)下可同时显示多个。大多数缓冲区都关联(参见相关词条)某个文件。参见:使用多缓冲区(Using Multiple Buffers)。
缓冲区选择历史(Buffer Selection History)- Emacs 记录每个缓冲区最近被选中的时间,用于决定下次切换到哪个缓冲区。参见:使用多缓冲区(Using Multiple Buffers)。
错误(Bug)- 错误指程序不正确、不合理的行为,或文档不准确、易混淆的内容。Emacs 开发者非常重视代码与文档错误,欢迎报告。参见:报告错误(Reporting Bugs)。
按下事件(Button Down Event)- 按下鼠标按键时立即产生的输入事件(参见相关词条)。参见:重新绑定鼠标按键(Rebinding Mouse Buttons)。
默认(By Default)- 参见:术语表 —— 默认(Default)。
字节编译(Byte Compilation)- 参见:术语表 —— 编译(Compilation)。
cf.c.f.- 拉丁语 confer 的缩写,意为 “参见”“比较”。c.f. 是常见错误拼写。
C-- 字符名中的 C- 是 Control 的缩写。参见:C-。
C-M-- 字符名中的 C-M- 是 Control-Meta 的缩写。如果终端没有真实 Meta 键,可先按 ESC 再按对应 Control 字符输入。参见:C-M-。
大小写转换(Case Conversion)- 大小写转换指将文本在大写、小写之间切换。参见:大小写转换命令(Case Conversion Commands)。
大小写折叠(Case Folding)- 大小写折叠指忽略同一字母大小写、标题写法的差异。Emacs 文本搜索默认启用大小写折叠。参见:搜索时宽松匹配(Lax Matching During Searching)。
字符(Character)- 字符构成 Emacs 缓冲区内容,按键序列(参见相关词条)通常也由字符组成(也可包含其他输入事件)。参见:用户输入类型(Kinds of User Input)。
字符折叠(Character Folding)- 字符折叠指忽略外形相似字符的差异(如 a、ä、á)。Emacs 文本搜索默认启用字符折叠。参见:搜索时宽松匹配(Lax Matching During Searching)。
字符集(Character Set)- Emacs 支持多种字符集,每种对应特定字母或书写系统。参见:国际字符集支持(International Character Set Support)。
字符终端(Character Terminal)- 参见:术语表 —— 文本终端(Text Terminal)。
点击事件(Click Event)- 按下并松开鼠标按键、且未移动鼠标时产生的输入事件(参见相关词条)。参见:重新绑定鼠标按键(Rebinding Mouse Buttons)。
客户端(Client)- 参见:术语表 —— 服务器(Server)。
剪贴板(Clipboard)- 剪贴板是窗口系统提供的缓冲区,用于应用间文本传输。X Window 系统中剪贴板与主选择区(参见相关词条)并存;Windows、macOS 中只用剪贴板。参见:使用剪贴板(Using the Clipboard)。
编码系统(Coding System)- 编码系统是文件或数据流中字符的编码方式。Emacs 读写文件时可在多种编码间转换。参见:编码系统(Coding Systems)。
命令(Command)- 命令是专门定义为可按键绑定或按名调用的 Lisp 函数(也叫交互式函数,二者同义)。按下按键序列时,Emacs 在对应按键映射(参见相关词条)中查找绑定并执行命令。参见:按键与命令(Keys and Commands)。
命令历史(Command History)- 参见:术语表 —— 小缓冲区历史(Minibuffer History)。
命令名(Command Name)- 命令名是作为命令的 Lisp 符号的名称(见:Keys and Commands)。可使用 M-x 按命令名调用。参见:按名运行命令(Running Commands by Name)。
注释(Comment)- 注释是程序中仅供人阅读、会被编译器 / 加载器忽略的标记文本。Emacs 提供专门命令创建、对齐、删除注释。参见:操作注释(Manipulating Comments)。
Common Lisp- Common Lisp 是比 Emacs Lisp 更庞大强大的 Lisp 方言。Emacs 的 CL 包提供其部分功能。参见:Common Lisp 扩展概述(Overview in Common Lisp Extensions)。
编译(Compilation)- 编译是从源代码生成可执行程序的过程。Emacs 可编译 Emacs Lisp 文件(字节编译)与 C 等语言程序。字节编译后的 Lisp 代码加载与运行更快。参见:Emacs Lisp 参考手册・字节编译、在 Emacs 中运行编译(Running Compilations under Emacs)。
完整按键(Complete Key)- 完整按键是唯一指定一个 Emacs 动作的按键序列,如 X、C-f、C-x m。完整按键通过绑定(参见相关词条)到命令(参见相术语表—Bind)))获得含义。参见:按键(Keys)。
补全(Completion)- 补全是 Emacs 自动将名称缩写展开为全名的功能,常用于小缓冲区(参见相关词条)参数:命令名、缓冲区名、文件名等。通常按 TAB、SPC、RET 触发。参见:补全(Completion)。
续行(Continuation Line)- 文本行长度超过窗口宽度时,会显示为多个屏幕行,第一行之后的行称为续行。相关功能:填充(参见术语表—Truncation))。参见:续行(Continuation Lines)。
控制字符(Control Character)- 按住
Ctrl键输入的字符为控制字符。部分控制字符有独立按键(如 RET、TAB、ESC、DEL)。参见:用户输入类型(Kinds of User Input)。 Copyleft著作左(Copyleft) 是一种向公众授予合法权限的声明,允许其重新分发和修改程序或其他作品,但要求修改后的版本也必须附带相同的授权许可。版权通常被用来让用户彼此孤立、无能为力;而借助著作左,我们扭转这一局面,赋予用户权利并鼓励他们相互协作。
GNU 项目所使用的特定著作左形式称为 GNU 通用公共许可证。详见《GNU 通用公共许可证》。
Ctrl- Ctrl 键用于输入控制字符(参见相关词条)。参见:术语表 ——C-。
当前缓冲区(Current Buffer)- 大多数编辑命令作用于当前缓冲区,可将任意缓冲区设为当前。参见:使用多缓冲区(Using Multiple Buffers)。
当前行(Current Line)- 光标所在行即为当前行(参见:光标 Point)。
当前段落(Current Paragraph)- 光标所在段落为当前段落;光标在两段之间时,后一段为当前段落。参见:段落(Paragraphs)。
当前函数定义(Current Defun)- 光标所在的顶层定义(defun)为当前 defun;光标在多个 defun 之间时,后一个为当前 defun。参见:顶层定义或 Defun(Top-Level Definitions, or Defuns)。
光标(Cursor)- 光标是屏幕上指示插入 / 删除位置(即光标点,参见相关词条)的矩形块,位于光标点后一字符处。人们常说的 “光标” 实际指 “光标点”。参见:光标(Cursor)。
自定义(Customization)- 自定义是按偏好修改 Emacs 行为,通常通过设置变量(参见变量)、外观(参见自定义外观)、重新绑定按键(参见映射表)实现。
剪切粘贴(Cut and Paste)- 参见:术语表 —— 删除(Killing)、术语表 —— 粘贴(Yanking)。
守护进程(Daemon)- 守护进程是后台运行的系统级进程。Emacs 以守护进程模式运行时不打开显示,由 emacsclient 连接。参见:将 Emacs 用作服务器(Using Emacs as a Server)。
默认参数(Default Argument)- 未指定参数时使用的值即为默认参数。小缓冲区中直接按 RET 即使用默认参数。参见:迷你缓冲区(The Minibuffer)。
默认(Default)- 未显式指定值时自动采用的值。
默认目录(Default Directory)- 不以 / 或 ~ 开头的文件名,按当前缓冲区的默认目录解析。(Windows 中以盘符 x: 开头视为绝对路径。)参见:默认目录(Default Directory)。
Defun- Defun 是程序中的顶层主要定义,名称来自 Lisp 的 defun 结构。参见:顶层定义或 Defun(Top-Level Definitions, or Defuns)。
DELDEL字符执行删除光标前一个字符的命令,通常对应Delete或Backspace键。参见:DEL。删除(Deletion)- 删除指直接擦除文本,不存入删除环(参见相关词条)。与删除(Killing,剪切)相对。参见:删除(Deletion)。
删除文件(Deletion of Files)- 从文件系统中删除文件。部分系统提供回收站 / 废纸篓可恢复。参见:杂项文件操作(Miscellaneous File Operations)。
删除消息(Deletion of Messages)- 在邮件客户端中将邮件标记为待删除,清空(expunge)前可恢复。参见:删除消息(Deleting Messages)。
删除窗口(Deletion of Windows)- 从屏幕移除窗口,其他窗口自动占满空间。窗口内文本不会丢失,可重建相同大小窗口。参见:多窗口(Multiple Windows)。
目录(Directory)- 目录是文件系统中存放文件与子目录的集合,也叫 “文件夹”。参见:文件目录(File Directories)。
目录局部变量(Directory Local Variable)- 对某目录下所有文件生效的局部变量(参见相关词条)。参见:按目录局部变量(Per-Directory Local Variables)。
目录名(Directory Name)- 在 GNU 及其他类 Unix 系统中,目录名是以 / 结尾的字符串。例如,/no-such-dir/ 是一个目录名,而 /tmp 则不是,尽管 /tmp 所指向的文件恰好是一个目录。在微软 Windows 系统中,这一关系更为复杂。参见《Emacs Lisp 参考手册》中的目录名(Directory Names)章节。
Dired 模式- Dired 是 Emacs 目录编辑工具,可显示目录内容并 “编辑目录”,操作其中文件。参见:Dired,目录编辑器(Dired, the Directory Editor)。
禁用命令(Disabled Command)- 需确认才能运行的命令,通常因对新手不友好。参见:禁用命令(Disabling Commands)。
按下事件(Down Event)- “按键按下事件” 的简写(参见相关词条)。
拖动事件(Drag Event)- 按住鼠标按键移动后松开产生的输入事件(参见相关词条)。参见:重新绑定鼠标按键(Rebinding Mouse Buttons)。
调试文件(Dribble File)- Emacs 将所有键盘输入写入调试文件,用于调试 Emacs 错误。需手动开启。参见:报告错误(Reporting Bugs)。
e.g.- 拉丁语 exempli gratia 缩写,意为 “例如”。
回显区(Echo Area)- 屏幕最底行,用于回显命令参数、提问、显示简短消息(含错误)。消息保存在
*Messages*缓冲区。参见:回显区(The Echo Area)。 回显(Echoing)- 在回显区显示输入事件以确认接收。Emacs 不回显单字符按键,长按键序列暂停时才回显。
电动字符(Electric)- 普通情况下自动插入,但在当前主模式下被重新定义执行额外动作的字符称为电动字符。例如部分编程模式中,输入分隔符会自动缩进、换行。
行尾(End Of Line)- 表示文本行结束的字符或字符序列。类 Unix 系统为换行符(newline),其他系统有不同惯例。Emacs 可识别并转换多种行尾格式。参见:行尾(end-of-line)。
环境变量(Environment Variable)- 操作系统保存的命名变量,Emacs 可读取父 Shell 环境变量,并为子进程设置环境变量。参见:环境变量(Environment Variables)。
EOL- 参见:术语表 —— 行尾(End Of Line)。
错误(Error)- Emacs 命令无法在当前环境执行时发生错误,命令停止并显示错误信息(参见相关词条)。
错误信息(Error Message)- 执行无效操作或命令异常时 Emacs 输出的提示,出现在回显区并伴随提示音。
ESC- ESC 是一个字符,在没有 Meta 键的键盘上,它用作输入 Meta 字符的前缀。与 Meta 键(类似于 Shift 键,需要在输入另一个字符的同时按住)不同,你按 ESC 键就像按字母键一样,它会作用于你接下来输入的字符。
etc.- 拉丁语 et cetera 缩写,意为 “等等”。
表达式(Expression)- 参见:术语表 —— 平衡表达式(Balanced Expression)。
清空(Expunging)- 真正删除邮件、新闻组、Dired 中标记为删除的消息或文件。
外观(Face)- 外观是字符的显示样式,包括字体、前景 / 背景色、下划线、删除线等。Emacs 可用不同外观高亮缓冲区文本。参见:文本外观(Text Faces)。
文件局部变量(File Local Variable)- 在指定文件中生效的局部变量(参见相关词条)。参见:文件中的局部变量(Local Variables in Files)、术语表 —— 目录局部变量。
文件锁定(File Locking)- Emacs 用文件锁定检测多用户同时编辑同一文件。参见:并发编辑防护(Protection against Simultaneous Editing)。
文件名(File Name)- 指向文件的名称,分相对路径与绝对路径。Unix 绝对路径以
/、~/、~user/开头;Windows 可从盘符 x: 开头。我们只用 “路径” 表示搜索路径(search path),不用 “路径名”。 文件名分量(File-Name Component)- 目录内直接命名文件的部分。Unix 以 / 分隔,Windows 可用反斜杠 \。
填充前缀(Fill Prefix)- 填充文本时每行开头应出现、但不计入填充内容的字符串。参见:文本填充(Filling Text)。
填充(Filling)- 调整换行位置,使所有行长度大致均匀。其他编辑器也称 “自动换行”。参见:文本填充(Filling Text)。
语法高亮模式(Font Lock)- 根据语法用不同外观高亮文本的模式,其他编辑器常称 “语法高亮”。例如注释标为红色。参见:Font Lock 模式(Font Lock mode)。
字体集(Fontset)- 字体集是一组命名字体集合,指定每个字符集使用的字体,方便统一切换。参见:字体集(Fontsets)。
换页符(Formfeed Character)- 参见:术语表 —— 页(Page)。
框架(Frame)- 框架是 Emacs 窗口的矩形容器,启动时只有一帧,可创建多帧。每框架可分为多个 Emacs 窗口(参见相关词条)。窗口系统下可同时显示多帧。其他编辑器常把 “框架” 叫 “窗口”。参见:框架与图形显示(Frames and Graphical Displays)。
自由软件(Free Software)- 自由软件赋予用户共享、学习、修改的自由。Emacs 是自由软件,属于 GNU 项目,以 GNU 通用公共许可证发布。参见:GNU GENERAL PUBLIC LICENSE。
自由软件基金会(Free Software Foundation)- 致力于推动自由软件发展的非营利组织,简称 FSF。
边缘区(Fringe)- 在图形显示器(参见该词条)上,帧(参见该词条)中位于文本区域与窗口边框之间的狭长区域,称为边缘区。
这些 “边缘区” 用于显示符号,以提供与缓冲区文本相关的信息(见窗口边缘区)。Emacs 使用一个名为 fringe 的特殊外观(参见该词条)来显示边缘区。参见:fringe。
FSF- 参见:术语表 —— 自由软件基金会。
FTP- 文件传输协议,用于获取远程文件(参见相关词条)。
功能键(Function Key)- 键盘上不对应字符的输入键。参见:重新绑定功能键(Rebinding Function Keys)。
全局(Global)- 全局意为 “与当前环境无关,在整个 Emacs 生效”,与局部(local)相对。
全局缩写(Global Abbrev)- 在所有未定义局部缩写的主模式中生效的缩写。参见:缩写(Abbrevs)。
全局按键映射(Global Keymap)- 全局按键映射包含全局生效的绑定,除非被主模式局部按键映射(参见相关词条)覆盖。参见:按键映射(Keymaps)。
全局标记环(Global Mark Ring)- 记录最近设置过标记的缓冲区,可用于在缓冲区间回溯。参见:全局标记环(The Global Mark Ring)。
全局替换(Global Substitution)- 在大量文本中批量替换所有匹配字符串。参见:替换命令(Replacement Commands)。
全局变量(Global Variable)- 在未设置局部值的所有缓冲区中生效的变量值。参见:变量(Variables)。
GNU- GNU 是 “GNU's Not Unix” 的递归缩写,指一套类 Unix 自由操作系统。通常与 Linux 内核配合使用。参见:GNU 宣言(The GNU Manifesto)。
图形字符(Graphic Character)- 有可视图案而非仅名称的字符。不含 Control、Meta 修饰,包括字母、数字、标点、空格,不含 RET、ESC。普通编辑模式下输入图形字符会直接插入。参见:插入文本(Inserting Text)。
图形显示(Graphical Display)- 可显示图像、多字体的显示设备,通常带窗口系统。
高亮(Highlighting)- 高亮文本是指用不同的前景色和 / 或背景色来显示文本,使其在缓冲区中与其他文本区分开来。
Emacs 在多种场景下使用高亮:每当区域处于激活状态时,Emacs 会对其进行高亮(参见标记与区域)。增量搜索也会高亮匹配的内容(参见增量搜索)。参见术语表 ——语法高亮模式(Font Lock)。
硬拷贝(Hardcopy)- 指打印输出。Emacs 提供多种打印缓冲区内容的命令。参见:打印硬拷贝(Printing Hard Copies)。
HELP- Emacs 中 C-h 或 F1 为帮助键,随时查看选项与命令说明。参见:帮助(Help)。
帮助回显(Help Echo)- 鼠标指向需说明的界面元素时,在回显区显示的简短提示。图形显示下可作为工具提示(参见相关词条)弹出。参见:工具提示(Tooltips)。
主目录(Home Directory)- 用户个人文件所在目录,类 Unix 系统每个用户有独立主目录。简写为 ~,~user 表示其他用户主目录。
钩子(Hook)- 钩子是特定时机(如保存缓冲区、激活主模式)调用的函数列表。可通过钩子自定义行为而不修改代码。参见:钩子(Hooks)。
Hyper 键- Hyper 是一种修饰位,按住 Hyper 键输入的字符以 Hyper- 开头(简写 H-)。参见:修饰键(Modifier Keys)。
i.e.- 拉丁语 id est 缩写,意为 “即”“也就是”。
Iff- 数学用语,“当且仅当”(if and only if)。文档中尽量避免使用。
收件箱(Inbox)- 系统投递邮件的文件,邮件工具从中收取并转入邮件文件。参见:Rmail 文件与收件箱(Rmail Files and Inboxes)。
增量搜索(Incremental Search)- 输入第一个字符就开始搜索,继续输入则不断精化结果。参见:增量搜索(Incremental Search)。
缩进(Indentation)- 行首的空白。多数编程语言用缩进体现结构,Emacs 提供专门缩进命令。参见:缩进(Indentation)。
间接缓冲区(Indirect Buffer)- 与另一缓冲区(基缓冲区)共享文本的缓冲区。参见:间接缓冲区(Indirect Buffers)。
Info- GNU 项目使用的超文本文档格式。
输入事件(Input Event)- Emacs 内部表示用户一次操作的对象:字符、功能键、鼠标按键、帧切换等。参见:用户输入类型(Kinds of User Input)。
输入法(Input Method)- 用 ASCII 字符序列输入非 ASCII 字符的系统。参见:输入法(Input Methods)。
插入(Insertion)- 将文本从键盘或其他位置加入缓冲区。
交互式函数(Interactive Function)- 命令(command)的另一名称。
交互式调用- 函数通过 M-x、按键、菜单等用户层面执行,称为交互式调用。
互锁(Interlocking)- 参见:术语表 —— 文件锁定(File Locking)。
Isearch- 参见:术语表 —— 增量搜索(Incremental Search)。
对齐(Justification)- 在行内增加空格,调整文本边缘位置。参见:显式填充命令(Explicit Fill Commands)。
按键绑定(Key Binding)- 参见:术语表 —— 绑定(Binding)。
键盘宏(Keyboard Macro)- 将现有命令序列录制成新命令,无需编写 Lisp 代码。可录制后反复回放。参见:键盘宏(Keyboard Macros)。
键盘快捷键(Keyboard Shortcut)- 调用命令的按键序列。其他程序叫 “设置快捷键”,Emacs 叫 “绑定按键序列”。参见:术语表 —— 绑定(Binding)。
按键序列(Key Sequence)- 一组有意义的输入事件(参见相关词条),简称 “键”。能唯一确定动作的是完整按键;仅作为前缀的是前缀键。参见:按键(Keys)。
按键映射(Keymap)- 记录按键序列到命令的绑定关系的数据结构。例如全局映射将
C-n绑定到next-line。参见:按键映射(Keymaps)。 键盘转换表(Keyboard Translation Table)- 将终端字符码转换为 Emacs 按键序列字符码的数组。
删除环(Kill Ring)- 保存最近删除(参见Glossary—Killing))文本的地方,可通过粘贴(参见相关词条)恢复。参见:粘贴(Yanking)。
Killing(剪切式删除)- 擦除文本并保存到删除环,以便后续粘贴。其他系统称为 “剪切”。多数删除文本命令是 Killing,而非普通删除(Deletion)。参见:删除与移动文本(Killing and Moving Text)。
终止任务(Killing a Job)- 直接结束进程(如 Emacs),未保存数据会丢失。参见:退出 Emacs(Exiting Emacs)。
语言环境(Language Environment)- 指定输入法与编码系统的默认设置,用于编辑非 ASCII 文本。参见:语言环境(Language Environments)、国际字符集支持。
自动换行(Line Wrapping)- 参见:术语表 —— 填充(Filling)。
Lisp- 一种编程语言,Emacs 主要由 Emacs Lisp 编写,专为文本编辑扩展。
列表(List)- 以左括号开头、配对右括号结尾的文本。C 等语言中大括号包裹的结构也视为列表。Emacs 提供专门操作列表的命令。参见:括号结构移动(Moving in the Parenthesis Structure)。
局部(Local)- 只在特定上下文生效:特定函数、缓冲区、主模式。与全局(global)相对。
局部缩写(Local Abbrev)- 只在特定主模式生效,覆盖同名全局缩写。参见:缩写(Abbrevs)。
局部按键映射(Local Keymap)- 特定主模式使用的按键映射,覆盖全局同名绑定。参见:按键映射(Keymaps)。
局部变量(Local Variable)- 只在一个缓冲区生效的变量值。参见:局部变量(Local Variables)。
M-- 字符名中 M- 是 Meta 的缩写。参见:M-。
M-C-- 与
C-M-相同,Control-Meta 缩写。 M-x- 按命令名调用 Emacs 命令的按键,用于执行无按键绑定的命令。参见:按名运行命令(Running Commands by Name)。
邮件(Mail)- 邮件是指通过计算机系统在用户之间发送的消息,供接收方在方便时阅读。Emacs 提供用于撰写和发送邮件,以及阅读和编辑已收到邮件的命令。参见 “发送邮件(Sending Mail)”。有关在 Emacs 中阅读邮件的一种方式,参见 “使用 Rmail 阅读邮件(Reading Mail with Rmail)”。
邮件编写方式(Mail Composition Method)- Emacs 中可选用的邮件编辑发送程序。参见:邮件编写方式(Mail-Composition Methods)。
主模式(Major Mode)- 主模式互斥,每种主模式为编辑某类文本配置 Emacs。理想情况下每种编程语言有独立主模式。参见:主模式(Major Modes)。
边距(Margin)- 窗口可用区域(含边缘区)与窗口边缘之间的空白。
标记(Mark)- 标记指向文本中的一个位置,与光标点共同确定区域(参见相关词条)。许多命令作用于光标点到标记之间的文本。每个缓冲区有独立标记。参见:标记与区域(The Mark and the Region)。
标记环(Mark Ring)- 保存多个最近标记位置,方便回退。每个缓冲区有独立标记环,另有全局标记环。参见:标记环(The Mark Ring)。
菜单栏(Menu Bar)- 帧顶部的一行,可鼠标点击或键盘操作打开菜单。参见:菜单栏(Menu Bars)。
消息(Message)- 参见:术语表 —— 邮件(Mail)。
Meta 键- Meta 是一种修饰位,按住 Meta 键输入的字符以 Meta- 开头(简写 M-)。部分终端标为 Alt 或 Edit。参见:Meta。
Meta 字符(Meta Character)- 带有 Meta 位的字符。
迷你缓冲区(Minibuffer)- 回显区内临时出现的窗口,用于读取命令参数。参见:迷你缓冲区(The Minibuffer)。
迷你缓冲区历史(Minibuffer History)- 记录之前输入的参数,方便重复使用。参见:迷你缓冲区历史(Minibuffer History)。
次要模式(Minor Mode)- 可选功能,可独立开关。部分全局、部分局部。参见:次要模式(Minor Modes)。
次要模式按键映射(Minor Mode Keymap)- 次要模式启用时生效的按键映射,优先级高于缓冲区局部映射,局部映射高于全局映射。参见:按键映射(Keymaps)。
模式行(Mode Line)- 每个窗口底部的一行,显示缓冲区状态信息。参见:模式行(The Mode Line)。
已修改缓冲区(Modified Buffer)- 文本自上次保存(或创建)后被修改过的缓冲区。参见:保存文件(Saving Files)。
移动文本(Moving Text)- 从一处删除并插入到另一处,通常用剪切(Killing)+ 粘贴(Yanking)实现。参见:删除与移动文本(Killing and Moving Text)。
MULE- Emacs 23 之前提供多语言支持的包,现已集成进 Emacs 并被 Unicode 支持部分取代。部分字符集相关代码仍使用 MULE 名称。参见:国际字符集支持。
xa
多字节字符(Multibyte Character)- 占用多个字节的字符,用于表示非 ASCII 字符。参见:国际字符(International Characters)。
命名标记(Named Mark)- 用作记录位置的寄存器(参见相关词条),可跳转到该位置。参见:寄存器(Registers)。
窄化(Narrowing)- 限制当前缓冲区只编辑部分文本,其余内容不可见不可编辑,但仍存在,保存时会一起保存。解除限制称为宽化(widening)。参见:窄化(Narrowing)。
换行符(Newline)- 缓冲区中 C-j 字符用于结束文本行,也叫 newline。参见:术语表 —— 行尾。
nil- 逻辑 “假”,对立面为 t(真)。
数字参数(Numeric Argument)- 命令前指定的数字,用于改变命令效果,常作重复次数。参见:数字参数(Numeric Arguments)。
覆盖模式(Overwrite Mode)- 一种次要模式,输入字符直接覆盖光标后文本,而非后移文本。参见:次要模式(Minor Modes)。
包(Package)- 可从 Emacs 内部下载自动安装的 Lisp 代码集合,用于扩展功能。参见:Emacs Lisp 包(Emacs Lisp Packages)。
页(Page)- 以换页符(ASCII C-l)开头分隔的文本单元。Emacs 提供按页移动与操作命令。参见:页(Pages)。
段落(Paragraph)- 自然语言文本的中等单元,Emacs 提供按段落移动与操作命令。参见:段落(Paragraphs)。
解析(Parsing)- Emacs 某些命令用于解析单词或表达式,实际只是找到其另一端位置。
光标点(Point)- 缓冲区中插入与删除发生的位置,位于两个字符之间。终端光标指示光标点位置。参见:光标点(Point)。
前缀参数(Prefix Argument)- 参见:术语表 —— 数字参数(Numeric Argument)。
前缀键(Prefix Key)- 仅用于引导更长按键序列的按键,如
C-x。以C-x开头的双字符序列均为合法按键。参见:按键(Keys)。 主选择区(Primary Selection)- X 选择区的一种,多数 X 应用用于文本传输。Emacs 标记 / 选中文本时会设置主选择区。参见:Shift 选择(Shift Selection)。
提示(Prompt)- 提示是用来请求你进行输入的文本。显示提示的过程称为提示输入(prompting)。Emacs 的提示总是出现在回显区(参见该词条)。一种提示场景是当小缓冲区用于读取参数时(参见《迷你缓冲区》);当你在输入多字符按键序列的过程中暂停时所出现的回显,也属于一种提示(参见《回显区》)。
q.v.- 拉丁语 quod vide 缩写,意为 “参见”。
查询替换(Query-Replace)- 交互式字符串替换功能,每处替换可确认。参见:查询替换(Query Replace)。
退出(Quitting)- 用
C-g(或 DOS 下C-Break)取消部分输入的命令或正在运行的命令。参见:退出与中止(Quitting and Aborting)。 引用(Quoting)- 取消字符通常的特殊含义,Emacs 中最常用
C-q。例如引用特殊字符使其像普通字符一样插入。参见:引用(Quoting)。 文件名引用(Quoting File Names)- 关闭 $、~、: 等结构的特殊含义。参见:引用文件名(Quoted File Names)。
只读缓冲区(Read-Only Buffer)- 不允许修改文本的缓冲区,如 Dired 缓冲区、打开写保护文件时。参见:使用多缓冲区(Using Multiple Buffers)。
矩形块(Rectangle)- 指定行范围、列范围内的文本。通常将光标点与标记设为对角顶点确定矩形。参见:矩形块(Rectangles)。
递归编辑层级(Recursive Editing Level)- 执行命令过程中进入的编辑状态。模式行用方括号
[]标识递归编辑。参见:递归编辑层级(Recursive Editing Levels)。 重绘(Redisplay)- 根据文本修改更新屏幕显示内容的过程。参见:重绘(Redisplay)。
Regexp- 参见:术语表 —— 正则表达式(Regular Expression)。
区域(Region)- 光标点与标记之间的文本,许多命令作用于区域文本。参见:区域(Region)。
寄存器(Register)- 可保存文本、位置、矩形块的命名槽位,供后续使用。相关功能:书签。参见:寄存器(Registers)。
正则表达式(Regular Expression)- 可匹配多种文本的模式,如 a[0-9]+ 匹配 a 后接一个或多个数字。参见:正则表达式语法(Syntax of Regular Expressions)。
远程文件(Remote File)- 存储在其他计算机上的文件。Emacs 可通过网络访问支持协议的远程文件。参见:远程文件(Remote Files)。
重复次数(Repeat Count)- 参见:术语表 —— 数字参数(Numeric Argument)。
替换(Replacement)- 参见:术语表 —— 全局替换(Global Substitution)。
限制(Restriction)- 缓冲区开头或结尾暂时不可访问的文本范围。设置限制称为窄化,解除称为宽化。参见:窄化(Narrowing)。
RET- 在 Emacs 中插入换行符,并用于结束小缓冲区参数输入。参见:回车(Return)。
恢复(Reverting)- 回到原始状态,如重新从磁盘读取文件恢复缓冲区。参见:恢复缓冲区(Reverting a Buffer)。
保存(Saving)- 将缓冲区文本写入关联文件,使编辑真正生效。参见:保存文件(Saving Files)。
滚动条(Scroll Bar)- 窗口侧边的长条,可用鼠标操作滚动。仅窗口系统支持。参见:滚动条(Scroll Bars)。
滚动(Scrolling)- 移动窗口中文本,查看缓冲区不同部分。参见:滚动(Scrolling)。
搜索(Searching)- 将光标点移动到指定字符串或正则表达式下一处。参见:搜索与替换(Searching and Replacement)。
搜索路径(Search Path)- 用于按用途查找文件的目录列表,如 load-path 用于查找 Lisp 库。参见:Emacs Lisp 代码库。
次选择区(Secondary Selection)- X 选择区的一种,部分应用用于文本传输。Emacs 提供专用鼠标命令。参见:次选择区(Secondary Selection)。
选中框架(Selected Frame)- 当前输入作用的框架。参见:框架与图形显示。
选中窗口(Selected Window)- 当前输入作用的窗口。参见:Emacs 窗口概念(Concepts of Emacs Windows)。
选中缓冲区(Selecting a Buffer)- 将某缓冲区设为当前缓冲区。参见:创建与选中缓冲区(Creating and Selecting Buffers)。
选择区(Selection)- 窗口系统中应用间文本传输的主要方式。Emacs 支持主选择区、次选择区、剪贴板。
自文档化(Self-Documentation)- Emacs 可随时告诉你任意命令的作用或相关命令列表,用 C-h 触发。参见:帮助(Help)。
自动插入字符(Self-Inserting Character)- 输入该字符就直接插入缓冲区的字符。普通可打印字符与空白字符均为自动插入字符(特殊主模式除外)。
句子(Sentences)- Emacs 提供按句子移动、删除的命令。参见:句子(Sentences)。
服务器(Server)- Emacs 中可启动服务器进程监听客户端连接,比启动多个 Emacs 更快。参见:将 Emacs 用作服务器、术语表 —— 守护进程。
Sexp- Sexp(s-expression)是 Lisp 基本语法单元:列表或原子。也是 Lisp 的平衡表达式,因此编辑平衡表达式的命令名中含 sexp。参见:Sexp(Sexps)。
并发编辑(Simultaneous Editing)- 两人同时修改同一文件,未检测会导致数据丢失。Emacs 会检测并警告用户。参见:并发编辑(Simultaneous Editing)。
SPC- 空格字符,按空格键输入。
快捷栏(Speedbar)- 快捷栏是一个特殊的狭长窗口,用于快速访问 Emacs 缓冲区、缓冲区中的函数、Info 节点以及 Emacs 内部其他有用的文本内容。参见 “快捷栏窗口(Speedbar Frames)”。
拼写检查(Spell Checking)- 检查文本单词拼写是否正确。Emacs 可调用外部拼写检查工具。参见:检查与更正拼写(Checking and Correcting Spelling)。
字符串(String)- Lisp 数据对象,包含字符序列。用双引号 " 包裹,内部 " 写为
\",\写为\\。 字符串替换(String Substitution)- 参见:术语表 —— 全局替换(Global Substitution)。
符号(Symbol)- Emacs Lisp 中具名对象,可作为变量、函数、命令、外观。名称为其打印表示。参见:Emacs Lisp 参考手册・符号类型(Symbol Type)。
语法高亮(Syntax Highlighting)- 参见:术语表 —— 语法高亮模式(Font Lock)。
语法表(Syntax Table)- 告诉 Emacs 哪些字符构成单词、哪些像括号一样配对等。参见:Emacs Lisp 参考手册・语法表(Syntax Tables)。
Super 键- Super 是一种修饰位,按住 SUPER 键输入的字符以 Super- 开头(简写 s-)。参见:修饰键(Modifier Keys)。
挂起(Suspending)- 临时暂停 Emacs 并返回 Shell,可恢复而不丢失缓冲区、未保存修改、撤销历史等。参见:退出 Emacs(Exiting Emacs)。
TAB- 制表符,常用于缩进或补全。
标签栏(Tab Bar)- 帧顶部一行标签,点击切换持久窗口配置。参见:标签栏(Tab Bars)。
标签行(Tab Line)- 窗口顶部一行标签,点击切换窗口缓冲区。参见:窗口标签行(Window Tab Line)。
标记(Tag)- 程序源代码中的标识符。参见:查找标识符引用(Find Identifier References)。
标记表(Tags Table)- 索引文件,记录多个文件中函数、宏、数据结构等定义。参见:标记表(Tags Tables)。
终端脚本文件(Termscript File)- 记录 Emacs 发送到终端的所有字符,用于调试显示问题。需手动开启。参见:报告错误(Reporting Bugs)。
文本(Text)- “文本(Text)” 有两种含义(参见 “面向人类语言的命令”):
- 由字符序列构成的数据,区别于二进制数据、可执行程序等。Emacs 缓冲区的基本内容(文本属性除外,参见该词条)在这个意义上始终是文本。
- 由人类书面语言构成的数据(区别于程序),或遵循人类语言的书写风格规范。
文本终端(Text Terminal)- 只能以字符为单位显示的终端,无法控制像素。Emacs 在文本终端上支持部分显示功能。也叫字符终端、TTY。
文本属性(Text Properties)- 缓冲区中特定字符的注释信息,如图像、格式信息。参见:编辑格式信息(Editing Format Information)。
主题(Theme)- 一组自定义设置,统一 Emacs 外观与行为,如一套外观组合。
工具栏(Tool Bar)- 帧顶部带图标的按钮行,点击执行命令,是图形化菜单栏。也有窗口级工具栏。参见:工具栏(Tool Bars)、窗口工具栏(Window Tool Bar)。
工具提示(Tooltips)- 显示帮助回显文本的小窗口,解释界面元素、鼠标操作选项等。参见:工具提示(Tooltips)。
顶层(Top Level)- Emacs 的正常编辑状态,不在递归编辑、小缓冲区、命令执行中。可通过中止、退出返回顶层。参见:退出与中止(Quitting and Aborting)。
临时标记模式(Transient Mark Mode)- Emacs 默认行为:设置标记即激活并高亮区域。默认启用。参见:禁用临时标记模式(Disabling Transient Mark Mode)。
交换(Transposition)- 交换两个相邻文本单元的位置:字符、单词、平衡表达式、行等。参见:交换文本(Transposing Text)。
回收站(Trash Can)- 参见:术语表 —— 删除文件(Deletion of Files)。
截断(Truncation)- 显示文本行时,超出窗口右边缘的部分直接不显示。参见:截断(Truncation)、术语表 —— 续行。
TTY- 参见:术语表 —— 文本终端(Text Terminal)。
撤销(Undoing)- 回退之前的编辑操作,恢复更早文本。参见:撤销(Undo)。
Unix- 一类历史悠久的多用户操作系统。GNU 项目目标是开发自由的类 Unix 系统。
用户选项(User Option)- 可由用户设置以自定义 Emacs 的外观或变量。参见:简易自定义界面(Easy Customization Interface)。
变量(Variable)- Lisp 中可存储任意值的对象。部分用于内部,部分为用户选项。参见:变量(Variables)。
版本控制(Version Control)- 记录源码文件多个版本,比备份文件更强大。参见:版本控制(Version Control)。
关联(Visiting)- 将文件内容加载到缓冲区以便编辑。参见:关联文件(Visiting Files)。
空白字符(Whitespace)- 连续的格式字符:空格、制表符、换行、退格等。
宽化(Widening)- 解除当前缓冲区的限制,与窄化相对。参见:窄化(Narrowing)。
窗口(Window)- Emacs 会将一个框架(参见该词条)划分成一个或多个窗口,每个窗口可以在任意时刻显示一个缓冲区(参见屏幕布局)的内容。关于 Emacs 如何使用屏幕的基本信息,请参阅屏幕的组织(The Organization of the Screen)。关于控制窗口使用的命令,请参阅多窗口(Multiple Windows)。在其他一些编辑器中被称为 “window(窗口)” 的概念,在 Emacs 里我们称之为 “frame(框架)”。
窗口系统(Window System)- 在图形显示上运行,将屏幕分为多个应用窗口的软件。现代系统均自带。
单词缩写(Word Abbrev)- 参见:术语表 —— 缩写(Abbrev)。
单词搜索(Word Search)- 搜索单词序列,忽略之间标点。参见:单词搜索(Word Search)。
粘贴(Yanking)- 重新插入之前剪切(Killing)的文本,可用于撤销误删、复制、移动文本。其他系统称为 “粘贴”。参见:粘贴(Yanking)。
致谢
按键(字符)索引
命令行选项索引
命令与函数索引
变量索引
概念索引
脚注
Footnotes:
本手册本身受《GNU 自由文档许可证》保护。该许可证的理念与《通用公共许可证》相近,但更适用于文档类作品。参见《GNU 自由文档许可证》章节。
“point(光标位置)” 这一术语源于字符 . ,该字符曾是 TECO 语言(初代 Emacs 的开发语言)中用于定位编辑位置的命令。
出于历史原因,我们将 Alt 键称为 Meta 键。
在 site-start.el 文件中设置 inhibit-startup-screen 是无效的,因为启动界面的初始化流程,早于 site-start.el 文件的读取时间。关于 site-start.el 的更多信息,参见《Emacs 初始化文件》章节。
ASCII 编码中并无 C-SPC 字符;在文本终端中,按下 C-SPC 通常会输入 C-@ 字符。该按键同样绑定了 set-mark-command 命令,因此除非你的文本终端行为特殊,否则可直接将 C-@ 视作 C-SPC 使用。
除 C-/ 外,撤销命令还绑定至 C-x u ,因该快捷键对初学者而言更易记忆:字母 u 对应英文 “undo(撤销)”;同时也绑定至 C-_ ,原因是在部分文本终端中,按下 C-/ 实际输入的是 C-_ 。
若你的文件系统不支持符号链接,Emacs 会改用普通文件实现文件锁定。
在 Nextstep 系统中,由工具包创建的工具提示,其前景色和背景色也可通过设置 tooltip-frame-parameters 中包含的前景、背景框架参数进行自定义。
该行尾规范也被多用途互联网邮件扩展(MIME)的 'text/*' 类型正文及其他网络传输场景采用,与标准通用标记语言(SGML)参考语法中的记录开始 / 记录结束格式不同,Emacs 不直接支持后者。
若你在 X 窗口系统中运行 Emacs,可能需要通过以下命令告知 X 服务器新安装字体的存放路径:
xset fp+ /usr/local/share/emacs/fonts xset fp rehash
单弯引号对应的字符编码为:左单弯引号 U+2018、右单弯引号 U+2019;双弯引号对应的字符编码为:左双弯引号 U+201C、右双弯引号 U+201D。在无法显示这类字符的文本终端中,信息阅读器可能会将其显示为打字机风格的 ASCII 直引号。
该格式已被 LaTeX 自带的slides文档类替代。
术语「S表达式(sexp)」原本用于指代 Lisp 语言中的表达式。
「ElDoc」这一名称源于历史原因:该模式最初仅为 Emacs Lisp 缓冲区提供支持。
在图形化显示界面中, M-TAB 组合键通常被窗口管理器占用,用于切换图形化窗口,因此你应改用 C-M-i 或 ESC TAB 来执行该操作。
即正则表达式与语法表。
标记是标识符引用的同义词。基于 etags 软件包的命令和功能历来沿用该含义的 “标记(tag)” 一词,本节也遵循这一传统。
美国国家安全局(The US National Security Agency)
该功能需要 Emacs 编译时带 libxml2 支持,或已安装 Lynx 浏览器。
对应文档类型所需的外部工具必须可用,且 Emacs 必须在图形界面框架中运行并支持 PNG 图片。若不满足这些条件,Emacs 会回退到其他主模式。
你不应该挂起 Shell 进程本身。挂起 Shell 的子任务是另一回事 —— 那是常规操作,但必须用 Shell 自身命令继续子任务,本命令做不到。
部分程序使用其他环境变量;例如,要让 TeX 使用 'emacsclient' ,可将环境变量 TEXEDIT 设为: 'emacsclient +%d %s'
关于版权转让的更多信息,参见《为何 FSF 要求贡献者转让版权》。
在 MS-DOS 下,受文件系统限制,该文件名应为 _dir-locals.el 。如果文件系统只支持 8+3 文件名,操作系统会将其截断为 _dir-loc.el 。
在 MS-Windows 系统中,没有一个被所有程序统一视为用户 “家目录” 的目录。Emacs 会使用相关目录之一作为家目录的等效位置;详见《MS-Windows 上的 HOME 与启动目录》。
该选项在 MS-Windows 上无效。
本节及下文凡是提到 “由冒号分隔的目录列表”,都适用于 Unix/GNU/Linux 系统。在 MS-DOS 和 Windows 上,目录用分号分隔,因为 DOS/Windows 文件名可能在盘符后带冒号
该功能在 MS-Windows 与 MS-DOS 文本模式终端上不生效。
除非指定了 “共享用户 ID”,并且安装了使用相同 “包签名密钥” 签名的其他应用,此时 Emacs 会以相同用户身份运行,并可访问上述应用的相同文件。
旧版 Emacs 不会检查应用数据目录。
已知一个例外:锁定工作站的 Win-L 组合键由系统底层处理, w32-register-hot-key 无法覆盖,按下仍会锁定计算机。
注意: net use 命令要求 UNC 路径使用 Windows 风格的反斜杠 \ ,而 printer-name 变量的值可以用正斜杠或反斜杠。
通常有一个代码页固化在显示内存中,其他代码页需要修改 CONFIG.SYS 等系统配置并重启才能安装。虽然有第三方工具允许不重启改代码页,但这里描述的是标准 MS‑DOS 的行为。
Emacs 标准的 ISO 8859 编码并不完全适用于 DOS,因为 DOS 代码页通常与标准 ISO 字符编码不一致。例如:字符 ‘ç’(带变音符号的 ‘c’),标准 Latin-1:编码 231,DOS 代码页 850:编码 135
此处表述不够严谨。本意是没有人需要为使用 GNU 系统的许可付费。但文字并未明确这一点,人们常理解为 GNU 副本应始终以极低价格或免费分发。这从来都不是初衷;宣言后文也提到了公司可以提供收费分发服务。后来我学会了严格区分 “自由”(指自由)与 “免费”(指价格)。自由软件是用户拥有再分发与修改自由的软件。有些用户可以免费获得副本,有些则付费购买 —— 如果资金有助于支持软件改进,那就更好了。重要的是:每个拥有副本的人,都有与他人合作使用它的自由。
这是另一处我未能严格区分 “自由” 两种含义的地方。这句话本身并非错误 —— 你可以从朋友或网上免费获得 GNU 软件副本。但它确实容易引发误解。
如今已有多家此类公司。
自由软件基金会的大部分资金来自分发服务,尽管它是慈善机构而非公司。如果没人选择从基金会订购副本,它就无法开展工作。但这并不意味着可以用专有限制强迫每个用户付费。只要一小部分用户从基金会订购副本,就足以维持其运转。因此我们恳请用户以这种方式支持我们。你尽到自己的一份力了吗?
最近有一批计算机公司联合出资支持 GNU C 编译器的维护。
在上世纪 80 年代,我还没意识到谈论 “知识产权”“问题” 会带来多大混淆。这个词显然带有偏向性;更隐蔽的是,它把性质迥异、引发不同问题的各类法律混为一谈。如今我强烈建议人们完全拒绝使用 “知识产权” 一词,以免让人误以为这些法律属于同一个统一问题。清晰的做法是分别讨论专利、版权与商标。有关该词如何传播混淆与偏见的更多说明,请见:https://www.gnu.org/philosophy/not-ipr.xhtml。
后来我们发现需要区分 “自由软件(free software)” 与 “免费软件(freeware)”。“freeware” 指可以自由再分发的软件,但通常用户无权研究与修改源码,因此其中大多数并非自由软件。有关应避免使用的词汇的更多说明,请见:https://www.gnu.org/philosophy/words-to-avoid.html。