comparison lisp/replace.el @ 209:41ff10fd062f r20-4b3

Import from CVS: tag r20-4b3
author cvs
date Mon, 13 Aug 2007 10:04:58 +0200
parents
children 85a06df23a9a
comparison
equal deleted inserted replaced
208:f427b8ec4379 209:41ff10fd062f
1 ;;; replace.el --- search and replace commands for XEmacs.
2
3 ;; Copyright (C) 1985-7, 1992, 1994, 1997 Free Software Foundation, Inc.
4
5 ;; Maintainer: XEmacs Development Team
6 ;; Keywords: dumped, matching
7
8 ;; This file is part of XEmacs.
9
10 ;; XEmacs is free software; you can redistribute it and/or modify it
11 ;; under the terms of the GNU General Public License as published by
12 ;; the Free Software Foundation; either version 2, or (at your option)
13 ;; any later version.
14
15 ;; XEmacs is distributed in the hope that it will be useful, but
16 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 ;; General Public License for more details.
19
20 ;; You should have received a copy of the GNU General Public License
21 ;; along with XEmacs; see the file COPYING. If not, write to the Free
22 ;; Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
23 ;; 02111-1307, USA.
24
25 ;;; Synched up with: FSF 19.34 [Partially].
26
27 ;;; Commentary:
28
29 ;; This file is dumped with XEmacs.
30
31 ;; This package supplies the string and regular-expression replace functions
32 ;; documented in the XEmacs Reference Manual.
33
34 ;; All the gettext calls are for XEmacs I18N3 message catalog support.
35 ;; (This is hopelessly broken and we should remove it. -sb)
36
37 ;;; Code:
38
39 (defvar case-replace t "\
40 *Non-nil means `query-replace' should preserve case in replacements.
41 What this means is that `query-replace' will change the case of the
42 replacement text so that it matches the text that was replaced.
43 If this variable is nil, the replacement text will be inserted
44 exactly as it was specified by the user, irrespective of the case
45 of the text that was replaced.
46
47 Note that this flag has no effect if `case-fold-search' is nil,
48 or if the replacement text has any uppercase letters in it.")
49
50 (defvar query-replace-history nil)
51
52 (defvar query-replace-interactive nil
53 "Non-nil means `query-replace' uses the last search string.
54 That becomes the \"string to replace\".")
55
56 (defun query-replace-read-args (string regexp-flag)
57 (let (from to)
58 (if query-replace-interactive
59 (setq from (car (if regexp-flag regexp-search-ring search-ring)))
60 (setq from (read-from-minibuffer (format "%s: " (gettext string))
61 nil nil nil
62 'query-replace-history)))
63 (setq to (read-from-minibuffer (format "%s %s with: " (gettext string)
64 from)
65 nil nil nil
66 'query-replace-history))
67 (list from to current-prefix-arg)))
68
69 ;; As per suggestion from Per Abrahamsen, limit replacement to the region
70 ;; if the region is active.
71 (defun query-replace (from-string to-string &optional arg)
72 "Replace some occurrences of FROM-STRING with TO-STRING.
73 As each match is found, the user must type a character saying
74 what to do with it. For directions, type \\[help-command] at that time.
75
76 If `query-replace-interactive' is non-nil, the last incremental search
77 string is used as FROM-STRING--you don't have to specify it with the
78 minibuffer.
79
80 Preserves case in each replacement if `case-replace' and `case-fold-search'
81 are non-nil and FROM-STRING has no uppercase letters.
82 \(Preserving case means that if the string matched is all caps, or capitalized,
83 then its replacement is upcased or capitalized.)
84
85 Third arg DELIMITED (prefix arg if interactive), if non-nil, means replace
86 only matches surrounded by word boundaries.
87
88 To customize possible responses, change the \"bindings\" in `query-replace-map'."
89 (interactive (query-replace-read-args "Query replace" nil))
90 (if (or (and (boundp 'zmacs-region-active-p) zmacs-region-active-p)
91 (and (boundp 'transient-mark-mode) transient-mark-mode mark-active))
92 (save-restriction
93 (save-excursion
94 (narrow-to-region (point) (mark))
95 (goto-char (point-min))
96 (query-replace from-string to-string arg)))
97 (perform-replace from-string to-string t nil arg)))
98
99 (defun query-replace-regexp (regexp to-string &optional arg)
100 "Replace some things after point matching REGEXP with TO-STRING.
101 As each match is found, the user must type a character saying
102 what to do with it. For directions, type \\[help-command] at that time.
103
104 If `query-replace-interactive' is non-nil, the last incremental search
105 regexp is used as REGEXP--you don't have to specify it with the
106 minibuffer.
107
108 Preserves case in each replacement if `case-replace' and `case-fold-search'
109 are non-nil and REGEXP has no uppercase letters.
110 Third arg DELIMITED (prefix arg if interactive), if non-nil, means replace
111 only matches surrounded by word boundaries.
112 In TO-STRING, `\\&' stands for whatever matched the whole of REGEXP,
113 and `\\=\\N' (where N is a digit) stands for
114 whatever what matched the Nth `\\(...\\)' in REGEXP."
115 (interactive (query-replace-read-args "Query replace regexp" t))
116 (if (or (and (boundp 'zmacs-region-active-p) zmacs-region-active-p)
117 (and (boundp 'transient-mark-mode) transient-mark-mode mark-active))
118 (save-restriction
119 (save-excursion
120 (narrow-to-region (point) (mark))
121 (goto-char (point-min))
122 (perform-replace regexp to-string t t arg)))
123 (perform-replace regexp to-string t t arg)))
124
125 ;;#### Not patently useful
126 (defun map-query-replace-regexp (regexp to-strings &optional arg)
127 "Replace some matches for REGEXP with various strings, in rotation.
128 The second argument TO-STRINGS contains the replacement strings, separated
129 by spaces. This command works like `query-replace-regexp' except
130 that each successive replacement uses the next successive replacement string,
131 wrapping around from the last such string to the first.
132
133 Non-interactively, TO-STRINGS may be a list of replacement strings.
134
135 If `query-replace-interactive' is non-nil, the last incremental search
136 regexp is used as REGEXP--you don't have to specify it with the minibuffer.
137
138 A prefix argument N says to use each replacement string N times
139 before rotating to the next."
140 (interactive
141 (let (from to)
142 (setq from (if query-replace-interactive
143 (car regexp-search-ring)
144 (read-from-minibuffer "Map query replace (regexp): "
145 nil nil nil
146 'query-replace-history)))
147 (setq to (read-from-minibuffer
148 (format "Query replace %s with (space-separated strings): "
149 from)
150 nil nil nil
151 'query-replace-history))
152 (list from to current-prefix-arg)))
153 (let (replacements)
154 (if (listp to-strings)
155 (setq replacements to-strings)
156 (while (/= (length to-strings) 0)
157 (if (string-match " " to-strings)
158 (setq replacements
159 (append replacements
160 (list (substring to-strings 0
161 (string-match " " to-strings))))
162 to-strings (substring to-strings
163 (1+ (string-match " " to-strings))))
164 (setq replacements (append replacements (list to-strings))
165 to-strings ""))))
166 (perform-replace regexp replacements t t nil arg)))
167
168 (defun replace-string (from-string to-string &optional delimited)
169 "Replace occurrences of FROM-STRING with TO-STRING.
170 Preserve case in each match if `case-replace' and `case-fold-search'
171 are non-nil and FROM-STRING has no uppercase letters.
172 \(Preserving case means that if the string matched is all caps, or capitalized,
173 then its replacement is upcased or capitalized.)
174
175 Third arg DELIMITED (prefix arg if interactive), if non-nil, means replace
176 only matches surrounded by word boundaries.
177
178 If `query-replace-interactive' is non-nil, the last incremental search
179 string is used as FROM-STRING--you don't have to specify it with the
180 minibuffer.
181
182 This function is usually the wrong thing to use in a Lisp program.
183 What you probably want is a loop like this:
184 (while (search-forward FROM-STRING nil t)
185 (replace-match TO-STRING nil t))
186 which will run faster and will not set the mark or print anything."
187 (interactive (query-replace-read-args "Replace string" nil))
188 (perform-replace from-string to-string nil nil delimited))
189
190 (defun replace-regexp (regexp to-string &optional delimited)
191 "Replace things after point matching REGEXP with TO-STRING.
192 Preserve case in each match if `case-replace' and `case-fold-search'
193 are non-nil and REGEXP has no uppercase letters.
194 \(Preserving case means that if the string matched is all caps, or capitalized,
195 then its replacement is upcased or capitalized.)
196
197 Third arg DELIMITED (prefix arg if interactive), if non-nil, means replace
198 only matches surrounded by word boundaries.
199 In TO-STRING, `\\&' stands for whatever matched the whole of REGEXP,
200 and `\\=\\N' (where N is a digit) stands for
201 whatever what matched the Nth `\\(...\\)' in REGEXP.
202
203 If `query-replace-interactive' is non-nil, the last incremental search
204 regexp is used as REGEXP--you don't have to specify it with the minibuffer.
205
206 This function is usually the wrong thing to use in a Lisp program.
207 What you probably want is a loop like this:
208 (while (re-search-forward REGEXP nil t)
209 (replace-match TO-STRING nil nil))
210 which will run faster and will not set the mark or print anything."
211 (interactive (query-replace-read-args "Replace regexp" t))
212 (perform-replace regexp to-string nil t delimited))
213
214
215 (defvar regexp-history nil
216 "History list for some commands that read regular expressions.")
217
218 (define-function 'keep-lines 'delete-non-matching-lines)
219 (defun delete-non-matching-lines (regexp)
220 "Delete all lines except those containing matches for REGEXP.
221 A match split across lines preserves all the lines it lies in.
222 Applies to all lines after point."
223 (interactive (list (read-from-minibuffer
224 "Keep lines (containing match for regexp): "
225 nil nil nil 'regexp-history)))
226 (save-excursion
227 (or (bolp) (forward-line 1))
228 (let ((start (point)))
229 (while (not (eobp))
230 ;; Start is first char not preserved by previous match.
231 (if (not (re-search-forward regexp nil 'move))
232 (delete-region start (point-max))
233 (let ((end (save-excursion (goto-char (match-beginning 0))
234 (beginning-of-line)
235 (point))))
236 ;; Now end is first char preserved by the new match.
237 (if (< start end)
238 (delete-region start end))))
239 (setq start (save-excursion (forward-line 1)
240 (point)))
241 ;; If the match was empty, avoid matching again at same place.
242 (and (not (eobp)) (= (match-beginning 0) (match-end 0))
243 (forward-char 1))))))
244
245 (define-function 'flush-lines 'delete-matching-lines)
246 (defun delete-matching-lines (regexp)
247 "Delete lines containing matches for REGEXP.
248 If a match is split across lines, all the lines it lies in are deleted.
249 Applies to lines after point."
250 (interactive (list (read-from-minibuffer
251 "Flush lines (containing match for regexp): "
252 nil nil nil 'regexp-history)))
253 (save-excursion
254 (while (and (not (eobp))
255 (re-search-forward regexp nil t))
256 (delete-region (save-excursion (goto-char (match-beginning 0))
257 (beginning-of-line)
258 (point))
259 (progn (forward-line 1) (point))))))
260
261 (define-function 'how-many 'count-matches)
262 (defun count-matches (regexp)
263 "Print number of matches for REGEXP following point."
264 (interactive (list (read-from-minibuffer
265 "How many matches for (regexp): "
266 nil nil nil 'regexp-history)))
267 (let ((count 0) opoint)
268 (save-excursion
269 (while (and (not (eobp))
270 (progn (setq opoint (point))
271 (re-search-forward regexp nil t)))
272 (if (= opoint (point))
273 (forward-char 1)
274 (setq count (1+ count))))
275 (message "%d occurrences" count))))
276
277
278 (defvar occur-mode-map ())
279 (if occur-mode-map
280 ()
281 (setq occur-mode-map (make-sparse-keymap))
282 (set-keymap-name occur-mode-map 'occur-mode-map) ; XEmacs
283 (define-key occur-mode-map 'button2 'occur-mode-mouse-goto) ; XEmacs
284 (define-key occur-mode-map "\C-c\C-c" 'occur-mode-goto-occurrence)
285 (define-key occur-mode-map "\C-m" 'occur-mode-goto-occurrence))
286
287 (defvar occur-buffer nil)
288 (defvar occur-nlines nil)
289 (defvar occur-pos-list nil)
290
291 (defun occur-mode ()
292 "Major mode for output from \\[occur].
293 \\<occur-mode-map>Move point to one of the items in this buffer, then use
294 \\[occur-mode-goto-occurrence] to go to the occurrence that the item refers to.
295 Alternatively, click \\[occur-mode-mouse-goto] on an item to go to it.
296
297 \\{occur-mode-map}"
298 (kill-all-local-variables)
299 (use-local-map occur-mode-map)
300 (setq major-mode 'occur-mode)
301 (setq mode-name (gettext "Occur")) ; XEmacs
302 (make-local-variable 'occur-buffer)
303 (make-local-variable 'occur-nlines)
304 (make-local-variable 'occur-pos-list)
305 (require 'mode-motion) ; XEmacs
306 (setq mode-motion-hook 'mode-motion-highlight-line) ; XEmacs
307 (run-hooks 'occur-mode-hook))
308
309 ;; FSF Version of next function:
310 ; (let (buffer pos)
311 ; (save-excursion
312 ; (set-buffer (window-buffer (posn-window (event-end event))))
313 ; (save-excursion
314 ; (goto-char (posn-point (event-end event)))
315 ; (setq pos (occur-mode-find-occurrence))
316 ; (setq buffer occur-buffer)))
317 ; (pop-to-buffer buffer)
318 ; (goto-char (marker-position pos))))
319
320 (defun occur-mode-mouse-goto (event)
321 "Go to the occurrence highlighted by mouse.
322 This function is only reasonable when bound to a mouse key in the occur buffer"
323 (interactive "e")
324 (let ((window-save (selected-window))
325 (frame-save (selected-frame)))
326 ;; preserve the window/frame setup
327 (unwind-protect
328 (progn
329 (mouse-set-point event)
330 (occur-mode-goto-occurrence))
331 (select-frame frame-save)
332 (select-window window-save))))
333
334 ;; Called occur-mode-find-occurrence in FSF
335 (defun occur-mode-goto-occurrence ()
336 "Go to the occurrence the current line describes."
337 (interactive)
338 (if (or (null occur-buffer)
339 (null (buffer-name occur-buffer)))
340 (progn
341 (setq occur-buffer nil
342 occur-pos-list nil)
343 (error "Buffer in which occurrences were found is deleted")))
344 (let* ((line-count
345 (count-lines (point-min)
346 (save-excursion
347 (beginning-of-line)
348 (point))))
349 (occur-number (save-excursion
350 (beginning-of-line)
351 (/ (1- line-count)
352 (cond ((< occur-nlines 0)
353 (- 2 occur-nlines))
354 ((> occur-nlines 0)
355 (+ 2 (* 2 occur-nlines)))
356 (t 1)))))
357 (pos (nth occur-number occur-pos-list))
358 ;; removed t arg from Bob Weiner, 10/6/95
359 (window (get-buffer-window occur-buffer))
360 (occur-source-buffer occur-buffer))
361 (if (< line-count 1)
362 (error "No occurrence on this line"))
363 (or pos
364 (error "No occurrence on this line"))
365 ;; XEmacs: don't raise window unless it isn't visible
366 ;; allow for the possibility that the occur buffer is on another frame
367 (or (and window
368 (window-live-p window)
369 (frame-visible-p (window-frame window))
370 (set-buffer occur-source-buffer))
371 (and (pop-to-buffer occur-source-buffer)
372 (setq window (get-buffer-window occur-source-buffer))))
373 (goto-char pos)
374 (set-window-point window pos)))
375
376
377 (defvar list-matching-lines-default-context-lines 0
378 "*Default number of context lines to include around a `list-matching-lines'
379 match. A negative number means to include that many lines before the match.
380 A positive number means to include that many lines both before and after.")
381
382 ;; XEmacs addition
383 ;;; Damn you Jamie, this is utter trash.
384 (defvar list-matching-lines-whole-buffer t
385 "If t, occur operates on whole buffer, otherwise occur starts from point.
386 default is t.")
387
388 (define-function 'occur 'list-matching-lines)
389 (defun list-matching-lines (regexp &optional nlines)
390 "Show all lines in the current buffer containing a match for REGEXP.
391
392 If a match spreads across multiple lines, all those lines are shown.
393
394 If variable `list-matching-lines-whole-buffer' is non-nil, the entire buffer is
395 searched, otherwise search begins at point.
396
397 Each line is displayed with NLINES lines before and after, or -NLINES
398 before if NLINES is negative.
399 NLINES defaults to `list-matching-lines-default-context-lines'.
400 Interactively it is the prefix arg.
401
402 The lines are shown in a buffer named `*Occur*'.
403 It serves as a menu to find any of the occurrences in this buffer.
404 \\[describe-mode] in that buffer will explain how."
405 (interactive
406 ;; XEmacs change
407 (list (let* ((default (or (symbol-near-point)
408 (and regexp-history
409 (car regexp-history))))
410 (minibuffer-history-minimum-string-length 0)
411 (input
412 (if default
413 ;; rewritten for I18N3 snarfing
414 (read-from-minibuffer
415 (format "List lines matching regexp (default `%s'): "
416 default) nil nil nil 'regexp-history)
417 (read-from-minibuffer
418 "List lines matching regexp: "
419 nil nil nil
420 'regexp-history))))
421 (if (and (equal input "") default)
422 (progn
423 (setq input default)
424 (setcar regexp-history default)))
425 ;; clear extra entries
426 (setcdr regexp-history (delete (car regexp-history)
427 (cdr regexp-history)))
428 input)
429 current-prefix-arg))
430 (if (equal regexp "")
431 (error "Must pass non-empty regexp to `list-matching-lines'"))
432 (setq nlines (if nlines (prefix-numeric-value nlines)
433 list-matching-lines-default-context-lines))
434 (let ((first t)
435 (dir default-directory)
436 (buffer (current-buffer))
437 (linenum 1)
438 (prevpos (point-min))
439 ;; The rest of this function is very different from FSF.
440 ;; Presumably that's due to Jamie's misfeature
441 (final-context-start (make-marker)))
442 (if (not list-matching-lines-whole-buffer)
443 (save-excursion
444 (beginning-of-line)
445 (setq linenum (1+ (count-lines (point-min) (point))))
446 (setq prevpos (point))))
447 (with-output-to-temp-buffer "*Occur*"
448 (save-excursion
449 (set-buffer standard-output)
450 (setq default-directory dir)
451 ;; We will insert the number of lines, and "lines", later.
452 ;; #### Needs fixing for I18N3
453 (let ((print-escape-newlines t))
454 (insert (format " matching %s in buffer %s.\n"
455 regexp (buffer-name buffer))))
456 (occur-mode)
457 (setq occur-buffer buffer)
458 (setq occur-nlines nlines)
459 (setq occur-pos-list ()))
460 (if (eq buffer standard-output)
461 (goto-char (point-max)))
462 (save-excursion
463 (if list-matching-lines-whole-buffer
464 (beginning-of-buffer))
465 (message "Searching for %s ..." regexp)
466 ;; Find next match, but give up if prev match was at end of buffer.
467 (while (and (not (= prevpos (point-max)))
468 (re-search-forward regexp nil t))
469 (goto-char (match-beginning 0))
470 (beginning-of-line)
471 (save-match-data
472 (setq linenum (+ linenum (count-lines prevpos (point)))))
473 (setq prevpos (point))
474 (goto-char (match-end 0))
475 (let* ((start (save-excursion
476 (goto-char (match-beginning 0))
477 (forward-line (if (< nlines 0) nlines (- nlines)))
478 (point)))
479 (end (save-excursion
480 (goto-char (match-end 0))
481 (if (> nlines 0)
482 (forward-line (1+ nlines))
483 (forward-line 1))
484 (point)))
485 (tag (format "%5d" linenum))
486 (empty (make-string (length tag) ?\ ))
487 tem)
488 (save-excursion
489 (setq tem (make-marker))
490 (set-marker tem (point))
491 (set-buffer standard-output)
492 (setq occur-pos-list (cons tem occur-pos-list))
493 (or first (zerop nlines)
494 (insert "--------\n"))
495 (setq first nil)
496 (insert-buffer-substring buffer start end)
497 (set-marker final-context-start
498 (- (point) (- end (match-end 0))))
499 (backward-char (- end start))
500 (setq tem (if (< nlines 0) (- nlines) nlines))
501 (while (> tem 0)
502 (insert empty ?:)
503 (forward-line 1)
504 (setq tem (1- tem)))
505 (let ((this-linenum linenum))
506 (while (< (point) final-context-start)
507 (if (null tag)
508 (setq tag (format "%5d" this-linenum)))
509 (insert tag ?:)
510 ;; FSFmacs -- we handle this using mode-motion-highlight-line, above.
511 ; (put-text-property (save-excursion
512 ; (beginning-of-line)
513 ; (point))
514 ; (save-excursion
515 ; (end-of-line)
516 ; (point))
517 ; 'mouse-face 'highlight)
518 (forward-line 1)
519 (setq tag nil)
520 (setq this-linenum (1+ this-linenum)))
521 (while (<= (point) final-context-start)
522 (insert empty ?:)
523 (forward-line 1)
524 (setq this-linenum (1+ this-linenum))))
525 (while (< tem nlines)
526 (insert empty ?:)
527 (forward-line 1)
528 (setq tem (1+ tem)))
529 (goto-char (point-max)))
530 (forward-line 1)))
531 (set-buffer standard-output)
532 ;; Put positions in increasing order to go with buffer.
533 (setq occur-pos-list (nreverse occur-pos-list))
534 (goto-char (point-min))
535 (if (= (length occur-pos-list) 1)
536 (insert "1 line")
537 (insert (format "%d lines" (length occur-pos-list))))
538 (if (interactive-p)
539 (message "%d matching lines." (length occur-pos-list)))))))
540
541 ;; It would be nice to use \\[...], but there is no reasonable way
542 ;; to make that display both SPC and Y.
543 (defconst query-replace-help
544 (purecopy
545 "Type Space or `y' to replace one match, Delete or `n' to skip to next,
546 RET or `q' to exit, Period to replace one match and exit,
547 Comma to replace but not move point immediately,
548 C-r to enter recursive edit (\\[exit-recursive-edit] to get out again),
549 C-w to delete match and recursive edit,
550 C-l to clear the frame, redisplay, and offer same replacement again,
551 ! to replace all remaining matches with no more questions,
552 ^ to move point back to previous match."
553 )
554 "Help message while in query-replace")
555
556 (defvar query-replace-map nil
557 "Keymap that defines the responses to questions in `query-replace'.
558 The \"bindings\" in this map are not commands; they are answers.
559 The valid answers include `act', `skip', `act-and-show',
560 `exit', `act-and-exit', `edit', `delete-and-edit', `recenter',
561 `automatic', `backup', `exit-prefix', and `help'.")
562
563 ;; Why does it seem that ever file has a different method of doing this?
564 (if query-replace-map
565 nil
566 (let ((map (make-sparse-keymap)))
567 (set-keymap-name map 'query-replace-map)
568 (define-key map " " 'act)
569 (define-key map "\d" 'skip)
570 (define-key map [delete] 'skip)
571 (define-key map [backspace] 'skip)
572 (define-key map "y" 'act)
573 (define-key map "n" 'skip)
574 (define-key map "Y" 'act)
575 (define-key map "N" 'skip)
576 (define-key map "," 'act-and-show)
577 (define-key map [escape] 'exit)
578 (define-key map "q" 'exit)
579 (define-key map [return] 'exit)
580 (define-key map "." 'act-and-exit)
581 (define-key map "\C-r" 'edit)
582 (define-key map "\C-w" 'delete-and-edit)
583 (define-key map "\C-l" 'recenter)
584 (define-key map "!" 'automatic)
585 (define-key map "^" 'backup)
586 (define-key map [(control h)] 'help) ;; XEmacs change
587 (define-key map [f1] 'help)
588 (define-key map [help] 'help)
589 (define-key map "?" 'help)
590 (define-key map "\C-g" 'quit)
591 (define-key map "\C-]" 'quit)
592 ;FSFmacs (define-key map "\e" 'exit-prefix)
593 (define-key map [escape] 'exit-prefix)
594
595 (setq query-replace-map map)))
596
597
598 (autoload 'isearch-highlight "isearch")
599
600 ;; XEmacs
601 (defun perform-replace-next-event (event)
602 (if isearch-highlight
603 (let ((aborted t))
604 (unwind-protect
605 (progn
606 (if (match-beginning 0)
607 (isearch-highlight (match-beginning 0) (match-end 0)))
608 (next-command-event event)
609 (setq aborted nil))
610 (isearch-dehighlight aborted)))
611 (next-command-event event)))
612
613 (defun perform-replace (from-string replacements
614 query-flag regexp-flag delimited-flag
615 &optional repeat-count map)
616 "Subroutine of `query-replace'. Its complexity handles interactive queries.
617 Don't use this in your own program unless you want to query and set the mark
618 just as `query-replace' does. Instead, write a simple loop like this:
619 (while (re-search-forward \"foo[ \t]+bar\" nil t)
620 (replace-match \"foobar\" nil nil))
621 which will run faster and probably do exactly what you want."
622 (or map (setq map query-replace-map))
623 (let* ((event (make-event))
624 (nocasify (not (and case-fold-search case-replace
625 (string-equal from-string
626 (downcase from-string)))))
627 (literal (not regexp-flag))
628 (search-function (if regexp-flag 're-search-forward 'search-forward))
629 (search-string from-string)
630 (real-match-data nil) ; the match data for the current match
631 (next-replacement nil)
632 (replacement-index 0)
633 (keep-going t)
634 (stack nil)
635 (next-rotate-count 0)
636 (replace-count 0)
637 (lastrepl nil) ;Position after last match considered.
638 (match-again t)
639 ;; XEmacs addition
640 (qr-case-fold-search
641 (if (and case-fold-search search-caps-disable-folding)
642 (isearch-no-upper-case-p search-string)
643 case-fold-search))
644 (message
645 (if query-flag
646 (substitute-command-keys
647 "Query replacing %s with %s: (\\<query-replace-map>\\[help] for help) "))))
648 (if (stringp replacements)
649 (setq next-replacement replacements)
650 (or repeat-count (setq repeat-count 1)))
651 (if delimited-flag
652 (setq search-function 're-search-forward
653 search-string (concat "\\b"
654 (if regexp-flag from-string
655 (regexp-quote from-string))
656 "\\b")))
657 (push-mark)
658 (undo-boundary)
659 (unwind-protect
660 ;; Loop finding occurrences that perhaps should be replaced.
661 (while (and keep-going
662 (not (eobp))
663 (let ((case-fold-search qr-case-fold-search))
664 (funcall search-function search-string nil t))
665 ;; If the search string matches immediately after
666 ;; the previous match, but it did not match there
667 ;; before the replacement was done, ignore the match.
668 (if (or (eq lastrepl (point))
669 (and regexp-flag
670 (eq lastrepl (match-beginning 0))
671 (not match-again)))
672 (if (eobp)
673 nil
674 ;; Don't replace the null string
675 ;; right after end of previous replacement.
676 (forward-char 1)
677 (let ((case-fold-search qr-case-fold-search))
678 (funcall search-function search-string nil t)))
679 t))
680
681 ;; Save the data associated with the real match.
682 (setq real-match-data (match-data))
683
684 ;; Before we make the replacement, decide whether the search string
685 ;; can match again just after this match.
686 (if regexp-flag
687 (progn
688 (setq match-again (looking-at search-string))
689 ;; XEmacs addition
690 (store-match-data real-match-data)))
691 ;; If time for a change, advance to next replacement string.
692 (if (and (listp replacements)
693 (= next-rotate-count replace-count))
694 (progn
695 (setq next-rotate-count
696 (+ next-rotate-count repeat-count))
697 (setq next-replacement (nth replacement-index replacements))
698 (setq replacement-index (% (1+ replacement-index) (length replacements)))))
699 (if (not query-flag)
700 (progn
701 (store-match-data real-match-data)
702 (replace-match next-replacement nocasify literal)
703 (setq replace-count (1+ replace-count)))
704 (undo-boundary)
705 (let ((help-form
706 '(concat (format "Query replacing %s%s with %s.\n\n"
707 (if regexp-flag (gettext "regexp ") "")
708 from-string next-replacement)
709 (substitute-command-keys query-replace-help)))
710 done replaced def)
711 ;; Loop reading commands until one of them sets done,
712 ;; which means it has finished handling this occurrence.
713 (while (not done)
714 ;; Don't fill up the message log
715 ;; with a bunch of identical messages.
716 ;; XEmacs change
717 (display-message 'prompt
718 (format message from-string next-replacement))
719 (perform-replace-next-event event)
720 (setq def (lookup-key map (vector event)))
721 ;; Restore the match data while we process the command.
722 (store-match-data real-match-data)
723 (cond ((eq def 'help)
724 (with-output-to-temp-buffer (gettext "*Help*")
725 (princ (concat
726 (format "Query replacing %s%s with %s.\n\n"
727 (if regexp-flag "regexp " "")
728 from-string next-replacement)
729 (substitute-command-keys
730 query-replace-help)))
731 (save-excursion
732 (set-buffer standard-output)
733 (help-mode))))
734 ((eq def 'exit)
735 (setq keep-going nil)
736 (setq done t))
737 ((eq def 'backup)
738 (if stack
739 (let ((elt (car stack)))
740 (goto-char (car elt))
741 (setq replaced (eq t (cdr elt)))
742 (or replaced
743 (store-match-data (cdr elt)))
744 (setq stack (cdr stack)))
745 (message "No previous match")
746 (ding 'no-terminate)
747 (sit-for 1)))
748 ((eq def 'act)
749 (or replaced
750 (replace-match next-replacement nocasify literal))
751 (setq done t replaced t))
752 ((eq def 'act-and-exit)
753 (or replaced
754 (replace-match next-replacement nocasify literal))
755 (setq keep-going nil)
756 (setq done t replaced t))
757 ((eq def 'act-and-show)
758 (if (not replaced)
759 (progn
760 (replace-match next-replacement nocasify literal)
761 (store-match-data nil)
762 (setq replaced t))))
763 ((eq def 'automatic)
764 (or replaced
765 (replace-match next-replacement nocasify literal))
766 (setq done t query-flag nil replaced t))
767 ((eq def 'skip)
768 (setq done t))
769 ((eq def 'recenter)
770 (recenter nil))
771 ((eq def 'edit)
772 (store-match-data
773 (prog1 (match-data)
774 (save-excursion (recursive-edit))))
775 ;; Before we make the replacement,
776 ;; decide whether the search string
777 ;; can match again just after this match.
778 (if regexp-flag
779 (setq match-again (looking-at search-string))))
780 ((eq def 'delete-and-edit)
781 (delete-region (match-beginning 0) (match-end 0))
782 (store-match-data (prog1 (match-data)
783 (save-excursion (recursive-edit))))
784 (setq replaced t))
785 ;; Note: we do not need to treat `exit-prefix'
786 ;; specially here, since we reread
787 ;; any unrecognized character.
788 (t
789 (setq this-command 'mode-exited)
790 (setq keep-going nil)
791 (setq unread-command-events
792 (cons event unread-command-events))
793 (setq done t))))
794 ;; Record previous position for ^ when we move on.
795 ;; Change markers to numbers in the match data
796 ;; since lots of markers slow down editing.
797 (setq stack
798 (cons (cons (point)
799 (or replaced
800 (mapcar
801 #'(lambda (elt)
802 (if (markerp elt)
803 (prog1 (marker-position elt)
804 (set-marker elt nil))
805 elt))
806 (match-data))))
807 stack))
808 (if replaced (setq replace-count (1+ replace-count)))))
809 (setq lastrepl (point)))
810 (replace-dehighlight))
811 (or unread-command-events
812 (message "Replaced %d occurrence%s"
813 replace-count
814 (if (= replace-count 1) "" "s")))
815 (and keep-going stack)))
816
817 (defvar query-replace-highlight nil
818 "*Non-nil means to highlight words during query replacement.")
819
820 (defvar replace-overlay nil)
821
822 (defun replace-dehighlight ()
823 (and replace-overlay
824 (progn
825 (delete-overlay replace-overlay)
826 (setq replace-overlay nil))))
827
828 (defun replace-highlight (start end)
829 (and query-replace-highlight
830 (progn
831 (or replace-overlay
832 (progn
833 (setq replace-overlay (make-overlay start end))
834 (overlay-put replace-overlay 'face
835 (if (internal-find-face 'query-replace)
836 'query-replace 'region))))
837 (move-overlay replace-overlay start end (current-buffer)))))
838
839 (defun match-string (num &optional string)
840 "Return string of text matched by last search.
841 NUM specifies which parenthesized expression in the last regexp.
842 Value is nil if NUMth pair didn't match, or there were less than NUM pairs.
843 Zero means the entire text matched by the whole regexp or whole string.
844 STRING should be given if the last search was by `string-match' on STRING."
845 (if (match-beginning num)
846 (if string
847 (substring string (match-beginning num) (match-end num))
848 (buffer-substring (match-beginning num) (match-end num)))))
849
850 (defmacro save-match-data (&rest body)
851 "Execute BODY forms, restoring the global value of the match data."
852 (let ((original (make-symbol "match-data")))
853 (list 'let (list (list original '(match-data)))
854 (list 'unwind-protect
855 (cons 'progn body)
856 (list 'store-match-data original)))))
857
858 ;;; replace.el ends here