Emacs 完全指南 / 第 04 章:移动与导航
第 04 章:移动与导航
4.1 高级移动命令
在第 3 章中我们介绍了基本的光标移动。本章将深入更强大的导航方式。
单词与符号移动
| 快捷键 | 命令 | 说明 |
|---|
M-f | forward-word | 前进一个单词 |
M-b | backward-word | 后退一个单词 |
C-M-f | forward-sexp | 前进一个 S-表达式 |
C-M-b | backward-sexp | 后退一个 S-表达式 |
M-@ | mark-word | 选中下一个单词 |
C-M-@ | mark-sexp | 选中下一个 S-表达式 |
提示: S-表达式(S-expression)在不同模式下有不同含义:
- 在 Elisp 中:匹配括号对
- 在 C/Python 中:匹配大括号或圆括号
- 在 HTML 中:匹配标签对
段落与页面移动
| 快捷键 | 命令 | 说明 |
|---|
M-{ | backward-paragraph | 上一个段落 |
M-} | forward-paragraph | 下一个段落 |
M-g g | goto-line | 跳转到指定行 |
M-g c | goto-char | 跳转到指定字符位置 |
函数级移动(编程模式)
| 快捷键 | 命令 | 说明 |
|---|
C-M-a | beginning-of-defun | 函数开头 |
C-M-e | end-of-defun | 函数末尾 |
C-M-h | mark-defun | 选中整个函数 |
;; 示例:在 Python 文件中
;; C-M-a 跳转到当前 def/class 的开头
;; C-M-e 跳转到当前 def/class 的末尾
;; C-M-h 选中整个函数
(defun demo-function ()
"这是一个示例函数。"
(message "Hello")
(message "World"))
;; 光标在此处时 C-M-a 跳转到 (defun
;; 光标在此处时 C-M-e 跳转到最后的 )
空白字符感知移动
;; 使用 subword-mode 处理驼峰命名
(global-subword-mode 1)
;; M-f 现在会在 camelCase 的每个单词边界处停止
;; 例如:myVariableName → my|Variable|Name
;; 使用 hungry-delete 模式删除所有连续空白
(use-package hungry-delete
:config
(global-hungry-delete-mode 1))
4.2 搜索
Emacs 提供了多种搜索方式,从简单的增量搜索到强大的正则搜索。
增量搜索(Isearch)
| 快捷键 | 命令 | 说明 |
|---|
C-s | isearch-forward | 向前增量搜索 |
C-r | isearch-backward | 向后增量搜索 |
C-s C-s | — | 搜索上一次的关键词 |
C-s M-p | — | 搜索历史上一条 |
C-s M-n | — | 搜索历史下一条 |
Isearch 中的快捷键
| 按键 | 说明 |
|---|
RET | 结束搜索,停留在当前位置 |
C-g | 取消搜索,回到起始位置 |
C-s | 继续搜索下一个 |
C-r | 继续搜索上一个 |
M-c | 切换大小写敏感 |
M-r | 切换正则模式 |
C-w | 将光标处单词加入搜索 |
M-s C-e | 将光标到行尾加入搜索 |
M-s o | 搜索结果出现在 occur 缓冲区 |
搜索流程示例:
1. C-s → 进入增量搜索
2. 输入 "def" → 实时高亮匹配
3. C-s → 跳转到下一个匹配
4. C-s → 继续跳转
5. RET → 停留在当前位置
正则搜索流程:
1. C-M-s → 进入正则增量搜索
2. 输入 "def \\w+" → 匹配 def 后跟任意单词
3. C-s → 跳转到下一个匹配
正则搜索
| 快捷键 | 命令 | 说明 |
|---|
C-M-s | isearch-forward-regexp | 正则向前搜索 |
C-M-r | isearch-backward-regexp | 正则向后搜索 |
Emacs 正则表达式速查
| 表达式 | 说明 | 示例 |
|---|
. | 任意字符 | a.c 匹配 abc, a1c |
* | 零次或多次 | ab*c 匹配 ac, abc, abbc |
+ | 一次或多次 | ab+c 匹配 abc, abbc |
? | 零次或一次 | ab?c 匹配 ac, abc |
[...] | 字符集 | [aeiou] 匹配元音 |
[^...] | 排除字符集 | [^0-9] 匹配非数字 |
\b | 单词边界 | \bdef\b 匹配完整的 def |
\w | 单词字符 | \w+ 匹配一个或多个单词字符 |
\( ... \) | 分组 | \(ab\)+ 匹配 ab, abab |
\1 | 反向引用 | \(.\)\1 匹配重复字符如 aa |
| | 或 | cat|dog 匹配 cat 或 dog |
^ | 行首 | ^def 匹配行首的 def |
$ | 行尾 | end$ 匹配行尾的 end |
注意: Emacs 的正则语法与 PCRE(Perl 风格)有区别:
- 分组用
\( \) 而非 ( ) +, ?, | 需要转义为 \+, \?, \|\( 和 \) 用于分组,( 和 ) 匹配字面括号
Occur 模式
;; M-s o 或 M-x occur
;; 在当前缓冲区中搜索匹配行,结果出现在 *Occur* 缓冲区
;; 使用示例:
M-s o TODO RET
;; → *Occur* 缓冲区列出所有包含 TODO 的行
;; 在 *Occur* 中按 RET 跳转到对应位置
;; 按 e 进入编辑模式,可直接编辑匹配的行
4.3 全局搜索与替换
替换命令
| 快捷键 | 命令 | 说明 |
|---|
M-% | query-replace | 交互式替换 |
C-M-% | query-replace-regexp | 正则交互式替换 |
M-x replace-string | — | 全局替换(不询问) |
M-x replace-regexp | — | 全局正则替换 |
交互式替换中的操作
| 按键 | 说明 |
|---|
y 或 SPC | 替换当前匹配并跳到下一个 |
n 或 DEL | 跳过当前匹配 |
! | 替换所有剩余匹配 |
^ | 回到上一个匹配 |
e | 编辑替换字符串 |
q | 结束替换 |
C-r | 进入递归编辑(修改后再继续) |
C-w | 删除匹配并进入递归编辑 |
多文件搜索与替换
;; 方法 1:使用 grep
M-x grep ; 在文件中搜索
M-x rgrep ; 递归目录搜索(推荐)
;; rgrep 使用示例:
M-x rgrep RET
Search for: function_name
Files: *.py
Directory: ~/project/
;; 方法 2:使用 consult(推荐的现代方案)
(use-package consult
:bind (;; 项目内搜索
("C-c s" . consult-grep)
("C-c r" . consult-ripgrep)))
;; 方法 3:使用 wgrep(在 grep 结果中编辑)
(use-package wgrep
:config
(setq wgrep-auto-save-buffer t))
;; wgrep 工作流:
;; 1. M-x rgrep 搜索结果
;; 2. C-c C-p 在 grep 结果中进入编辑模式
;; 3. 直接编辑匹配的文本
;; 4. C-c C-e 应用更改到原始文件
;; 5. C-x C-s 保存
替换中的正则捕获
场景:将 "function_name(args)" 替换为 "args => function_name"
搜索正则:\(\w+\)(\(.+\))
替换为: \2 => \1
示例:
原文:calculate(total)
结果:total => calculate
4.4 书签(Bookmarks)
书签让你快速跳转到文件中的特定位置,甚至跨越不同文件。
基本书签操作
| 快捷键 | 命令 | 说明 |
|---|
C-x r m | bookmark-set | 设置书签 |
C-x r b | bookmark-jump | 跳转到书签 |
C-x r l | bookmark-bmenu-list | 列出所有书签 |
C-x r M | bookmark-set-no-overwrite | 设置书签(不覆盖) |
书签操作流程
1. 导航到目标位置
2. C-x r m → 输入书签名称 → RET
3. 在任何时候:
C-x r b → 输入书签名称 → RET → 跳转到该位置
管理书签:
C-x r l → 打开书签列表
d → 标记删除
x → 执行删除
r → 重命名
RET → 跳转到选中的书签
;; 书签自动保存
(setq bookmark-save-flag 1) ; 每次设置书签后自动保存
;; 书签文件位置
(setq bookmark-default-file "~/.emacs.d/bookmarks")
;; 使用 bm 包获得可视化的书签
(use-package bm
:bind (("<f2>" . bm-toggle)
("<f3>" . bm-next)
("<S-f3>" . bm-previous)
("C-c b l" . bm-show-all))
:config
(setq bm-highlight-style 'bm-highlight-line-and-fringe))
Imenu 可以快速跳转到当前文件中的函数、类、变量等定义。
| 快捷键 | 命令 | 说明 |
|---|
M-g i | imenu | 打开 Imenu 菜单 |
M-x idomenu | — | Ido 增强版 Imenu |
;; 使用 consult-imenu(推荐)
(use-package consult
:bind ("M-g i" . consult-imenu)
:config
(setq consult-imenu-config
'((emacs-lisp-mode :toplevel "Functions"
:types ((?f "Functions" font-lock-function-name-face)
(?v "Variables" font-lock-variable-name-face)
(?m "Macros" font-lock-keyword-face)))
(python-mode :toplevel "Functions"
:types ((?f "Functions" font-lock-function-name-face)
(?c "Classes" font-lock-type-face))))))
;; 使用 imenu-list(侧边栏显示)
(use-package imenu-list
:bind ("C-' " . imenu-list-smart-toggle)
:config
(setq imenu-list-focus-after-activation t
imenu-list-auto-resize t))
4.6 Projectile(项目管理)
Projectile 是 Emacs 中最流行的项目管理包,它提供了项目级别的导航和操作。
安装与配置
(use-package projectile
:diminish projectile-mode
:config
(projectile-mode +1)
;; 设置项目搜索路径
(setq projectile-project-search-path '("~/projects/" "~/work/"))
;; 使用本机索引(更快)
(setq projectile-indexing-method 'alien)
;; 排除目录
(setq projectile-globally-ignored-directories
'(".git" "node_modules" ".venv" "__pycache__" "target"))
:bind-keymap
("C-c p" . projectile-command-map))
核心命令
| 快捷键 | 命令 | 说明 |
|---|
C-c p f | projectile-find-file | 项目内查找文件 |
C-c p g | projectile-grep | 项目内搜索 |
C-c p r | projectile-replace | 项目内替换 |
C-c p d | projectile-find-dir | 查找目录 |
C-c p b | projectile-switch-to-buffer | 项目内切换缓冲区 |
C-c p k | projectile-kill-buffers | 关闭项目缓冲区 |
C-c p s s | projectile-run-shell | 在项目根目录打开 shell |
C-c p s e | projectile-run-eshell | 在项目根目录打开 eshell |
C-c p p | projectile-switch-project | 切换项目 |
C-c p S | projectile-save-project-buffers | 保存项目所有缓冲区 |
C-c p i | projectile-invalidate-cache | 清除项目缓存 |
C-c p ! | projectile-run-shell-command-root | 在项目根目录执行命令 |
使用场景
场景 1:快速切换到项目中的某个文件
C-c p f → 输入文件名片段 → TAB 补全 → RET
场景 2:在整个项目中搜索某个函数名
C-c p g → 输入搜索词 → RET → 浏览 grep 结果
场景 3:切换到另一个项目
C-c p p → 选择项目 → 自动进入该项目的文件查找
场景 4:在项目根目录运行测试
C-c p ! → npm test RET
Consult + Projectile
;; 使用 consult-projectile 集成两者优势
(use-package consult-projectile
:bind ("C-c p s" . consult-projectile))
4.7 Ace-Jump / Avy(快速跳转)
Avy 让你通过输入少量字符就跳转到屏幕上任意可见位置。
安装与配置
(use-package avy
:bind (("C-:" . avy-goto-char) ; 跳转到指定字符
("C-'" . avy-goto-char-2) ; 跳转到指定双字符
("M-g w" . avy-goto-word-1) ; 跳转到指定单词
("M-g e" . avy-goto-symbol-1)) ; 跳转到指定符号
:config
(setq avy-background t
avy-all-windows t
avy-keys '(?a ?s ?d ?f ?g ?h ?j ?k ?l)))
使用流程
1. C-: (avy-goto-char)
→ 输入一个字符,如 "d"
→ 屏幕上所有 "d" 字符都被标记为 a, s, f, g, h...
→ 按对应字母跳转到目标位置
2. C-' (avy-goto-char-2)
→ 输入两个字符,如 "th"
→ 更精确的匹配,跳转标记更少
3. M-g w (avy-goto-word-1)
→ 输入单词首字母
→ 跳转到以该字母开头的单词
4.8 跳转历史与寄存器
寄存器(Registers)
寄存器可以存储文本、位置、数字等。
| 快捷键 | 命令 | 说明 |
|---|
C-x r SPC r | point-to-register | 将位置存入寄存器 |
C-x r j r | jump-to-register | 跳转到寄存器中的位置 |
C-x r s r | copy-to-register | 将选区复制到寄存器 |
C-x r i r | insert-register | 插入寄存器内容 |
C-x r n r | number-to-register | 将数字存入寄存器 |
C-x r + r | increment-register | 给寄存器中的数字加值 |
寄存器使用示例:
存储位置:
C-x r SPC a → 将当前位置存入寄存器 a
... 编辑其他位置 ...
C-x r j a → 跳回寄存器 a 的位置
存储文本:
选择文本 → C-x r s x → 复制到寄存器 x
在其他位置 → C-x r i x → 插入寄存器 x 的内容
4.9 本章小结
| 功能 | 工具 | 核心快捷键 |
|---|
| 基本移动 | 内置 | C-f/b/n/p, M-f/b |
| 搜索 | Isearch | C-s, C-r, C-M-s |
| 替换 | query-replace | M-%, C-M-% |
| 书签 | Bookmarks | C-x r m/b/l |
| 符号跳转 | Imenu | M-g i |
| 项目导航 | Projectile | C-c p f/g/p |
| 快速跳转 | Avy | C-:, C-' |
| 寄存器 | Registers | C-x r SPC/j |
4.10 扩展阅读
← 上一章 第 03 章:基本操作 | 下一章 → 第 05 章:编辑技巧