上一篇提到,可以使用 split-window 来创建新的窗口,也区分了buffer和窗口的概念,同时也知道如何获取、修改窗口展示的buffer。split-window 创建的窗口有一个特点:它需要我们自己指定新窗口的摆放位置,窗口位置由我们自己维护。一般情况下,在编程或者做工程化的项目时引入 workspace 的概念,我们规定如何划分窗口区间是有利的,但是有些窗口像 *Help*、*Messages* 、Shell 这类窗口我们经常看一眼或者临时需要打开,如果将它纳入自主的窗口管理就不合适了。本节我们来解决这个问题
知识铺垫
Emacs 中提供了两个函数 display-buffer 和 pop-to-buffer。
我们先从 display-buffer 开始。它的函数原形为
(display-buffer BUFFER-OR-NAME &optional ACTION FRAME)它会根据我们已有的窗口布局来选择合适的窗口显示对应的buffer内容,也就是说它不一定会创建新的窗口,它只是根据用户需求选择合适的窗口来显示。如果没有合适的则创建,有则利用已有的窗口来显示。我们可以通过 ACTION 参数来定义它的行为。
它只负责显示buffer,而不会跳转光标,这种一般用于设置查看日志但是不操作buffer内容的场合。如果我们希望在显示buffer之后光标也跟着跳转,可以使用函数 pop-to-buffer。它们显示buffer的行为完全一样,但是 pop-to-buffer 会移动光标到新的buffer所所在窗口中。
(pop-to-buffer BUFFER-OR-NAME &optional ACTION NORECORD)我们看到它的函数参数中,也有一个 ACTION 参数。我们可以通过这个参数来实现自定义显示行为。一个标准的ACTION是 '(FUNCTIONS . ALIST) 的结构。
其中 FUNCTION 表示函数或者函数列表,Emacs 在显示时会Emacs 会从左到右依次尝试这些函数,直到其中有一个成功找到或创建了窗口为止。
ALIST 是一个由 (属性 . 值) 构建的关联列表。用来微调上述函数执行时的行为(如窗口高度、分屏方向、是否专有等)。
各位读者看到这些结构也不必惊慌,Emacs为我们提供了一些常用的通用封装。对于function来说,有以下常用的预定义函数:
display-buffer-reuse-window: 重用已有窗口, 如果该 Buffer 已经在某个窗口显示了,直接使用该窗口。display-buffer-same-window: 占用当前窗口, 直接在当前光标所在的窗口中显示该 Buffer。display-buffer-pop-up-window: 切分新窗口, 根据split-height-threshold或split-width-threshold在周围切出一个新窗口。display-buffer-below-selected: 在下方切分, 在当前窗口的正下方新建或重用一个窗口。display-buffer-at-bottom: 底部全宽弹出, 在当前 Frame(或屏幕)的最底部创建一个跨越整宽的窗口。display-buffer-in-side-window: 侧边栏/侧边窗口, 在 Frame 的四周(left/right/top/bottom)作为Sidebar弹出(通常搭配专属属性)。display-buffer-pop-up-frame: 弹出新Frame, 直接新建一个独立的 OS 窗口(Frame)。
对于 ACTION 中的 ALIST 也有一些预定义的属性
(window-height . N): 设置弹出窗口的高度。整数(如 15)表示固定行数;浮点数(如 0.3)表示占当前/全屏高度的比例。(window-width . N): 设置弹出窗口的宽度。整数表示列数;浮点数表示宽度比例。(side . left/right/top/bottom): 指定侧边栏弹出的位置,可以与上述display-buffer-in-side-window搭配使用(slot . N): 侧边栏的位置,用于同侧有多个窗口的情况,(reusable-frames . val): 寻找可重用窗口时的范围:t(所有 Frame)、visible(所有可见 Frame)、0(当前及当前可见 Frame)、nil(仅当前 Frame)。(inhibit-same-window . t): 明确要求绝对不能占用当前窗口。(dedicated . t): 将新创建的窗口标记为专有(Dedicated)。专有窗口在用户切换其他 Buffer 时不会被轻易替换。
有了这些知识,我们可以对显示messages buffer给出简单的定义,例如我希望message buffer永远只出现在最底部,并且占用整个底部。就可以有下面的代码:
(defun my/show-messages-buffer()
(interactive)
(display-buffer "*Messages*"
;; 底部全宽显示
'((display-buffer-at-bottom)
;;ALIST 参数, 高度为30,强制不覆盖当前窗口
(window-height 30)
(inhibit-same-window . t))))实际上除了在 ACTION 参数中设置这些属性外,还可以通过全局变量 display-buffer-alist 来设置这里的 ACTION 参数。它与这里通过参数设置的方式相比,优势在于它可以通过正则表达式来匹配一次性定义一堆buffer的显示方式。它的数据结构为:
((CONDITION . ACTION) (CONDITION . ACTION) ...)
上一节我们提到,可以使用 quit-window 和 delete-window 删除窗口,但是没有讲它们的区别,因为那个时候还没有引入这种显示buffer的新姿势。现在有了这些知识就可以来说说它们的区别了。
今天的新姿势对比之前来讲,最大的区别就是不一定要创建新窗口,它可以重用现在的窗口来显示新内容。quit-window 是一种温和的删除窗口的方式,对于这种重用的状态,它只是不显示新buffer的内容但是会酌情保留原来的窗口。而 delete-window 来说就比较不讲情面了,它不管你还需不需要用这个窗口,直接删除。
练习
今天的练习,我们来实现一个查看message、log这种buffer的功能,我们希望通过 display-buffer-alist 设置全局的,对于这种窗口我们采用底部显示的方式,取底部35%的区域进行显示。
(add-to-list 'display-buffer-alist
'("\\*.*\\(?:log\\|messages\\).*\\*"
(display-buffer-reuse-window display-buffer-at-bottom);; 优先重用已打开的对应窗口;若未打开,则在当前 Frame 的最底部弹出
(window-height . 0.35) ;; 占用25%的空间
(reusable-frames . visible))) ;; 允许在当前可见的其他Frame中搜寻
(display-buffer "*Messages*")总结
本篇我们了解了另一种通过window 显示buffer的方式,主要的函数有:
display-bufferpop-to-buffer
评论 (0)