Pokemacs layout provides a way to save window layouts and apply them. Unlike desktop-save
, it’s not about saving the state of your Emacs but more about restoring your windows the way you want them.
A friend of mine had a wide screen and told me about his morning routine:
- Split horizontally thrice
- Start magit
- Lock the window
- Split vertically
- Compile
- Lock the window
- Split vertically
- Open a LSP buffer
- Lock the window
I had just received a wide screen and the idea of having to do that whenever I wanted to open Emacs frightened me. I started with a simple function to help my friends:
(defun pokemacs-restore-session (&optional columns) "Restore a session by creating the proper buffers." (interactive "P") (when use-visual-fill (visual-fill-column-mode -1)) (setq columns-number (or columns pokemacs-columns)) (setq current-prefix-arg nil) (setq middle-window (selected-window)) (dotimes (_ (- columns-number 2)) (setq middle-window (split-window-right)) (select-window middle-window) (balance-windows)) (select-window middle-window) ;; Third window, start with magit (setq magit-window (split-window-right)) (select-window magit-window) (with-selected-window magit-window (defvar magit-display-buffer-function) (let ((magit-display-buffer-function 'magit-display-buffer-same-window-except-diff-v1)) (magit-status-quick)) ;; Lock after creating the magit buffer otherwise it's created in another window (locked-window-buffer-mode)) ;; Fourth window below magit is the compilation window, ;; create a buffer without compiling (setq compilation-window (split-window-below)) (select-window compilation-window) (set-window-buffer (selected-window) (get-buffer-create "*compilation*")) (with-selected-window compilation-window (locked-window-buffer-mode)) ;; Fifth window below compilation is the lsp-help window, ;; create a buffer without calling lsp (setq lsp-window (split-window-below)) (select-window lsp-window) (set-window-buffer (selected-window) (get-buffer-create "*lsp-help*")) (with-selected-window lsp-window (locked-window-buffer-mode)) (select-window middle-window) (balance-windows))
And then I thought: “Hey, I’m sure I could create a simple layout type and allow for more customisations, let’s do it!”. This is the result of this experiment.
Currently this package is not in any package manager but you can use it easily with elpaca or straight:
(use-package pokemacs-layout
:ensure (:host github :repo "mattiasdrp/pokemacs-layout")
:commands pokemacs-layout-apply pokemacs-layout-monitor-names
pokemacs-restore-session pokemacs-layout-current-monitor)
Or, if you don’t use use-package
:
(require 'pokemacs-layout)
Just make sure that pokemacs-layout.el
is in load-path
A layout is a plist of '(:monitor monitor :windows windows-layout :sides sides-layout :children children-layout)
.
Let’s break it down.
The :monitor
property specifies on which monitor this frame should be created. As you’ll usually apply a layout from a monitor, it’s not recommended to specify a :monitor
property for the main layout.
A window layout is a list of alists:
- 3 columns, no actions
(defvar pokemacs-layout-simple-3C
'((column . ())
(column . ())
(none . ())))
;; Or:
(defvar pokemacs-layout-even-simpler-3C
'((column . (nil nil 3))))
You can already use these layout with
(pokemacs-layout--apply-layout pokemacs-layout-simple-3C)
(pokemacs-layout--apply-layout pokemacs-layout-even-simpler-3C)
(if you read this README in emacs you can evaluate the source blocks with C-c C-c
and see the results immediately – use winner-undo
to go back to the previous layout)
This simple example shows the skeleton of a window layout: (split . action)
.
Currently split
can be:
column
(split horizontally)row
(split vertically)none
(don’t split this window)
An action
is composed of three fields (fill lock number)
:
fill
is anything to help creating the buffer in the window (nil
, a buffer name, a function call…)lock
ist
if this window should be locked, meaning no other buffer can spawn therenumber
to repeat this alist multiple times
- 3 columns, the last one is also split in 3 with 3 specific buffers
(defvar pokemacs-layout-complex
'((column . (nil nil 2))
(none . ('((row . (magit-status-quick t))
(row . ("*compilation*" t))
(none . ("*lsp-help*" t))) nil nil))))
(pokemacs-layout--apply-layout pokemacs-layout-complex)
This layout will create three columns:
- The first two will be the current buffer duplicated in two windows
- The third one is split in three locked rows:
- The first one is created by executing
(magit-status-quick)
- The second one is created with the
compilation
buffer (created if it doesn’t exist) - The third one is like the second one but with the
lsp-help
buffer
- The first one is created by executing
A side layout is an alist of each side associated to its layout.
(defvar pokemacs-layout-simple-right-side
'((right . ((1 "*Messages*" t)
(2 "*scratch*" t)))))
And you can see the result with
(pokemacs-layout--apply-layout-sides-alist pokemacs-layout-simple-right-side)
An side
is a list of slots (number fill lock)
:
number
is the slot index (starts at 1)fill
is anything to help creating the buffer in the window (nil
, a buffer name, a function call…)lock
ist
if this window should be locked, meaning no other buffer can spawn there
Finally, the :children
is a list of layouts with the same architecture as the main layout. The interesting thing is that if the :monitor
property is set, the child is created in a frame on another monitor.
Example layouts:
2 columns with the message and scratch buffers and a side window with the magit buffer and a buffer for completion or lsp-help results
(defconst pokemacs-layout-prog-default
'((:windows
((column . ("*Messages*" nil 1))
(none . ("*scratch*" nil 1)))
:sides
((right . ((1 magit-status-quick t)
(2 ("*compilation*" "*lsp-help*") t)))))))
Some columns with the current buffer and a side window with the magit buffer and a buffer for completion or lsp-help results. The number of columns is specified by pokemacs-layout-columns
or with the universal argument C-u
(try it with M-<n>
)
A second frame is created on a second monitor specified by the custom variable pokemacs-layout-second-monitor
and contains the message and scratch buffers.
(defconst pokemacs-layout-prog-default-frames-custom-number
`((:windows
((column . (nil nil pokemacs-layout-columns)))
:sides
((right . ((1 magit-status-quick t)
(2 ("*compilation*" "*lsp-help*") t))))
:children
((:monitor ,pokemacs-layout-second-monitor
:windows
((column . ("*Messages*" nil 1))
(none . ("*scratch*" nil 1))))))))
Wrap your newly created layouts:
(pokemacs-layout--create-layout
"my/layout"
my/layout
"My layout description")
And add it to pokemacs-layout-layouts
, this will make it available when calling M-x pokemacs-layout-apply
.
- [ ] Improve this README
- [ ] Add some pictures or gif to this README
- [X] Add new frames to the layout choices with monitor choice