第 05 章:编辑技巧
第 05 章:编辑技巧
5.1 高级编辑命令
文本变换
| 快捷键 | 命令 | 说明 |
|---|
M-u | upcase-word | 单词转大写 |
M-l | downcase-word | 单词转小写 |
M-c | capitalize-word | 单词首字母大写 |
C-x C-u | upcase-region | 区域转大写 |
C-x C-l | downcase-region | 区域转小写 |
M-; | comment-dwim | 智能注释 |
TAB | indent-for-tab-command | 智能缩进 |
行操作
;; 实用行操作
(defun my/duplicate-line ()
"复制当前行。"
(interactive)
(let ((col (current-column)))
(move-beginning-of-line 1)
(kill-line)
(yank)
(open-line 1)
(forward-line 1)
(yank)
(move-to-column col)))
(global-set-key (kbd "C-c d") 'my/duplicate-line)
(defun my/move-line-up ()
"将当前行上移。"
(interactive)
(transpose-lines 1)
(forward-line -2))
(defun my/move-line-down ()
"将当前行下移。"
(interactive)
(forward-line 1)
(transpose-lines 1)
(forward-line -1))
(global-set-key (kbd "M-<up>") 'my/move-line-up)
(global-set-key (kbd "M-<down>") 'my/move-line-down)
行排序与去重
;; 排序命令
M-x sort-lines ; 按字母排序
M-x sort-numeric-fields ; 按数字排序
M-x sort-columns ; 按列排序
M-x reverse-region ; 反转行顺序
;; 去重
M-x delete-duplicate-lines ; 删除重复行(Emacs 29+)
M-x uniq ; 相邻去重
Join Line
| 快捷键 | 命令 | 说明 |
|---|
M-^ | delete-indentation | 将下一行合并到当前行 |
M-j | default-indent-new-line | 在注释中换行并保持注释前缀 |
M-^ 使用示例:
之前:
first line
second line
光标在第二行,按 M-^:
first line second line
5.2 矩形编辑(Rectangle)
矩形编辑是 Emacs 的杀手级功能之一,可以同时编辑多行的同一列区域。
矩形命令
| 快捷键 | 命令 | 说明 |
|---|
C-x r k | kill-rectangle | 剪切矩形区域 |
C-x r M-w | copy-rectangle-as-kill | 复制矩形区域 |
C-x r y | yank-rectangle | 粘贴矩形区域 |
C-x r d | delete-rectangle | 删除矩形区域 |
C-x r c | clear-rectangle | 清空矩形区域(用空格替换) |
C-x r o | open-rectangle | 插入矩形空白区域 |
C-x r N | rectangle-number-lines | 矩形区域插入行号 |
C-x r t | string-rectangle | 矩形区域填充字符串 |
矩形编辑流程
原始数据:
name = "Alice"
name = "Bob"
name = "Charlie"
目标:在等号后面统一加一个空格
步骤:
1. 光标定位到第一行等号后的引号位置
2. C-SPC(设置标记)
3. 移动到第三行等号后的引号位置
4. C-x r t → " " → RET
结果:
name = "Alice"
name = "Bob"
name = "Charlie"
矩形编号示例
原始数据:
item
item
item
item
item
步骤:
1. 选中矩形区域(覆盖所有 "item")
2. C-x r N → 起始编号: 1 → 格式: %d. → RET
结果:
1. item
2. item
3. item
4. item
5. item
;; 使用 cua-mode 获得更直观的矩形编辑
;; C-RET 进入矩形选择模式
(cua-mode 1)
(setq cua-enable-cua-keys nil) ; 禁用 C-x, C-c 的 CUA 绑定
;; 使用 C-RET 开始矩形选择
;; 使用方向键调整大小
;; 输入文本会同时插入到矩形区域的每一行
多光标编辑(Multiple Cursors)
;; multiple-cursors 包提供类似 VS Code 的多光标功能
(use-package multiple-cursors
:bind (("C-S-c C-S-c" . mc/edit-lines) ; 矩形区域多光标
("C->" . mc/mark-next-like-this) ; 选中下一个相同文本
("C-<" . mc/mark-previous-like-this) ; 选中上一个相同文本
("C-c C-<" . mc/mark-all-like-this) ; 选中所有相同文本
("C-S-<mouse-1>" . mc/add-cursor-on-click))) ; 鼠标点击添加光标
;; 使用流程:
;; 1. C-> 逐个选中下一个匹配
;; 2. 编辑时所有光标同时生效
;; 3. C-g 退出多光标模式
5.3 宏录制(Keyboard Macro)
宏是 Emacs 中非常强大的功能,可以将一系列操作录制下来然后重复执行。
宏命令
| 快捷键 | 命令 | 说明 |
|---|
F3 | kmacro-start-macro | 开始录制宏 |
F4 | kmacro-end-or-call-macro | 停止录制 / 执行宏 |
C-x ( | kmacro-start-macro | 开始录制(旧式) |
C-x ) | kmacro-end-macro | 停止录制(旧式) |
C-x e | kmacro-end-and-call-macro | 执行宏 |
C-u F4 | — | 执行宏 N 次 |
C-x C-k n | kmacro-name-last-macro | 给宏命名 |
C-x C-k b | kmacro-bind-to-key | 将宏绑定到按键 |
宏录制流程
场景:给每一行的开头加上 "• "
步骤:
1. 移动到第一行开头
2. F3(开始录制)
3. C-a → 输入 "• " → C-n → C-a
4. F4(停止录制)
5. 反复按 F4 执行,或 C-u 999 F4 执行到文件末尾
录制过程中的操作记录:
C-a → 行首
"• " → 插入文本
C-n → 下一行
C-a → 行首(为下次执行准备位置)
高级宏技巧
;; 1. 保存宏到文件
;; C-x C-k n my-macro RET → 命名宏
;; M-x insert-kbd-macro RET my-macro RET → 插入到文件
;; 保存为:
(fset 'my-macro
(kmacro-lambda-form [?• ? return] 0 "%d"))
;; 2. 将宏保存为命令
;; 录制宏后:
;; C-x C-k n → 输入名称 → 保存到 init.el
;; 3. 在宏中使用递归编辑
;; C-x q → 在宏执行时暂停,等待用户输入
;; 当你希望宏在某些行停下来让你手动处理时很有用
宏的实用场景
场景 1:批量格式化 CSV 数据
原始:Alice,30,Engineer
目标:| Alice | 30 | Engineer |
宏操作:C-a | SPC M-f M-f M-f S-SPC M-b M-b M-b C-w | SPC M-f SPC | RET C-a
场景 2:批量添加引号
原始:name, age, city
目标:"name", "age", "city"
宏操作:" C-s , RET " " C-n C-a
场景 3:转换 JSON 键值对
原始:key: value
目标:"key": "value",
宏操作:" C-a " C-e " C-b , C-n C-a
5.4 缩写与展开(Abbrev)
;; 启用缩写模式
(setq-default abbrev-mode t)
(setq abbrev-file-name "~/.emacs.d/abbrevs")
(if (file-exists-p abbrev-file-name)
(quietly-read-abbrev-file))
;; 定义缩写:
;; 1. 输入缩写词,如 "teh"
;; 2. C-x a g → 输入展开文本 "the" → RET
;; 3. 以后输入 "teh " 会自动展开为 "the "
;; 缩写表定义
(define-abbrev-table 'global-abbrev-table
'(
("btw" "by the way")
("fyi" "for your information")
("asap" "as soon as possible")
("wrt" "with respect to")
))
5.5 拼写检查
内置 Ispell/Aspell
| 快捷键 | 命令 | 说明 |
|---|
M-$ | ispell-word | 检查当前单词 |
M-x ispell-buffer | — | 检查整个缓冲区 |
M-x ispell-region | — | 检查选区 |
M-x ispell-change-dictionary | — | 切换词典 |
;; 安装 aspell
;; sudo apt install aspell aspell-en aspell-zh
;; 配置
(setq ispell-program-name "aspell")
(setq ispell-extra-args '("--sug-mode=ultra"))
(setq ispell-dictionary "en_US")
;; 使用 flyspell 实时检查
(use-package flyspell
:hook ((text-mode . flyspell-mode)
(prog-mode . flyspell-prog-mode)) ; 只检查注释和字符串
:config
(setq flyspell-issue-message-flag nil))
Jinx(现代拼写检查)
;; jinx 是更快的拼写检查包
(use-package jinx
:hook (emacs-startup . global-jinx-mode)
:bind ([remap ispell-word] . jinx-correct))
5.6 代码补全基础
内置补全
| 快捷键 | 说明 |
|---|
M-/ | dabbrev-expand — 动态缩写补全 |
C-M-/ | complete-symbol — 符号补全 |
TAB | 模式相关的智能补全 |
;; dabbrev 补全(最古老的补全方式)
;; M-/ 在当前缓冲区中搜索匹配的文本并补全
;; 连续按 M-/ 循环更多候选
;; hippie-expand(更强大的 dabbrev)
(global-set-key (kbd "M-/") 'hippie-expand)
(setq hippie-expand-try-functions-list
'(try-complete-file-name-partially
try-complete-file-name
try-expand-all-abbrevs
try-expand-dabbrev
try-expand-dabbrev-all-buffers
try-expand-dabbrev-from-kill))
Company 模式(经典补全框架)
(use-package company
:diminish company-mode
:hook (after-init . global-company-mode)
:config
(setq company-minimum-prefix-length 1
company-idle-delay 0.1
company-selection-wrap-around t
company-tooltip-align-annotations t
company-show-quick-access t)
:bind (:map company-active-map
("C-n" . company-select-next)
("C-p" . company-select-previous)
("RET" . company-complete-selection)
("TAB" . company-complete-common-or-cycle)
("<tab>" . company-complete-common-or-cycle)))
5.7 Yasnippet(代码片段)
(use-package yasnippet
:diminish yas-minor-mode
:hook ((prog-mode . yas-minor-mode)
(org-mode . yas-minor-mode))
:config
(yas-reload-all)
;; 自定义片段目录
(setq yas-snippet-dirs '("~/.emacs.d/snippets")))
;; 片段集合
(use-package yasnippet-snippets
:after yasnippet)
编写自定义片段
# ~/.emacs.d/snippets/python-mode/ifmain
# -*- mode: snippet -*-
# name: ifmain
# key: ifmain
# --
if __name__ == "__main__":
$0
# ~/.emacs.d/snippets/org-mode/src
# -*- mode: snippet -*-
# name: code block
# key: src
# --
#+BEGIN_SRC ${1:python}
$0
#+END_SRC
5.8 本章小结
| 功能 | 工具 | 说明 |
|---|
| 文本变换 | 内置 | 大小写、排序、去重 |
| 矩形编辑 | CUA / 内置 | C-x r k/t/o 等 |
| 多光标 | multiple-cursors | C-> 逐个选中 |
| 宏 | Kmacro | F3/F4 录制与执行 |
| 缩写 | Abbrev | 自动展开预定义缩写 |
| 拼写 | Flyspell/Jinx | 实时拼写检查 |
| 补全 | Company/Corfu | 自动代码补全 |
| 片段 | Yasnippet | 代码模板 |
5.9 扩展阅读
← 上一章 第 04 章:移动与导航 | 下一章 → 第 06 章:缓冲区管理