请教:emacs 的窗口管理

类似问题论坛应该很多,讲如何高效切窗口的

楼上的人也说了,和楼主说的最接近的可能是 windmove, 允许像 vim 的 c-w hjkl 一样去上下左右移动 focus。不要纠结于光标键(我猜测是箭头键),直接自己绑定相关的函数就行,我自己并不启用 windmove-mode

C-x o 切多个窗口是噩梦,楼上说的 repeat-mode 可以解决。不过我个人是用 ace-window, 当然看得出来楼主懒得每次看窗口 id

另外推荐 winum, 直接给各个窗口加数字 id, 然后自己绑定按键,按数字切换,不用事先看 id, 我绑定到 M-[num]

题外话:目前我正在用 transient 将窗口相关功能的命令都集合起来,作为统一入口触发,顺带支持反复键入,推荐楼主尝试

回到最开始的问题:

  • grep 模式, 我印象中的行为是:最开始会尝试分割 window 以创建新窗口,但是它并不是无限分割新建,到一定的程度就不会继续分割了,转而复用已有的窗口。这个肯定可以调,但是我没有研究过 看起来楼主已经发现问题所在了
  • compile, 没用过

推荐阅读 The Emacs Window Management Almanac, 我就是从这里看到了 winum, 有空我应该再读一遍这篇


题外话,i3 很好,但没有用过。但我能从 sway, i3 的克隆中看到 i3 的设计还是很到位的

2 个赞

手疼的可以玩玩花切锻炼一下手指(

2 个赞

如果觉得用 C-x o 切换窗口不够效率,又不想像用 ace-window 那样盯屏,可以试试坛友开发的 spatial-window (GitHub - lewang/spatial-window: Jump to Emacs windows using keyboard spatial mapping · GitHub)。其核心思想是直接将你键盘上的每个按键,按照其空间位置,映射在屏幕上对应位置的窗口,例如:q 代表左上角,p 代表右上角等等。

另外,开启 repeat-mode 后,可以用 C-x o o o 连续在窗口间跳转,并且小写 o 和大写 O 方向相反。不过,这个功能有时候会有负面效果。比如,你 C-x o 之后想写 ok,但字母 o 会被认为是连续切换窗口,结果就是在另一个你想不到的窗口里写了 k。

回头再说说题主的问题 1。总体上,我也觉得 Emacs 默认提供的窗口管理不如 Vim 的直观和便捷。比如,我想做如下图所示的窗口结构变化:

+---+---+     +---+---+
|   | B |     | A | B |
| A +---+  => +---+---+
|   | C |     |   C   |
+---+---+     +---+---+

在 Vim 里,只需要光标在 C 窗口,然后按 ctrl-w J,搞定!在 Emacs 里,hmm……我不知道最优路径是什么,我会 C-x 1 关掉其他窗口,然后手动 C-x 2C-x 3 分割,然后在每个窗口 C-x b 切换到我要的 buffer。我相信坛友们能提供很多巧妙的脚本或包来解决此问题,只是这些还没有形成我的肌肉记忆。

不过,Emacs 的窗口管理也并非处处不如 Vim。我发现 Emacs 的窗口叠放,有些像真实的文件在桌面上叠放的方式。我不知道术语怎么表达,举例说明吧。比如,我正在唯一的窗口里写代码,这时我运行 M-x compile,输入编译命令并回车后,compile 默认会在右侧打开新的窗口,显示编译结果。如果你此时 C-x o 切换到右边窗口,然后按 q,右侧窗口会被关闭,回到单个窗口的状态。这好像没什么稀奇。但如果你在右边窗口中按 C-x b 切换到其他 buffer,此时,表面上左右两个窗口没有什么本质不同,但实际上,如果你先关闭右侧新切换到的 buffer(按 C-x k RETC-x w q),再按 q 关闭 *compilation* buffer,右侧窗口就会被关闭,回到单个窗口状态。也就是说,刚才右侧窗口里其实只有两个 buffer,全部关闭后,窗口会随之关闭。这很像你在桌面的右侧临时放了一个、又一个文件,然后又把文件全部收走,右侧自然变空,桌面回到单窗口状态。

文字叙述显得琐碎,其实体验起来很符合直觉。我真正想说的是,Emacs 的窗口设计也不乏巧思,只是我自己还并没有完全领悟。

1 个赞
1 个赞

感谢分享!

我试了这个,会提示:

split-window: Window #<B> too small for splitting

切换窗口比较频繁,ace-window 开启了 (ace-window-display-mode 1) 用了段时间,经常要去看窗口编号,增加心智负担。后来绑定到一个按键,无脑往下个窗口移动,好用多了:

(require 'ace-window)
(defun lld/next-window()
  "根据当前窗口编号,移动到下一个编号的窗口。"
  (interactive)
  (let* ((number (string-to-number
                  (window-parameter
                   (selected-window) 'ace-window-path)))
         (len (length (aw-window-list)))
         (number (+ number 1)))
    (when (> number len)
      (setq number 1))
    (dolist (win (aw-window-list))
      (when (eq number
                (string-to-number
                 (window-parameter win 'ace-window-path)))
        (aw-switch-to-window win)))))
(bind-keys ("C-`" . lld/next-window))

可以用workgroups2(我在维护,是个比较老的软件了,同类软件很多), 特定布局你就以名字如"w1", "w2"什么的保存.然后再载入w1或w2

我目前用的就是 C-x o 然后 repeat,挺好用的。

spacemacs里有一个函数是spacemacs/winum-select-window-[x],绑定了command+x(窗口对应的数字)。

vim管理窗口怎么可能更方便?Emacs有很多种方法,手搓一个hydra,想怎么移动就怎么移动。另外,ace-window那么大的leading char很醒目啊,还可以改成字母 asdfgh……

参考代码:

3 个赞

这个问题我是用 rotate 包解决的(装上了,但是可能一个月都用不到一次……)。emacs 确实没有自带很多功能,需要用包或者自己的小东西填补

但是在其他的垃圾工具面前,vim 和 emacs 其实是一个阵营的,应当团结起来。emacs 很好,vim 也很好

感谢大家。很认同这句话,我下面去研究研究大家的解决方案。

感觉 Emacs 和 Vim 的很多地方是可能是设计哲学不同导致的差异,可能只是我目前没有意识到所以感觉窗口管理 Vim 比 Emacs 的方便,所以想找一个“更方便的” Emacs 窗口操作。比如:对于长 Tab Emacs 是把光标放在第一个位置,而 Vim 是放在最后的位置,我猜测就是因为 Emacs 的复制粘贴或者 C-k 之类的操作都是光标在最前面操作的,Vim 有行模式所以很少需要光标在第一个位置。Vim 的行连接是从上到下进行的,Emacs 的行连接是从下往上进行的,我猜测是因为 Emacs 常常在屏幕下半部分操作的?因为一般是通过 C-L 定位到中间然后向下操作,但是 M-h 又是从上到下进行的..

1 个赞

我发现问题了,如果我把这个取消,当多窗口的时候会自动替换另一个窗口的内容。

比如,当我有两个窗口,在第一个窗口按 M-. 第二个窗口的内容会替换为 xref 了。

1 个赞

这个在 emacs 31 里有一大堆命令可以实现:

  window-layout-transpose
    Command: Transpose windows under WINDOW.
    Properties: event-symbol-element-mask event-symbol-elements modifier-cache
  	      function-history
  
  window-layout-flip-leftright
    Command: Horizontally flip windows under WINDOW.
    Properties: repeat-map function-history
  window-layout-flip-topdown
    Command: Vertically flip windows under WINDOW.
    Properties: repeat-map function-history
  window-layout-flip-topdown
    Command: Vertically flip windows under WINDOW.
    Properties: repeat-map function-history
  window-layout-flip-topdown
    Command: Vertically flip windows under WINDOW.
    Properties: repeat-map function-history
  window-layout-flip-leftright
    Command: Horizontally flip windows under WINDOW.
    Properties: repeat-map function-history
  window-layout-flip-leftright
    Command: Horizontally flip windows under WINDOW.
    Properties: repeat-map function-history
  window-layout-rotate-anticlockwise
    Command: Rotate window layout of WINDOW counterclockwise by 90 degrees.
    Properties: repeat-map function-history
  window-layout-rotate-clockwise
    Command: Rotate window layout under WINDOW clockwise by 90 degrees.
    Properties: repeat-map function-history
  window-layout-rotate-clockwise
    Command: Rotate window layout under WINDOW clockwise by 90 degrees.
    Properties: repeat-map function-history
  window-layout-rotate-anticlockwise
    Command: Rotate window layout of WINDOW counterclockwise by 90 degrees.
    Properties: repeat-map function-history
  rotate-windows
    Command: Rotate windows under WINDOW in cyclic ordering.
    Properties: repeat-map function-history
  rotate-windows-back
    Command: Rotate windows under WINDOW backward in cyclic ordering.
    Properties: repeat-map function-history

这些命令默认都绑定在 C-x w 下面。

1 个赞

强烈推荐我这个魔改的shackle 和 winnie-mode github.com:QiangF/emacs-winnie.git