强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

Emacs 完全指南 / 第 05 章:编辑技巧

第 05 章:编辑技巧

5.1 高级编辑命令

文本变换

快捷键命令说明
M-uupcase-word单词转大写
M-ldowncase-word单词转小写
M-ccapitalize-word单词首字母大写
C-x C-uupcase-region区域转大写
C-x C-ldowncase-region区域转小写
M-;comment-dwim智能注释
TABindent-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-jdefault-indent-new-line在注释中换行并保持注释前缀
M-^ 使用示例:

之前:
  first line
  second line

光标在第二行,按 M-^:
  first line second line

5.2 矩形编辑(Rectangle)

矩形编辑是 Emacs 的杀手级功能之一,可以同时编辑多行的同一列区域。

矩形命令

快捷键命令说明
C-x r kkill-rectangle剪切矩形区域
C-x r M-wcopy-rectangle-as-kill复制矩形区域
C-x r yyank-rectangle粘贴矩形区域
C-x r ddelete-rectangle删除矩形区域
C-x r cclear-rectangle清空矩形区域(用空格替换)
C-x r oopen-rectangle插入矩形空白区域
C-x r Nrectangle-number-lines矩形区域插入行号
C-x r tstring-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 中非常强大的功能,可以将一系列操作录制下来然后重复执行。

宏命令

快捷键命令说明
F3kmacro-start-macro开始录制宏
F4kmacro-end-or-call-macro停止录制 / 执行宏
C-x (kmacro-start-macro开始录制(旧式)
C-x )kmacro-end-macro停止录制(旧式)
C-x ekmacro-end-and-call-macro执行宏
C-u F4执行宏 N 次
C-x C-k nkmacro-name-last-macro给宏命名
C-x C-k bkmacro-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-cursorsC-> 逐个选中
KmacroF3/F4 录制与执行
缩写Abbrev自动展开预定义缩写
拼写Flyspell/Jinx实时拼写检查
补全Company/Corfu自动代码补全
片段Yasnippet代码模板

5.9 扩展阅读


← 上一章 第 04 章:移动与导航 | 下一章 → 第 06 章:缓冲区管理