《elisp 每日一练》——Window 锁定与专属窗口

《elisp 每日一练》——Window 锁定与专属窗口

Masimaro
2026-08-31 / 0 评论 / 0 阅读 / 正在检测是否收录...

我们想象这样一个场景,假设你精心布局了当前的编程窗口,左边是文件树、右边一个大窗口区域是代码编辑区域,右侧有一个小的显示当前类和函数定义的缩率图,下面有输出的日志。但是你运行某个命令弹出了 *Help* 或者 *Compile* 占用了你的某个窗口,本来对整个项目可以一览无余的,现在不得不重新关掉这个新显示的窗口。整个场景想想就糟心。我们有没有办法固定这些布局,新弹出的窗口不要破话它。Emacs一般是只有你想不到,没有它办不到的。

背景知识

上述场景的解决办法是通过 专属窗口(Dedicated Window)窗口参数(Window Parameters),我们可以给特定 Window 加上“锁”,强制 Emacs 将新 Buffer 弹出到指定位置,保护主窗口不被侵占。

各位读者可能有这样的疑问,我们前面介绍了 display-bufferpop-to-buffer 已经解决了新显示的buffer乱弹窗导致布局混乱的问题,为什么又有一个 deddicated windowwindow parameters 这类解决方案呢?

其实它们是不同的机制,侧重于从不同方面解决。display-bufferpop-to-buffer 讲究的是这次应该将buffer放在哪。它侧重于将一个内容显示在窗口的某个位置,如果这个位置有了窗口,则重用这个窗口。

Dedicated WindowWindow Parameters 解决的是“这个窗口本身有什么规则”

例如我们上面那个布局,display-bufferpop-to-buffer 虽然可以靠算法精确的控制它显示的区域,但是无法保证我们主要的代码区域永远属于显示代码的buffer,设置专有窗口之后,这个窗口就专门用来显示这个buffer的内容。

每个窗口都有一个 dedicated 的标志位,当设置这个标志之后,Emacs会尽量避免在本窗口中显示其他内容,例如 switch-bufferpop-to-bufferdisplay-buffer 会尽量避免在对应窗口中显示新内容。

我们可以通过 set-window-dedicated-p 将一个窗口设置为专属窗口,它接受两个参数:

  • WINDOW 表示设置哪个窗口
  • flagt 时设置专属窗口,为 nil 解除该设置

通过 window-dedicated-p 函数可以查看窗口的专属状态。

但是仅仅设置 dedicated 标签并不意味着可以精细的管理窗口。我们可以通过设置窗口参数来更细致的控制窗口。Emacs对每个窗口都维护着一个参数的 alist 列表,可以控制窗口的一些行为。窗口参数实际是一推窗口的标签,有些标签Emacs自己就可以利用来改变窗口行为,我们也可以自定这些标签,然后在我们的程序中通过这些标签控制窗口行为和显示。也就是说窗口参数给我们扩展窗口带来了极大的便利

设置窗口属性可以使用 set-window-parameter 设置属性,通过 window-parameters 获取对应的属性

练习

我们来实现一个小例子,来体会一下该如何使用窗口自定义参数改变窗口行为。我们实现一个通过自定义参数 this-window-heightthis-window-width 参数来修改光标所在窗口大小的函数

我们先封装一些获取和设置窗口大小的基本函数

(defun my/set-window-height(height)
  (let ((window (selected-window)))
    (set-window-parameter window 'this-window-height height)
    (window-resize window (- height (window-total-height window)))))

(defun my/set-window-width(width)
  (let ((window (selected-window)))
    (set-window-parameter window 'this-window-width width)
    (window-resize window (- width (window-total-width window)) t)))

(defun my/get-window-height()
  (window-height (selected-window)))

(defun my/get-window-width()
  (window-width (selected-window)))

然后提供一个根据当前参数中存储的大小数据来更新窗口实际大小的函数

(defun my/update-window-size()
  (let* ((params (window-parameters (selected-window)))
     (height (cdr (assq 'this-window-height params)))
     (width (cdr (assq 'this-window-width params))))
    (if (not)
    (setq height (my/get-window-height)))
    (if (not width)
    (setq width (my/get-window-width)))
    (my/set-window-height height)
    (my/set-window-width width)))

最终我们在此基础上封装一套修改大小的函数:

(defun my/width_zoomin()
  (interactive)
  (let* ((params (window-parameters (selected-window)))
     (width (cdr (assq 'this-window-width params))))
    (if (not width)
    (setq width (my/get-window-width)))
    (setq width (- 10 width))
    (my/set-window-width width)))

(defun my/width_zoomout()
  (interactive)
  (let* ((params (window-parameters (selected-window)))
     (width (cdr (assq 'this-window-width params))))
    (if (not width)
    (setq width (my/get-window-width)))
    (setq width (+ 10 width))
    (my/set-window-width width)))

(defun my/height_zoomin()
  (interactive)
  (let* ((params (window-parameters (selected-window)))
     (height (cdr (assq 'this-window-height params))))
    (if (not height)
    (setq height (my/get-window-height)))
    (setq height (- 5 height))
    (my/set-window-height height)))

(defun my/height_zoomout()
  (interactive)
  (let* ((params (window-parameters (selected-window)))
     (height (cdr (assq 'this-window-height params))))
    (if (not height)
    (setq height (my/get-window-height)))
    (setq height (+ 5 height))
    (my/set-window-height height)))

小结

本节我们介绍了专属窗口和窗口属性。本节有关的函数有:

  • set-window-dedicated-p
  • window-dedicated-p
  • set-window-parameter
  • window-parameter
0

评论 (0)

取消