Bookmark the current window layout with Ivy
27 Jun 2016Today's post is about the newest feature related to the
ivy-switch-buffer command. If you
use ivy-mode
, you're probably already using ivy-switch-buffer
since it overwrites the built-in switch-to-buffer
.
The cool thing about ivy-switch-buffer
is that it's not only buffers
that are offered for completion. Other buffer-like entities can be
there as well: bookmarks, recently opened files (recentf
), and
finally, window layouts. Since all of those are relatively the same
concept, it's very convenient to have them all in one place available
for completion.
Here are the relevant settings:
;; Enable bookmarks and recentf
(setq ivy-use-virtual-buffers t)
;; Example setting for ivy-views
(setq ivy-views
`(("dutch + notes {}"
(vert
(file "dutch.org")
(buffer "notes")))
("ivy.el {}"
(horz
(file ,(find-library-name "ivy"))
(buffer "*scratch*")))))
I did mention ivy-views
before in the
ivy-0.8.0 release post.
But now, instead of setting ivy-views
by hand, you can also bind
ivy-push-view
to a key and store as many window configurations as
you like, really fast.
What gets stored:
- The window list - all windows open on the current frame.
- The window splits relative to each other as a tree. Currently, the size of the split isn't saved, all windows are split equally.
- The point positions in each window. If you use just one window,
you've got something similar to
bookmark-set
.
Recommended key bindings
Here's what I use currently:
(global-set-key (kbd "C-c v") 'ivy-push-view)
(global-set-key (kbd "C-c V") 'ivy-pop-view)
Typical workflow
Suppose I have two files open: the file 2016-06-23-ivy-push-view.md
and the _posts
directory. By pressing C-c v I am prompted
for a view name with the default being e.g.
{} 2016-06-23-ivy-push-view.md _posts 2
.
I can still name the view however I want, but I typically just press RET. The parts of the automatic view name are:
{}
- this is a simple string marker to distinguish the views in the buffer view. If I enter only{}
intoivy-switch-buffer
prompt, the candidates will normally filter to only views, since very rarely will a file or a buffer name match{}
.2016-06-23-ivy-push-view.md _posts
is the list of buffers stored in the view. This view has only two buffers, butivy-push-view
can handle as many windows as you can cram into a single frame.2
means that I already have two views with the same buffers, each new view with the same buffers gets an increased number for the suggested name. And it's not useless to have many views for the same buffers, since the views also store point positions, not just the window list.
Here's the beauty of it for me: when I type _posts
into
ivy-switch-buffer
I can chose to open the _posts
directory in a
variety of ways:
- If the buffer is currently open, I can just switch there.
- If the buffer is currently closed, I can re-open it, thanks to
recentf
. - I can open the buffer as part of a stored view(s) in
ivy-views
.
Finally, if I decide that I don't need a particular view any more, I
can delete it with C-c V (ivy-pop-view
). It's possible to
delete many views at once by pressing C-M-m (ivy-call
),
as usual with most ivy
completion functions.
Breaking API change
While implementing ivy-set-view
I decided that the current way alist
collections are handled together with actions is sub-optimal. Here's
the new way of working:
(let (res)
(ivy-with
'(ivy-read "test: "
'(("one" . 1) ("three" . 3))
:action (lambda (x) (setq res x)))
"t C-m")
res)
;; =>
;; ("three" . 3)
Previously, the return result would be 3
, i.e. the cdr
of the
selected candidate. Any code using ivy-read
with an alist-type
collection will break. I fixed all instances in counsel.el
, and
there actually aren't too many uses in the published third party
packages.
A simple fix to the problem is to use cdr
in the action function.
Additionally, having more information available in the action function
will serve to improve the code.