I use point-undo to jump between positions in the cursor history. However, point-undo uses every previous position of the cursor, and I often experience that what I actually want is to jump just to the last/next line where my cursor was. So I don't want to jump around within one line. In other words, I would like the cursor history to store only the last position per line.

How to do this?

  • You might want to check avy it allows you to quickly jump to any lines in the visible text with avy-goto-lines command. Oct 21 '16 at 12:41
  • 1
    You could give emacswiki.org/emacs/download/pointhistory.el a try. This is not on a per-line basis but it only saves "large jumps" as introduced by scroll-up and so on. Since multiple next-line commands also cause a scroll-up those line-commands are also covered in some sense.
    – Tobias
    Oct 21 '16 at 15:05
  • @Tobias Thanks for the hint! Unfortunately, jumps seem to be too large with pointhistory. And the way it jumps is somewhat enigmatic, as to the positions that go into the ring. Maybe the code of pointhistory could be modified to also include line-based jumps?
    – Timm
    Oct 22 '16 at 8:42
  • ace-jump-mode is a nice alternative to avy. Demoed here. Oct 4 at 15:50

I've tried to modify point-undo. It seems to work. However I included the first jump on the current line. That's intended behaviour.

(defvar point-undo-ring-length 20)

(defvar point-undo-ring (make-ring point-undo-ring-length))
(make-variable-buffer-local 'point-undo-ring)

(defvar point-redo-ring (make-ring point-undo-ring-length))
(make-variable-buffer-local 'point-redo-ring)

(defun point-undo-pre-command-hook ()
  "Save positions before command."
  (unless (or (eq this-command 'point-undo)
              (eq this-command 'point-redo))
    (let ((line (line-number-at-pos)))
      (when (eq line (cdr (nth 0 (ring-elements point-undo-ring))))
        (ring-remove point-undo-ring 0))
      (ring-insert point-undo-ring (cons (point) line))
      (setq point-redo-ring (make-ring point-undo-ring-length)))))
(add-hook 'pre-command-hook 'point-undo-pre-command-hook)

(defun point-undo-doit (ring1 ring2)
  "ring1, ring2 = {point-undo-ring, point-redo-ring}"
  (condition-case nil
        (goto-char (car (nth 0 (ring-elements ring1)))) 
        (ring-insert ring2 (ring-remove ring1 0)))
    (error nil)))

(defun point-undo ()
  "Undo position."
  (point-undo-doit point-undo-ring point-redo-ring))

(defun point-redo ()
  "Redo position."
  (when (or (eq last-command 'point-undo)
            (eq last-command 'point-redo))
    (point-undo-doit point-redo-ring point-undo-ring)))

(define-key global-map [f9] 'point-undo)
(define-key global-map [f10] 'point-redo)
  • Great! Maybe you could add a variable to set the size of the ring?
    – Timm
    Oct 24 '16 at 17:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.