Function: goto-last-change
goto-last-change is an autoloaded, interactive and byte-compiled
function defined in goto-chg.el.
Signature
(goto-last-change ARG)
Documentation
Go to the point where the last edit was made in the current buffer.
Repeat the command to go to the second last edit, etc.
To go back to more recent edit, the reverse of this command, use M-x goto-last-change-reverse (goto-last-change-reverse)
or precede this command with C-u (universal-argument) - (minus).
It does not go to the same point twice even if there has been many edits
there. I call the minimal distance between distinguishable edits "span".
Set variable glc-default-span to control how close is "the same point".
Default span is 8.
The span can be changed temporarily with C-u (universal-argument) right before M-x goto-last-change (goto-last-change):
C-u (universal-argument) <NUMBER> set current span to that number,
C-u (universal-argument) (no number) multiplies span by 4, starting with default.
The so set span remains until it is changed again with C-u (universal-argument), or the consecutive
repetition of this command is ended by any other command.
When span is zero (i.e. C-u (universal-argument) 0) subsequent M-x goto-last-change (goto-last-change) visits each and
every point of edit and a message shows what change was made there.
In this case it may go to the same point twice.
This command uses undo information. If undo is disabled, so is this command.
At times, when undo information becomes too large, the oldest information is
discarded. See variable undo-limit.
Key Bindings
Source Code
;; Defined in ~/.emacs.d/elpa/goto-chg-20240407.1110/goto-chg.el
;;;###autoload
(defun goto-last-change (arg)
"Go to the point where the last edit was made in the current buffer.
Repeat the command to go to the second last edit, etc.
\nTo go back to more recent edit, the reverse of this command, use \\[goto-last-change-reverse]
or precede this command with \\[universal-argument] - (minus).
\nIt does not go to the same point twice even if there has been many edits
there. I call the minimal distance between distinguishable edits \"span\".
Set variable `glc-default-span' to control how close is \"the same point\".
Default span is 8.
The span can be changed temporarily with \\[universal-argument] right before \\[goto-last-change]:
\\[universal-argument] <NUMBER> set current span to that number,
\\[universal-argument] (no number) multiplies span by 4, starting with default.
The so set span remains until it is changed again with \\[universal-argument], or the consecutive
repetition of this command is ended by any other command.
\nWhen span is zero (i.e. \\[universal-argument] 0) subsequent \\[goto-last-change] visits each and
every point of edit and a message shows what change was made there.
In this case it may go to the same point twice.
\nThis command uses undo information. If undo is disabled, so is this command.
At times, when undo information becomes too large, the oldest information is
discarded. See variable `undo-limit'."
(interactive "P")
(cond ((not (eq this-command last-command))
;; Start a glc sequence
;; Don't go to current point if last command was an obvious edit
;; (yank or self-insert, but not kill-region). Makes it easier to
;; jump back and forth when copying seleced lines.
(setq glc-probe-depth (if (memq last-command '(yank self-insert-command)) 1 0)
glc-direction 1
glc-current-span glc-default-span)
(if (< (prefix-numeric-value arg) 0)
(error "Negative arg: Cannot reverse as the first operation"))))
(cond ((and (null buffer-undo-list)
(or (not (boundp 'buffer-undo-tree))
(null buffer-undo-tree)))
(error "Buffer has not been changed"))
((eq buffer-undo-list t)
(error "No change info (undo is disabled)")))
(cond ((numberp arg) ; Numeric arg sets span
(setq glc-current-span (abs arg)))
((consp arg) ; C-u's multiply previous span by 4
(setq glc-current-span (* (abs (car arg)) glc-default-span))
(message "Current span is %d chars" glc-current-span))) ;todo: keep message with "waiting" and "is saved"
(setq glc-direction (if (< (prefix-numeric-value arg) 0) -1 1))
(let* (rev ; Reversed (and filtered) undo list
pos ; The pos we look for, nil until found
(n 0) ; Steps in undo list (length of 'rev')
(undo-tree-p (bound-and-true-p undo-tree-mode))
(orig-l (if (not undo-tree-p)
buffer-undo-list
(undo-list-transfer-to-tree)
;; Each node has a list of undo entries: Need to flatten.
;; Keep current entries and next node to consider in tuple.
(cons nil (undo-tree-current buffer-undo-tree))))
(l orig-l)
(passed-save-entry (not (buffer-modified-p)))
(new-probe-depth glc-probe-depth))
;; Walk back and forth in the buffer-undo-list, each time one step deeper,
;; until we can walk back the whole list with a 'pos' that is not coming
;; too close to another edit.
(while (null pos)
(setq new-probe-depth (+ new-probe-depth glc-direction))
(when (< glc-direction 0)
(setq rev ()
n 0
l orig-l
passed-save-entry (not (buffer-modified-p))))
(when (< new-probe-depth 1)
(error "No later change info"))
(when (> n 150)
(message "working..."))
;; Walk forward in buffer-undo-list, glc-probe-depth steps.
;; Build reverse list along the way
(while (< n new-probe-depth)
(let ((entry (if (not undo-tree-p)
(if l
(pop l)
(error "No further change info"))
(when (null (car l))
(setq l (cons (cons nil
(undo-tree-node-undo
(or (cdr l)
(error "No further change info"))))
(undo-tree-node-previous (cdr l)))))
(pop (car l)))))
(cond ((glc-is-positionable entry)
(setq n (1+ n)
rev (cons entry rev)))
((or passed-save-entry (glc-is-filetime entry))
(setq passed-save-entry t)))))
;; Walk back in reverse list, from older to newer edits.
;; Adjusting pos along the way.
(setq pos (glc-adjust-list rev)))
;; Found a place not previously visited, in 'pos'.
;; (An error have been issued if nothing (more) found.)
(if (> n 150)
(message nil)) ; remove message "working..."
(if (and (= glc-current-span 0) (glc-get-descript (car rev) n))
(message "%s" (glc-get-descript (car rev) n))
;; else
(if passed-save-entry
(message "(This change is saved)")))
(setq glc-probe-depth new-probe-depth)
(goto-char pos)))