(or emacs irrelevant)

lispy point history

It was kind of disappointing to summarize in the last post that there weren't many new features for lispy 0.24.0. So I thought long and hard and came up with something quite obvious: since lispy offers commands to quickly manipulate point and mark, it should offer one to quickly restore them.

Here's the bulk of the new code:

(defvar lispy-pos-ring (make-ring 200)
  "Ring for point and mark position history.")
(ring-insert lispy-pos-ring 1)

(defun lispy--remember ()
  "Store the current point and mark in history."
  (if (region-active-p)
      (let ((bnd (lispy--bounds-dwim)))
        (unless (equal bnd (ring-ref lispy-pos-ring 0))
          (ring-insert lispy-pos-ring bnd)))
    (unless (eq (point) (ring-ref lispy-pos-ring 0))
    (ring-insert lispy-pos-ring (point)))))

(defun lispy-back ()
  "Move point to a previous position"
  (interactive)
  (if (zerop (ring-length lispy-pos-ring))
      (user-error "At beginning of point history")
    (let ((pt (ring-remove lispy-pos-ring 0)))
      (if (consp pt)
          (lispy--mark pt)
        (deactivate-mark)
        (goto-char pt)))))

Here, I'm using lispy-pos-ring made with make-ring to store the last 200 point and mark positions. In Elisp, a ring is basically a stack backed by a vector. When the vector space overflows, the older stuff is overwritten.

In this ring I store each time either the point, or a cons of the point and mark if the region is active. This way possible to restore the region even if it was deactivated several movement commands ago.

I've put lispy--remember into the most used navigation commands:

  • j - lispy-down
  • k - lispy-up
  • h - lispy-left
  • l - lispy-right
  • f - lispy-flow
  • i - lispy-mark-car (when the region is active)
  • a - lispy-ace-paren

And lispy-back is now bound to b. The previous binding of b - lispy-store-region-and-buffer is now bound to xB (quite close to B - the binding for lispy-ediff-regions).

I really like the new command, it's especially useful to reverse h and l. Previously, they could be reversed with f, but that can get annoying if you have to press it many times. This results in a much more relaxed editing - I know that whatever I press, I can restore the point position quickly if needed.

This feature is added to the already present list of safeguards:

  • j and k are guaranteed not to exit the parent list.
  • > and < are guaranteed not to exit the parent list.
  • C reverses itself.

Here's a recipe to copy the third item of the current list and move the point back: 4mnb. You can use it to copy a function's docstring if you're in Elisp. You'll need 3mnb for Clojure, since it weirdly has the docstring before the arguments. I think it's pretty cool: by typing 3mnb I'm basically calling (kill-new (caddr (current-sexp))) on my own code.