How to persist evil marks, like `mD`, across sessions

What happened?

In Vim, global marks are persisted across sessions. I’d expect Doom to reproduce the same behavior.

What did you expect to happen?

Define a global mark mA in a file a.txt, close Doom, open Doom later in file b.txt jump back to the global mark A using 'A in file a.txt


Evil stores these marks in the variable evil-markers-alist as markers—an Elisp datatype that can’t trivially be serialized and restored later.

Buuut judging from the variable’s docstring, we do have the option of swapping out those markers with (PATH . POS) cons cells, where PATH is a string and POS is an integer, and those are trivial to serialize, so give this a try:

;;; add to $DOOMDIR/config.el
(after! savehist
  (add-to-list 'savehist-additional-variables 'evil-markers-alist)
  (add-hook! 'savehist-save-hook
    (kill-local-variable 'evil-markers-alist)
    (dolist (entry evil-markers-alist)
      (when (markerp (cdr entry))
        (setcdr entry (cons (file-truename (buffer-file-name (marker-buffer (cdr entry))))
                            (marker-position (cdr entry)))))))
  (add-hook! 'savehist-mode-hook
    (setq-default evil-markers-alist evil-markers-alist)
    (kill-local-variable 'evil-markers-alist)
    (make-local-variable 'evil-markers-alist)))

I’m exploiting savehist to do the persisting, I just have to make sure the variable is serializable when it does it (and that it won’t pick up a buffer-local value by accident).

This only serializes global marks however, not buffer-local ones, but iirc, that’s how it is with Vim too. If this works well enough, I’ll consider adding it to our :editor evil module.

1 Like

Some improvements indeed! However, there are a few caveats yet:

  1. If I follow the steps in original question it works fine! I’m only bothered by an annoying confirmation message “Visit file again?” that could be suppressed.

  2. However, if the global mark was already visited and I move to yet another file c.txt and hit the jump to global marker again then it doesn’t work. If I use :marks while visiting the a.txt file (where global mark was initially set) then I can see my global mark in the list. If I use :marks in the c.txt file then I don’t see anything in the list.

Seems like after first jump to the global mark it becomes “buffer-local”.

I found a reference in the Evil repository about this bug.

1 Like