面向产品经理的Emacs教程:24. Emacs里的窗口管理

· 1331字 · 3分钟

1 课程回顾 🔗

通过上节课的学习,我们了解到如何在 Emacs 里使用 shell,尤其是 eshell,非常方便好用,我们可以在 Emacs 里快速进入 eshell 执行一些命令,得到你想要的结果,在 eshell 里还支持 elisp 函数,着实非常方便。

今天我们学习在 Emacs 里的窗口管理。

2 概述 🔗

在第一节课里,我们谈到了 Emacs 的一些基本概念,如 buffer, frame, window 等,这里我们再次温习一下 frame 和 window 的概念:

frame
窗体,Emacs的整个窗口。
window
分屏,Emacs在一个窗口内创建的多个分屏。

比如下图就是在一个窗体内部的2个分屏:

针对 frame,我们可以有很多命令和快捷键,如 make-frame (s-n):

今天主要讨论的是对 window 的管理。

3 shackle 窗口行为控制 🔗

我们可以通过 shackle 插件来自定义窗口的弹出行为,主要有:方向、大小、弹出方式等等。

先来安装和配置它:

(use-package shackle
  :ensure t
  :hook (after-init . shackle-mode)
  :init
  (setq shackle-lighter "")
  (setq shackle-select-reused-windows nil) ; default nil
  (setq shackle-default-alignment 'below)  ; default below
  (setq shackle-rules
        ;; CONDITION(:regexp)            :select     :inhibit-window-quit   :size+:align|:other     :same|:popup
        '((compilation-mode              :ignore t)
          ("\\*Async Shell.*\\*" :regexp t :ignore t)
          ("\\*corfu.*\\*"       :regexp t :ignore t)
          ("*eshell*"                    :select t                          :size 0.4  :align t     :popup t)
          (helpful-mode                  :select t                          :size 0.6  :align right :popup t)
          ("*Messages*"                  :select t                          :size 0.4  :align t     :popup t)
          ("*Calendar*"                  :select t                          :size 0.3  :align t     :popup t)
          ("*info*"                      :select t                                                  :same t)
          (magit-status-mode             :select t   :inhibit-window-quit t                         :same t)
          (magit-log-mode                :select t   :inhibit-window-quit t                         :same t)
          ))
  )

其中,最重要的就是 shackle-rules 这个变量的配置,这个变量的配置主要有下面几个方面:

CONDITION
条件,即这一条规则适用于满足什么样的条件才生效。这个条件可以是正则表达式,可以是字符串,可以是模式,如上面的例子, *eshell* 就是匹配到缓冲区名字是 *eshell* 时生效。
:select
控制弹出 window 后是否选中
:inhibit-window-quit
按“q”退出时,不删除这个缓冲区
:size
0-1之间的数,控制 window 的百分比宽度或高度,如0.5就是指一半的宽度或高度
:align
弹出的 window 往哪里看齐,可以取值为 t, ’left, ‘right, ‘below, ‘above
:other
如果当前 frame 有多个 window,是否复用另外一个 window
:popup
弹出一个新的 window,而不是复用当前 window
:same
不弹出 window,复用当前 window
:ignore
禁止显示该窗口

当我们安装配置完 shackle 之前,我们打开 eshell 时,是直接覆盖当前 window,如下图:

当我们安装配置完 shackle 之后,我们再次打开 eshell 时,就变成了在下方以高度40%的方式打开一个新的 window,如下图:

4 popper 窗口弹出行为管理 🔗

popper 可以控制窗口的弹出行为,可以与 shackle 一起配合使用。我们先来安装配置它:

(use-package popper
  :ensure t
  :bind (("M-`"     . popper-toggle-latest)
         ("M-<tab>" . popper-cycle)
         ("M-\\"    . popper-toggle-type)
         )
  :init
  (setq popper-reference-buffers
        '("\\*Messages\\*"
          "\\*Async Shell Command\\*"
          help-mode
          helpful-mode
          occur-mode
          pass-view-mode
          "^\\*eshell.*\\*$" eshell-mode ;; eshell as a popup
          "^\\*shell.*\\*$"  shell-mode  ;; shell as a popup
          ("\\*corfu\\*" . hide)
          (compilation-mode . hide)
          ;; derived from `fundamental-mode' and fewer than 10 lines will be considered a popup
          (lambda (buf) (with-current-buffer buf
                          (and (derived-mode-p 'fundamental-mode)
                               (< (count-lines (point-min) (point-max))
                                  10))))
          )
        )
  (popper-mode +1)
  (popper-echo-mode +1)
  :config
  ;; group by project.el, projectile, directory or perspective
  (setq popper-group-function nil)

  ;; pop in child frame or not
  (setq popper-display-function #'display-buffer-in-child-frame)

  ;; use `shackle.el' to control popup
  (setq popper-display-control nil)
  )

配置完后,我们打开 eshell ,可以看到左下角显示了 POP 的字样,我们按下 M-` 可以一键关闭/打开这个 window,而且,这个 window 的位置和高度都如我们在 shackle 插件设置的那样,非常方便:

5 winner 窗口布局恢复 🔗

最后介绍一个内置的 winner 插件,作为补充,我们可以通过 winner-undowinner-redo 命令恢复或重做当前的窗口布局。我们先来安装和配置它:

(use-package winner
  :ensure nil
  :hook (after-init . winner-mode)
  :commands (winner-undo winner-redo)
  :config
  (setq winner-boring-buffers
        '("*Completions*"
          "*Compile-Log*"
          "*inferior-lisp*"
          "*Fuzzy Completions*"
          "*Apropos*"
          "*Help*"
          "*cvs*"
          "*Buffer List*"
          "*Ibuffer*"
          "*esh command on file*"))
  )

例如,当前的窗口布局如下,左边是 emacs-config.org 文件,显示配置,右上是 *message* 缓冲区,右下是编译缓冲区:

此时,如果我们不小心按下 C-x 1 将窗口都删除了,我们还想恢复之前的布局,就可以通过 winner-undo 命令来恢复:

6 结语 🔗

通过今天的学习,我们了解了如何在 Emacs 控制窗口的行为,它能进一步的提升我们使用 Emacs 的顺滑度和工作效率。

这节课的配置文件的快照见:emacs-config-l24.org

你也可以在 这里 查看最新的配置文件。