- (while (and keep-going
- (not (eobp))
- (funcall search-function search-string nil t)
- ;; If the search string matches immediately after
- ;; the previous match, but it did not match there
- ;; before the replacement was done, ignore the match.
- (if (or (eq lastrepl (point))
- (and regexp-flag
- (eq lastrepl (match-beginning 0))
- (not match-again)))
- (if (eobp)
- nil
- ;; Don't replace the null string
- ;; right after end of previous replacement.
- (forward-char 1)
- (funcall search-function search-string nil t))
- t))
-
- ;; Save the data associated with the real match.
- (setq real-match-data (match-data))
-
- ;; Before we make the replacement, decide whether the search string
- ;; can match again just after this match.
- (if regexp-flag
- (setq match-again (looking-at search-string)))
- ;; If time for a change, advance to next replacement string.
- (if (and (listp replacements)
- (= next-rotate-count replace-count))
- (progn
- (setq next-rotate-count
- (+ next-rotate-count repeat-count))
- (setq next-replacement (nth replacement-index replacements))
- (setq replacement-index (% (1+ replacement-index) (length replacements)))))
- (if (not query-flag)
- (progn
- (store-match-data real-match-data)
- (replace-match next-replacement nocasify literal)
- (setq replace-count (1+ replace-count)))
- (undo-boundary)
- (let (done replaced)
- (while (not done)
- (let ((help-form
- '(concat "Query replacing "
- (if regexp-flag "regexp " "")
- from-string " with " next-replacement ".\n\n"
- (substitute-command-keys query-replace-help))))
- (setq char help-char)
- (while (or (not (numberp char)) (= char help-char))
- (message "Query replacing %s with %s: " from-string next-replacement)
- (setq char (read-event))
- (if (and (numberp char) (= char ??))
- (setq unread-command-char help-char char help-char))))
- ;; Restore the match data while we process the command.
- (store-match-data real-match-data)
- (cond ((or (= char ?\e)
- (= char ?q))
- (setq keep-going nil)
- (setq done t))
- ((= char ?^)
- (let ((elt (car stack)))
- (goto-char (car elt))
- (setq replaced (eq t (cdr elt)))
- (or replaced
- (store-match-data (cdr elt)))
- (setq stack (cdr stack))))
- ((or (= char ?\ )
- (= char ?y))
- (or replaced
- (replace-match next-replacement nocasify literal))
- (setq done t replaced t))
- ((= char ?\.)
- (or replaced
- (replace-match next-replacement nocasify literal))
- (setq keep-going nil)
- (setq done t replaced t))
- ((= char ?\,)
- (if (not replaced)
- (progn
- (replace-match next-replacement nocasify literal)
- (setq replaced t))))
- ((= char ?!)
- (or replaced
- (replace-match next-replacement nocasify literal))
- (setq done t query-flag nil replaced t))
- ((or (= char ?\177)
- (= char ?n))
- (setq done t))
- ((= char ?\C-l)
- (recenter nil))
- ((= char ?\C-r)
- (store-match-data
- (prog1 (match-data)
- (save-excursion (recursive-edit))))
- ;; Before we make the replacement,
- ;; decide whether the search string
- ;; can match again just after this match.
- (if regexp-flag
- (setq match-again (looking-at search-string))))
- ((= char ?\C-w)
- (delete-region (match-beginning 0) (match-end 0))
- (store-match-data
- (prog1 (match-data)
- (save-excursion (recursive-edit))))
- (setq replaced t))
- (t
- (setq keep-going nil)
- (setq unread-command-char char)
- (setq done t))))
- ;; Record previous position for ^ when we move on.
- ;; Change markers to numbers in the match data
- ;; since lots of markers slow down editing.
- (setq stack
- (cons (cons (point)
- (or replaced
- (mapcar
- (function (lambda (elt)
- (and elt
- (marker-position elt))))
- (match-data))))
- stack))
- (if replaced (setq replace-count (1+ replace-count)))))
- (setq lastrepl (point)))
+ (unwind-protect
+ ;; Loop finding occurrences that perhaps should be replaced.
+ (while (and keep-going
+ (not (eobp))
+ ;; Use the next match if it is already known;
+ ;; otherwise, search for a match after moving forward
+ ;; one char if progress is required.
+ (setq real-match-data
+ (if (consp match-again)
+ (progn (goto-char (nth 1 match-again))
+ match-again)
+ (and (or match-again
+ (progn
+ (forward-char 1)
+ (not (eobp))))
+ (funcall search-function search-string limit t)
+ ;; For speed, use only integers and
+ ;; reuse the list used last time.
+ (match-data t real-match-data)))))
+
+ ;; Record whether the match is nonempty, to avoid an infinite loop
+ ;; repeatedly matching the same empty string.
+ (setq nonempty-match
+ (/= (nth 0 real-match-data) (nth 1 real-match-data)))
+
+ ;; If the match is empty, record that the next one can't be adjacent.
+ ;; Otherwise, if matching a regular expression, do the next
+ ;; match now, since the replacement for this match may
+ ;; affect whether the next match is adjacent to this one.
+ (setq match-again
+ (and nonempty-match
+ (or (not regexp-flag)
+ (and (looking-at search-string)
+ (match-data)))))
+
+ ;; If time for a change, advance to next replacement string.
+ (if (and (listp replacements)
+ (= next-rotate-count replace-count))
+ (progn
+ (setq next-rotate-count
+ (+ next-rotate-count repeat-count))
+ (setq next-replacement (nth replacement-index replacements))
+ (setq replacement-index (% (1+ replacement-index) (length replacements)))))
+ (if (not query-flag)
+ (progn
+ (set-match-data real-match-data)
+ (replace-match next-replacement nocasify literal)
+ (setq replace-count (1+ replace-count)))
+ (undo-boundary)
+ (let (done replaced key def)
+ ;; Loop reading commands until one of them sets done,
+ ;; which means it has finished handling this occurrence.
+ (while (not done)
+ (set-match-data real-match-data)
+ (replace-highlight (match-beginning 0) (match-end 0))
+ ;; Bind message-log-max so we don't fill up the message log
+ ;; with a bunch of identical messages.
+ (let ((message-log-max nil))
+ (message message from-string next-replacement))
+ (setq key (read-event))
+ ;; Necessary in case something happens during read-event
+ ;; that clobbers the match data.
+ (set-match-data real-match-data)
+ (setq key (vector key))
+ (setq def (lookup-key map key))
+ ;; Restore the match data while we process the command.
+ (cond ((eq def 'help)
+ (with-output-to-temp-buffer "*Help*"
+ (princ
+ (concat "Query replacing "
+ (if regexp-flag "regexp " "")
+ from-string " with "
+ next-replacement ".\n\n"
+ (substitute-command-keys
+ query-replace-help)))
+ (save-excursion
+ (set-buffer standard-output)
+ (help-mode))))
+ ((eq def 'exit)
+ (setq keep-going nil)
+ (setq done t))
+ ((eq def 'backup)
+ (if stack
+ (let ((elt (car stack)))
+ (goto-char (car elt))
+ (setq replaced (eq t (cdr elt)))
+ (or replaced
+ (set-match-data (cdr elt)))
+ (setq stack (cdr stack)))
+ (message "No previous match")
+ (ding 'no-terminate)
+ (sit-for 1)))
+ ((eq def 'act)
+ (or replaced
+ (progn
+ (replace-match next-replacement nocasify literal)
+ (setq replace-count (1+ replace-count))))
+ (setq done t replaced t))
+ ((eq def 'act-and-exit)
+ (or replaced
+ (progn
+ (replace-match next-replacement nocasify literal)
+ (setq replace-count (1+ replace-count))))
+ (setq keep-going nil)
+ (setq done t replaced t))
+ ((eq def 'act-and-show)
+ (if (not replaced)
+ (progn
+ (replace-match next-replacement nocasify literal)
+ (setq replace-count (1+ replace-count))
+ (setq replaced t))))
+ ((eq def 'automatic)
+ (or replaced
+ (progn
+ (replace-match next-replacement nocasify literal)
+ (setq replace-count (1+ replace-count))))
+ (setq done t query-flag nil replaced t))
+ ((eq def 'skip)
+ (setq done t))
+ ((eq def 'recenter)
+ (recenter nil))
+ ((eq def 'edit)
+ (goto-char (match-beginning 0))
+ (funcall search-function search-string limit t)
+ (setq real-match-data (match-data))
+ (save-excursion (recursive-edit))
+ (set-match-data real-match-data)
+ ;; Before we make the replacement,
+ ;; decide whether the search string
+ ;; can match again just after this match.
+ (if (and regexp-flag nonempty-match)
+ (setq match-again (and (looking-at search-string)
+ (match-data)))))
+ ((eq def 'delete-and-edit)
+ (delete-region (match-beginning 0) (match-end 0))
+ (set-match-data
+ (prog1 (match-data)
+ (save-excursion (recursive-edit))))
+ (setq replaced t))
+ ;; Note: we do not need to treat `exit-prefix'
+ ;; specially here, since we reread
+ ;; any unrecognized character.
+ (t
+ (setq this-command 'mode-exited)
+ (setq keep-going nil)
+ (setq unread-command-events
+ (append (listify-key-sequence key)
+ unread-command-events))
+ (setq done t))))
+ ;; Record previous position for ^ when we move on.
+ ;; Change markers to numbers in the match data
+ ;; since lots of markers slow down editing.
+ (setq stack
+ (cons (cons (point)
+ (or replaced (match-data t)))
+ stack)))))
+ (replace-dehighlight))
+ (or unread-command-events
+ (message "Replaced %d occurrence%s"
+ replace-count
+ (if (= replace-count 1) "" "s")))