Function: track-changes-fetch

track-changes-fetch is a byte-compiled function defined in track-changes.el.gz.

Signature

(track-changes-fetch ID FUNC)

Documentation

Fetch the pending changes for tracker ID pass them to FUNC.

ID is the tracker ID returned by a previous track-changes-register. FUNC is a function. It is called with 3 arguments (BEGIN END BEFORE) where BEGIN..END delimit the region that was changed since the last time track-changes-fetch was called and BEFORE is a string containing the previous content of that region (or just its length as an integer if the tracker ID was registered with the nobefore option). If track-changes detected that some changes were missed, then BEFORE will be the symbol error to indicate that the buffer got out of sync. This reflects a bug somewhere, so please report it when it happens.

If no changes occurred since the last time, it doesn't call FUNC and returns nil, otherwise it returns the value returned by FUNC and re-enable the TRACKER corresponding to ID.

During the call to FUNC, track-changes-undo-only indicates if the changes were the result of undo.

View in manual

Source Code

;; Defined in /usr/src/emacs/lisp/emacs-lisp/track-changes.el.gz
(defun track-changes-fetch (id func)
  "Fetch the pending changes for tracker ID pass them to FUNC.
ID is the tracker ID returned by a previous `track-changes-register'.
FUNC is a function.  It is called with 3 arguments (BEGIN END BEFORE)
where BEGIN..END delimit the region that was changed since the last
time `track-changes-fetch' was called and BEFORE is a string containing
the previous content of that region (or just its length as an integer
if the tracker ID was registered with the `nobefore' option).
If track-changes detected that some changes were missed, then BEFORE will
be the symbol `error' to indicate that the buffer got out of sync.
This reflects a bug somewhere, so please report it when it happens.

If no changes occurred since the last time, it doesn't call FUNC and
returns nil, otherwise it returns the value returned by FUNC
and re-enable the TRACKER corresponding to ID.

During the call to FUNC, `track-changes-undo-only' indicates if the changes
were the result of `undo'."
  (track-changes--trace)
  (cl-assert (memq id track-changes--trackers))
  (unless (equal track-changes--buffer-size (buffer-size))
    (track-changes--recover-from-error
     `(buffer-size ,track-changes--buffer-size ,(buffer-size))))
  (let ((beg nil)
        (end nil)
        (is-undo t)
        (before t)
        (lenbefore 0)
        (states ()))
    ;; Transfer the data from `track-changes--before-string'
    ;; to the tracker's state object, if needed.
    (track-changes--clean-state)
    ;; We want to combine the states from most recent to oldest,
    ;; so reverse them.
    (let ((state (track-changes--tracker-state id)))
      (while state
        (push state states)
        (setq state (track-changes--state-next state))))

    (cond
     ((eq (car states) track-changes--state)
      (cl-assert (null (track-changes--state-before (car states))))
      (setq states (cdr states)))
     (t
      ;; The states are disconnected from the latest state because
      ;; we got out of sync!
      (cl-assert (eq (track-changes--state-before (car states)) 'error))
      (setq beg (point-min))
      (setq end (point-max))
      (setq before 'error)
      (setq states nil)))

    (dolist (state states)
      (when is-undo (setq is-undo (track-changes--state-undo state)))
      (let ((prevbeg (track-changes--state-beg state))
            (prevend (track-changes--state-end state))
            (prevbefore (track-changes--state-before state)))
        (if (eq before t)
            (progn
              ;; This is the most recent change.  Just initialize the vars.
              (setq beg prevbeg)
              (setq end prevend)
              (setq lenbefore
                    (if (stringp prevbefore) (length prevbefore) prevbefore))
              (setq before
                    (unless (track-changes--tracker-nobefore id) prevbefore)))
          (let ((endb (+ beg lenbefore)))
            (when (< prevbeg beg)
              (if (not before)
                  (setq lenbefore (+ (- beg prevbeg) lenbefore))
                (setq before
                      (concat (buffer-substring-no-properties
                               prevbeg beg)
                              before))
                (setq lenbefore (length before)))
              (setq beg prevbeg)
              (cl-assert (= endb (+ beg lenbefore))))
            (when (< endb prevend)
              (let ((new-end (+ end (- prevend endb))))
                (if (not before)
                    (setq lenbefore (+ lenbefore (- new-end end)))
                  (setq before
                        (concat before
                                (buffer-substring-no-properties
                                 end new-end)))
                  (setq lenbefore (length before)))
                (setq end new-end)
                (cl-assert (= prevend (+ beg lenbefore)))
                (setq endb (+ beg lenbefore))))
            (cl-assert (<= beg prevbeg prevend endb))
            ;; The `prevbefore' is covered by the new one.
            (if (not before)
                (setq lenbefore
                      (+ (- prevbeg beg)
                         (if (stringp prevbefore)
                             (length prevbefore) prevbefore)
                         (- endb prevend)))
              (setq before
                    (concat (substring before 0 (- prevbeg beg))
                            prevbefore
                            (substring before (- (length before)
                                                 (- endb prevend)))))
              (setq lenbefore (length before)))))))
    (unwind-protect
        (if (null beg)
            (progn
              (cl-assert (null states))
              ;; We may have been called in the middle of another
              ;; `track-changes-fetch', in which case we may be in a clean
              ;; state but not yet on `track-changes--clean-trackers'
              ;;(cl-assert (memq id track-changes--clean-trackers))
              (cl-assert (eq (track-changes--tracker-state id)
                             track-changes--state))
              ;; Nothing to do.
              nil)
          ;; ID may still be in `track-changes--clean-trackers' if
          ;; `after-change-functions' was skipped.
          ;;(cl-assert (not (memq id track-changes--clean-trackers)))
          (cl-assert (<= (point-min) beg end (point-max)))
          ;; Update the tracker's state *before* running `func' so we don't risk
          ;; mistakenly replaying the changes in case `func' exits non-locally.
          (setf (track-changes--tracker-state id) track-changes--state)
          (let ((track-changes-undo-only is-undo))
            (funcall func beg end (or before lenbefore))))
      ;; Re-enable the tracker's signal only after running `func', so
      ;; as to avoid nested invocations.
      (cl-pushnew id track-changes--clean-trackers))))