强曰为道

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

第 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临时键位连续操作无需重复前缀
evilVim 模态模态编辑
transient临时菜单Magit 风格菜单

17.9 扩展阅读


← 上一章 第 16 章:界面定制 | 下一章 → 第 18 章:Docker 集成