强曰为道

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

第 06 章:缓冲区管理

第 06 章:缓冲区管理

6.1 缓冲区概念回顾

缓冲区(Buffer)是 Emacs 的核心抽象之一。在第 3 章中我们介绍了基本概念,本章将深入高级缓冲区管理。

缓冲区类型

类型命名规则示例说明
文件缓冲区文件名init.el, README.md对应磁盘文件
间接缓冲区*name*<N>*init.el*<2>同一文件的另一个视图
特殊缓冲区*name**scratch*, *Messages*Emacs 内部使用的缓冲区
隐藏缓冲区nameminibuf-0内部缓冲区,空格开头
临时缓冲区任意由命令动态创建用完即弃

缓冲区状态标志

  MR Buffer           Size  Mode           File
  -- ------           ----  ----           ----
  ** init.el          2341  Emacs-Lisp     ~/.emacs.d/init.el
  %% config.org       8900  Org            ~/.config.org
  .* README.md        1200  Markdown       ~/project/README.md
    *scratch*          119  Lisp           <buffer *scratch*>
    *Messages*         357  Fundamental    <buffer *Messages*>
标志含义
**已修改且未保存
%%只读
.*已修改但有自动保存
--未修改

6.2 高级缓冲区操作

缓冲区切换增强

;; 使用 ibuffer 替代默认的 list-buffers
(global-set-key (kbd "C-x C-b") 'ibuffer)

;; 使用 consult-buffer 提供更好的缓冲区切换体验
(use-package consult
  :bind ("C-x b" . consult-buffer))

;; 使用 uniquify 让同名缓冲区有唯一路径
(require 'uniquify)
(setq uniquify-buffer-name-style 'forward)
(setq uniquify-after-kill-buffer-p t)

间接缓冲区(Indirect Buffer)

;; 间接缓冲区允许同时查看同一文件的不同部分
;; C-x 4 c  → clone-indirect-buffer-other-window

;; 使用场景:
;; 1. 同时查看一个大文件的两个不同部分
;; 2. 用不同模式查看同一内容
;; 3. 窄化(narrow)到不同区域

;; 示例:
;; 在 init.el 中:
;; C-x 4 c → 输入新名称 → 在新窗口中创建间接缓冲区
;; 两个窗口可以独立滚动、独立窄化

缓冲区窄化(Narrowing)

快捷键命令说明
C-x n nnarrow-to-region窄化到选区
C-x n wwiden取消窄化,显示全部
C-x n dnarrow-to-defun窄化到当前函数
C-x n pnarrow-to-page窄化到当前页
;; 窄化使用场景:
;; 1. 在大文件中只关注某个函数
;;    C-x n d → 只显示当前函数
;;    C-x n w → 恢复显示全部
;;
;; 2. 只编辑选中的区域
;;    选中文本 → C-x n n → 只显示选区
;;    C-x n w → 恢复

;; 警告:窄化后容易误以为文件只有这么点内容
;; 开启 narrow-to-defun-indicator
(setq narrow-to-defun-indicator t)

6.3 Ibuffer 详解

Ibuffer 是 Emacs 内置的强大缓冲区管理器。

Ibuffer 基本操作

快捷键说明
RET打开缓冲区
d标记删除
m标记操作
x执行标记的操作
s按列排序
/ g按模式分组
/ m按主模式过滤
/ f按文件名过滤
/ r按正则过滤
% n按名称正则标记
* *标记所有修改的缓冲区
S保存标记的缓冲区
V查看缓冲区(只读)
U取消所有标记

Ibuffer 分组配置

;; 自定义 Ibuffer 分组
(setq ibuffer-saved-filter-groups
      '(("default"
         ("Dired" (mode . dired-mode))
         ("Org" (mode . org-mode))
         ("Python" (mode . python-mode))
         ("JavaScript" (or (mode . js-mode)
                           (mode . typescript-mode)
                           (mode . rjsx-mode)))
         ("Elisp" (mode . emacs-lisp-mode))
         ("Web" (or (mode . html-mode)
                    (mode . css-mode)))
         ("Shell" (or (mode . shell-mode)
                      (mode . eshell-mode)
                      (mode . vterm-mode)))
         ("Special" (name . "^\\*")))))

;; 应用分组
(add-hook 'ibuffer-mode-hook
          (lambda ()
            (ibuffer-switch-to-saved-filter-groups "default")))

;; 隐藏空组
(setq ibuffer-show-empty-filter-groups nil)

;; 自动更新
(setq ibuffer-auto-mode t)

Ibuffer 操作流程

打开 Ibuffer:C-x C-b

┌──────────────────────────────────────────────┐
│  Ibuffer: 12 buffers                         │
│                                              │
│  Default [3]                                 │
│    ** init.el          2341 Emacs-Lisp       │
│    ** config.org       8900 Org              │
│      README.md         1200 Markdown         │
│                                              │
│  Python [2]                                  │
│      main.py            890 Python           │
│      utils.py           450 Python           │
│                                              │
│  Special [4]                                 │
│    * *scratch*          119 Lisp             │
│    * *Messages*         357 Fundamental      │
│    * *Compile-Log*       89 Fundamental      │
│    * *Help*             200 Help             │
│                                              │
└──────────────────────────────────────────────┘

操作步骤:
1. d d → 标记删除 *Compile-Log* 和 *Help*
2. x   → 确认删除
3. / m → 过滤只显示 Python 模式
4. S   → 保存所有 Python 缓冲区

6.4 Workspace 与透视(Perspective)

Tab Bar(Emacs 27+ 内置)

;; 启用 tab-bar
(tab-bar-mode 1)
(setq tab-bar-close-button-show nil
      tab-bar-new-button-show nil
      tab-bar-tab-hints t
      tab-bar-show 1)  ; 只有一个 tab 时隐藏

;; 创建和切换 tab
;; C-x t 2    → 创建新 tab
;; C-x t 0    → 关闭当前 tab
;; C-x t o    → 切换到下一个 tab
;; C-x t RET  → 选择 tab

;; 重命名 tab
;; C-x t r    → 重命名当前 tab

Perspective.el(透视管理)

(use-package perspective
  :bind (("C-x C-b" . persp-list-buffers)
         ("C-x b" . persp-switch-to-buffer)
         ("C-x k" . persp-kill-buffer))
  :custom
  (persp-mode-prefix-key (kbd "C-c M-p"))
  :init
  (persp-mode))

;; 透视操作:
;; C-c M-p s → 切换/创建透视
;; C-c M-p k → 关闭透视
;; C-c M-p a → 将缓冲区添加到当前透视
;; C-c M-p r → 重命名透视

;; 使用场景:
;; Perspective 1: "前端开发" — 只包含前端相关缓冲区
;; Perspective 2: "后端开发" — 只包含后端相关缓冲区
;; Perspective 3: "文档写作" — 只包含文档相关缓冲区

Treemacs(项目侧边栏)

(use-package treemacs
  :bind ("C-c t" . treemacs)
  :config
  (setq treemacs-width 30
        treemacs-is-never-other-window t
        treemacs-sorting 'alphabetic-asc))

;; Treemacs 操作:
;; C-c t          → 打开/关闭侧边栏
;; RET            → 打开文件
;; j / k          → 上下移动
;; d / f          → 目录展开/折叠
;; cf / cd        → 创建文件/目录
;; R              → 重命名
;; d              → 删除
;; ?              → 帮助

6.5 缓冲区生命周期管理

自动清理

;; 自动关闭长时间不用的缓冲区
(use-package midnight
  :config
  ;; 每天午夜清理
  (midnight-delay-set 'midnight-delay "4:00am")
  ;; 清理规则
  (setq clean-buffer-list-kill-regexps
        '("\\`\\*.*\\*\\'"
          "\\` ?tmp"))
  (setq clean-buffer-list-kill-never-buffer-names
        '("*scratch*" "*Messages*" "*dashboard*")))

;; 手动清理未修改的特殊缓冲区
(defun my/cleanup-buffers ()
  "清理不重要的缓冲区。"
  (interactive)
  (dolist (buf (buffer-list))
    (let ((name (buffer-name buf)))
      (when (and (string-prefix-p "*" name)
                 (not (member name '("*scratch*" "*Messages*")))
                 (not (buffer-modified-p buf)))
        (kill-buffer buf)))))
(global-set-key (kbd "C-c C-k") 'my/cleanup-buffers)

Buffer List 排序策略

;; 按最近使用顺序排列缓冲区(MRU)
;; 使用 ibuffer 的排序
(setq ibuffer-default-sorting-mode 'recency)

;; 或者使用 bs 包
(use-package bs
  :bind ("C-x C-b" . bs-show)
  :config
  (setq bs-configurations
        '(("files" "^\\*" nil nil bs-visits-non-file bs-sort-buffer-interns-are-last))))

6.6 本章小结

功能工具说明
缓冲区列表IbufferC-x C-b,分组、过滤、批量操作
缓冲区切换consult-bufferC-x b,模糊搜索
窄化NarrowingC-x n n/d/w,只显示部分内容
间接缓冲区Indirect BufferC-x 4 c,同一文件多个视图
Tab/WorkspaceTab-barC-x t 2/o/0,窗口布局隔离
透视PerspectiveC-c M-p s,缓冲区集合管理
侧边栏TreemacsC-c t,文件树导航
自动清理midnight定时清理无用缓冲区

6.7 扩展阅读


← 上一章 第 05 章:编辑技巧 | 下一章 → 第 07 章:文件操作