Emacs 完全指南 / 第 17 章:键位设计
第 17 章:键位设计
17.1 键位设计哲学
设计原则
| 原则 | 说明 |
|---|---|
| 一致性 | 相似操作使用相似按键 |
| 分层 | 全局 → 模式 → 局部,逐层覆盖 |
| 前缀 | 按功能域分组,避免键位冲突 |
| 频率 | 常用操作优先放到容易按的键上 |
| 文档化 | 使用 Which-key 让按键可发现 |
Emacs 的键位空间
┌───────────────────────────────────────────────┐
│ Emacs 键位空间 │
├───────────┬───────────────────────────────────┤
│ 保留键位 │ C-x, C-c (模式), C-h, ESC │
├───────────┼───────────────────────────────────┤
│ C-c 前缀 │ C-c [a-zA-Z] (用户保留) │
│ │ C-c C-x (Org-mode 等使用) │
├───────────┼───────────────────────────────────┤
│ F 键 │ F1-F12 (适合个人功能) │
├───────────┼───────────────────────────────────┤
│ Hyper │ H- (需要映射 CapsLock) │
├───────────┼───────────────────────────────────┤
│ Super │ s- (Win/Cmd 键) │
└───────────┴───────────────────────────────────┘
⚠️ 注意:
- C-c 后跟 控制字符(C-x, C-c 等)是模式保留的
- C-c 后跟 普通字符(a-z)是用户保留的
- 尽量不要覆盖 Emacs 默认键位
推荐的前缀方案
;; 功能域前缀
;; C-c b → Buffer 相关
;; C-c f → File 相关
;; C-c g → Git 相关
;; C-c l → LSP 相关
;; C-c n → Notes / Roam 相关
;; C-c p → Project / Projectile 相关
;; C-c s → Search 相关
;; C-c t → Toggle 相关
;; C-c w → Window 相关
;; C-c x → Execute / External 相关
17.2 基本键位绑定
global-set-key
;; 绑定单个键
(global-set-key (kbd "C-c n") 'org-capture)
;; 使用 define-key 绑定到特定键映射
(define-key global-map (kbd "C-c f") 'find-file)
;; unbind 键
(global-unset-key (kbd "C-z")) ; 取消挂起
;; 与 mode-map 绑定
(define-key org-mode-map (kbd "C-c o") 'my-org-command)
配置模式键位的推荐方式
;; 方式 1::bind(推荐,延迟加载)
(use-package avy
:bind (("C-:" . avy-goto-char)
("C-'" . avy-goto-char-2)))
;; 方式 2::bind 结合 keymap
(use-package org
:bind (:map org-mode-map
("C-c o l" . org-insert-link)
("C-c o s" . org-schedule)))
;; 方式 3::general(general.el 包)
(use-package general
:config
(general-define-key
"C-c f" 'find-file
"C-c g" 'magit-status
"C-c s" 'consult-line))
;; 方式 4:直接 define-key
(with-eval-after-load 'org
(define-key org-mode-map (kbd "C-c o l") 'org-insert-link))
17.3 General.el(键位管理)
General.el 是最流行的键位管理包,提供统一的键位定义接口。
(use-package general
:config
;; 定义全局快捷键
(general-define-key
"C-=" 'text-scale-increase
"C--" 'text-scale-decrease
"M-o" 'ace-window)
;; 定义前缀映射
(general-create-definer my/leader-keys
:keymaps '(normal insert visual emacs)
:prefix "SPC"
:global-prefix "C-SPC")
;; SPC 前缀键位(类似 Doom Emacs)
(my/leader-keys
;; 文件
"f" '(:ignore t :which-key "file")
"ff" '(find-file :which-key "find-file")
"fs" '(save-buffer :which-key "save")
"fr" '(consult-recent-file :which-key "recent")
;; 缓冲区
"b" '(:ignore t :which-key "buffer")
"bb" '(consult-buffer :which-key "switch")
"bk" '(kill-current-buffer :which-key "kill")
"bs" '(save-buffer :which-key "save")
;; 项目
"p" '(:ignore t :which-key "project")
"pf" '(projectile-find-file :which-key "find-file")
"ps" '(consult-ripgrep :which-key "search")
"pp" '(projectile-switch-project :which-key "switch")
;; Git
"g" '(:ignore t :which-key "git")
"gs" '(magit-status :which-key "status")
"gp" '(magit-push :which-key "push")
"gc" '(magit-commit :which-key "commit")
;; 窗口
"w" '(:ignore t :which-key "window")
"wh" '(windmove-left :which-key "left")
"wl" '(windmove-right :which-key "right")
"wk" '(windmove-up :which-key "up")
"wj" '(windmove-down :which-key "down")
"wd" '(delete-window :which-key "delete")
"ws" '(split-window-below :which-key "split-v")
"wv" '(split-window-right :which-key "split-h")
;; 搜索
"s" '(:ignore t :which-key "search")
"ss" '(consult-line :which-key "line")
"sg" '(consult-ripgrep :which-key "grep")
"si" '(consult-imenu :which-key "imenu")))
17.4 Which-key(键位提示)
Which-key 在按下前缀键后弹出所有可用的后续按键列表,是"可发现性"的关键。
(use-package which-key
:diminish which-key-mode
:init
(which-key-mode 1)
:config
(setq which-key-idle-delay 0.5
which-key-sort-order 'which-key-key-order-alpha
which-key-sort-uppercase-first nil
which-key-add-column-padding 1
which-key-max-display-columns 4
which-key-min-display-lines 3
which-key-side-window-location 'bottom
which-key-show-prefix 'left
which-key-separator " → "))
显示效果
按下 C-x 后:
C-x → ┌────────────────────────────────────────┐
│ 8 ... ← window │
│ + ... ← balance-windows │
│ 0 ... ← delete-window │
│ 1 ... ← delete-other-windows │
│ 2 ... ← split-window-below │
│ 3 ... ← split-window-right │
│ b ... ← switch-to-buffer │
│ d ... ← dired │
│ g ... ← magit-status │
└────────────────────────────────────────┘
17.5 Hydra(临时键位模式)
Hydra 让你创建"临时键位模式"——按下一系列相关按键而不需要重复前缀。
(use-package hydra)
;; 窗口调整 Hydra
(defhydra hydra-window (:color pink :hint nil)
"
窗口操作
_h_: ← _j_: ↓ _k_: ↑ _l_: →
_H_: 宽度← _L_: 宽度→ _J_: 高度↓ _K_: 高度↑
_v_: 垂直分割 _s_: 水平分割 _d_: 关闭 _o_: 关闭其他
_u_: 撤销 _q_: 退出
"
("h" windmove-left)
("j" windmove-down)
("k" windmove-up)
("l" windmove-right)
("H" shrink-window-horizontally)
("L" enlarge-window-horizontally)
("J" shrink-window)
("K" enlarge-window)
("v" split-window-right)
("s" split-window-below)
("d" delete-window)
("o" delete-other-windows)
("u" winner-undo)
("q" nil))
(global-set-key (kbd "C-c w") 'hydra-window/body)
;; 缩放 Hydra
(defhydra hydra-zoom (:color pink :hint nil)
"
缩放
_+_ 放大 _-_ 缩小 _0_ 重置 _q_ 退出
"
("+" text-scale-increase)
("-" text-scale-decrease)
("0" (text-scale-set 0))
("q" nil))
(global-set-key (kbd "C-c z") 'hydra-zoom/body)
;; Git 操作 Hydra
(defhydra hydra-git (:color blue :hint nil)
"
Git
_s_: status _d_: diff _l_: log _b_: blame
_c_: commit _p_: push _f_: pull _q_: quit
"
("s" magit-status)
("d" magit-diff)
("l" magit-log)
("b" magit-blame)
("c" magit-commit)
("p" magit-push)
("f" magit-pull)
("q" nil))
(global-set-key (kbd "C-c g") 'hydra-git/body)
Hydra 颜色说明
| 颜色 | 含义 |
|---|---|
red | 执行后退出 Hydra |
blue | 执行后退出 Hydra |
pink | 执行后保持 Hydra(默认) |
amaranth | 不允许退出(只能 q 退出) |
使用场景
窗口调整流程:
1. C-c w → 进入窗口 Hydra
2. h h h → 连续按 h 向左移动(不需要重复前缀!)
3. L L L → 连续按 L 增大宽度
4. v → 垂直分割
5. q → 退出 Hydra
17.6 Evil 模式(Vim 键位)
;; Evil 模式让 Emacs 支持 Vim 的模态编辑
(use-package evil
:init
(setq evil-want-integration t
evil-want-keybinding nil
evil-want-C-u-scroll t
evil-want-C-i-jump t)
:config
(evil-mode 1))
;; Evil 集成(让 Evil 覆盖更多模式)
(use-package evil-collection
:after evil
:config
(evil-collection-init))
;; Evil Leader 键
(use-package evil-leader
:after evil
:config
(global-evil-leader-mode)
(evil-leader/set-key
"f" 'find-file
"b" 'switch-to-buffer
"g" 'magit-status
"s" 'consult-line))
17.7 Transient(临时菜单)
Transient 是 Magit 使用的临时菜单系统,Emacs 29+ 内置。
(require 'transient)
;; 定义一个临时菜单
(transient-define-prefix my/text-ops ()
"文本操作菜单"
["大小写"
("u" "大写" upcase-region :if use-region-p)
("l" "小写" downcase-region :if use-region-p)
("c" "首字母大写" capitalize-region :if use-region-p)]
["编码"
("e" "URL 编码" url-encode-region :if use-region-p)
("d" "URL 解码" url-decode-region :if use-region-p)])
(global-set-key (kbd "C-c t") 'my/text-ops)
17.8 本章小结
| 工具 | 用途 | 说明 |
|---|---|---|
global-set-key | 基本绑定 | 全局键位绑定 |
general.el | 键位管理 | Leader 键、分组绑定 |
which-key | 键位发现 | 按键提示 |
hydra | 临时键位 | 连续操作无需重复前缀 |
evil | Vim 模态 | 模态编辑 |
transient | 临时菜单 | Magit 风格菜单 |
17.9 扩展阅读
← 上一章 第 16 章:界面定制 | 下一章 → 第 18 章:Docker 集成