Bookmarks are like Registers, with a few special traits:
Consider them as specialized, super convenient Registers.
Just like Registers are stored in the variable register-alist
,
Bookmarks are kept in the equivalent bookmark-alist
. Nothing new,
here: again, it’s all a combination of the same, basic building blocks.
If you read the Emacs Manual on Bookmarks, you will see a
bunch of functions to manipulate them. They are always useful to know,
but I would suggest you to start with a way simpler approach: just go
with Consult.
With Consult, there is a single command to keep in mind: C-x r b
(consult-bookmark)
.
(consult-bookmark)
and
give the bookmark a name.(consult-bookmark)
again,
and select the bookmark from the list.No need to remember multiple commands.
Naturally, in perfect consult style, the bookmark list can be filtered
as you type, and it provides you with a real-time preview of the file
you are going to jump back to. I often find it handy to run
(consult-bookmark)
only to preview a file: then, I hit C-g
to jump
back to what I was working.
Oh, and you can also jump to bookmarks using (consult-buffer)
: just
prefix your input with m
followed by a space, and you will be
filtering bookmarks instead of buffers. Neat!
When you quit Emacs (do you, really?), Bookmarks are automatically
serialized in the file bookmarks
, in the directory specified by
bookmark-default-file
— by default, where your init.el
file is.
Try to open that file: it just contains the bookmark-alist
variable,
serialized and prettified. No magic.
Should you ever desire to save the Bookmarks before quitting Emacs,
use M-x bookmark-save RET
. Of course, bookmark-save
comes together
with its alter-ego bookmark-load
.
Bookmarks are just items in a variable. This, and the existence of
bookmark-save
and bookmark-load
, could inspire you to have multiple
sets of bookmarks in different files, and to load them on demand,
maybe when you switch from one project to another, just like
direnv does with your .profile
.
Indeed, this is what the package Bookmark+ does.
But if you are curious, you could develop this by yourself, on top of
the built-in building blocks, with a bunch of Lisp lines. Here’s an idea:
(defun my/switch-project-bookmarks ()
(setq bookmark-default-file
(concat (projectile-project-root) ".bookmarks"))
(bookmark-load bookmark-default-file t))
(add-hook 'projectile-after-switch-project-hook #'my/switch-project-bookmarks)
This lets you have a .bookmarks
file in each project, and load it
whenever you enter its folder. It’s a draft, by no means ready for
production, but it could give you some inspiration.
There are indeed a few, interesting built-in functions for
manipulating bookmark-alist
.
One is edit-bookmark
.
Run it, and you will enter bmenu
, a Dired-like environment: just
like Dired lets you create, filter, select, delete, modify and
manipulate files, edit-bookmark
lets you do the same with
Bookmarks.
Here are some keybindings you can use:
Keybinding | What is does |
---|---|
d |
Mark a bookmark for deletion. Execute the deletion with x . |
r |
Rename a bookmark. d |
e |
Write an annotation, associated to a bookmark |
a |
Show the annotation annotations |
q |
Quit bmenu . |
Now, only the Almighty Flying Spaghetti Monster knows how much I
dislike having to keep all the keybindings in my mind. It’s just
beyond my possibilities. Having the memory of a goldfish, what I often
do is to run C-h b
(describe-bindings)
: it displays the set of all
the active keybindings for the current buffer, each with its
documentation, in a beautiful interactive and foldable list, from which I can even jump to the documentation.
After all, Emacs is sold as a self-documenting editor for a reason.
What if you jump to a bookmark and you have already killed the buffer
it targets? In the previous post we sow how
Registers deal with this with some less than crystalline machinery. On
this regards, bookmarks are way more linear: they always store a file
path, not a buffer reference.
Create a bookmark, visit the bookmark-alist
variable (M-x
describe-variable RET bookmark-alist
) and notice the field
filename
. For example:
(("writing"
(filename . "/home/arialdo/prg/personal/arialdomartini.github.io/_posts/emacs/2024-03-30-emacs-bookmarks.md")
(front-context-string . "\n\n# Less common ")
(rear-context-string . "ookmark and the ")
(position . 4605)
(last-modified 26400 56170 174644 0))
...
So, it’s always trivial for a bookmark to re-open a buffer by just visiting the file.
What if the file was moved?
Ideally, Emacs could detect when the file is being moved or renamed,
on the assumption this is done using Emacs itself: if so, Emacs could
reflect the change updating the item in bookmark-alist
.
That’s not what happens, though. Instead, if you move or rename a
file, its bookmarks will be orphaned. When trying to jump to the file,
not finding it, Emacs would invoke (bookmark-relocate)
This function
will ask you the new file location, and will then update the
filename
field for the affected bookmark.
What instead if the file is not moved, but the specific line the bookmark targets changes, maybe because other lines have been added or deleted?
This is where the context awarness of bookmarks comes into play.
Read the content of your bookmarks (M-x describe-variable RET
bookmark-alist RET
). You will see something like:
(("writing"
(filename . "/home/arialdo/prg/personal/arialdomartini.github.io/_posts/emacs/2024-03-30-emacs-bookmarks.md")
(front-context-string . "\n\n# Less common ")
(rear-context-string . "ookmark and the ")
(position . 4605)
(last-modified 26400 56170 174644 0))
...
Not only does each bookmark contain the exact location in the file, in
the field position
: it also holds a bit of the surrounding context,
in the front-context-string
and rear-context-string
fields. When
jumping to the bookmark, the 3 together are used, in the attempt of
compensating the possibly occurred changes.
I wish you a great day and many happy hours of coding!
(Thanks to Protesilaos for coaching me and for the kind review)