Remove unused file.
[bpt/emacs.git] / lisp / replace.el
CommitLineData
60370d40 1;;; replace.el --- replace commands for Emacs
c88ab9ce 2
653479ad 3;; Copyright (C) 1985, 86, 87, 92, 94, 96, 1997, 2000, 2001, 2002
8f3ff96b 4;; Free Software Foundation, Inc.
3a801d0c 5
698e1804
RS
6;; This file is part of GNU Emacs.
7
8;; GNU Emacs is free software; you can redistribute it and/or modify
9;; it under the terms of the GNU General Public License as published by
e5d77022 10;; the Free Software Foundation; either version 2, or (at your option)
698e1804
RS
11;; any later version.
12
13;; GNU Emacs is distributed in the hope that it will be useful,
14;; but WITHOUT ANY WARRANTY; without even the implied warranty of
15;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16;; GNU General Public License for more details.
17
18;; You should have received a copy of the GNU General Public License
b578f267
EN
19;; along with GNU Emacs; see the file COPYING. If not, write to the
20;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21;; Boston, MA 02111-1307, USA.
698e1804 22
d9ecc911
ER
23;;; Commentary:
24
25;; This package supplies the string and regular-expression replace functions
26;; documented in the Emacs user's manual.
27
4f4b8eff 28;;; Code:
698e1804 29
68608d9c
CW
30(eval-when-compile
31 (require 'cl))
32
9d325ebf 33(defcustom case-replace t
f54701d1 34 "*Non-nil means `query-replace' should preserve case in replacements."
9d325ebf
RS
35 :type 'boolean
36 :group 'matching)
77176e73 37
770970cb
RS
38(defvar query-replace-history nil)
39
151270f3
RS
40(defvar query-replace-interactive nil
41 "Non-nil means `query-replace' uses the last search string.
42That becomes the \"string to replace\".")
43
bdb1c08f 44(defcustom query-replace-from-history-variable 'query-replace-history
f54701d1 45 "History list to use for the FROM argument of `query-replace' commands.
bdb1c08f
RS
46The value of this variable should be a symbol; that symbol
47is used as a variable to hold a history list for the strings
48or patterns to be replaced."
49 :group 'matching
cd32a7ba
DN
50 :type 'symbol
51 :version "20.3")
bdb1c08f
RS
52
53(defcustom query-replace-to-history-variable 'query-replace-history
f54701d1 54 "History list to use for the TO argument of `query-replace' commands.
bdb1c08f
RS
55The value of this variable should be a symbol; that symbol
56is used as a variable to hold a history list for replacement
57strings or patterns."
58 :group 'matching
cd32a7ba
DN
59 :type 'symbol
60 :version "20.3")
bdb1c08f 61
1c4fe319
RS
62(defcustom query-replace-skip-read-only nil
63 "*Non-nil means `query-replace' and friends ignore read-only matches."
64 :type 'boolean
65 :group 'matching
66 :version "21.3")
67
86914dcc
RS
68(defun query-replace-read-args (string regexp-flag &optional noerror)
69 (unless noerror
70 (barf-if-buffer-read-only))
770970cb 71 (let (from to)
151270f3
RS
72 (if query-replace-interactive
73 (setq from (car (if regexp-flag regexp-search-ring search-ring)))
74 (setq from (read-from-minibuffer (format "%s: " string)
75 nil nil nil
bdb1c08f 76 query-replace-from-history-variable
d9291fa3
RS
77 nil t))
78 ;; Warn if user types \n or \t, but don't reject the input.
79 (if (string-match "\\\\[nt]" from)
80 (let ((match (match-string 0 from)))
81 (cond
82 ((string= match "\\n")
83 (message "Note: `\\n' here doesn't match a newline; to do that, type C-q C-j instead"))
84 ((string= match "\\t")
85 (message "Note: `\\t' here doesn't match a tab; to do that, just type TAB")))
86 (sit-for 2))))
87
770970cb
RS
88 (setq to (read-from-minibuffer (format "%s %s with: " string from)
89 nil nil nil
4a8f3b3d 90 query-replace-to-history-variable from t))
47d72254
GM
91 (if (and transient-mark-mode mark-active)
92 (list from to current-prefix-arg (region-beginning) (region-end))
93 (list from to current-prefix-arg nil nil))))
770970cb 94
47d72254 95(defun query-replace (from-string to-string &optional delimited start end)
da44e784
RM
96 "Replace some occurrences of FROM-STRING with TO-STRING.
97As each match is found, the user must type a character saying
98what to do with it. For directions, type \\[help-command] at that time.
99
7ef5c431
KH
100In Transient Mark mode, if the mark is active, operate on the contents
101of the region. Otherwise, operate from point to the end of the buffer.
102
151270f3
RS
103If `query-replace-interactive' is non-nil, the last incremental search
104string is used as FROM-STRING--you don't have to specify it with the
105minibuffer.
106
d2a0ee8b
RS
107Replacement transfers the case of the old text to the new text,
108if `case-replace' and `case-fold-search'
da44e784 109are non-nil and FROM-STRING has no uppercase letters.
9b0bf2b6
RS
110\(Preserving case means that if the string matched is all caps, or capitalized,
111then its replacement is upcased or capitalized.)
112
118a01c9 113Third arg DELIMITED (prefix arg if interactive), if non-nil, means replace
81bdc14d 114only matches surrounded by word boundaries.
47d72254 115Fourth and fifth arg START and END specify the region to operate on.
81bdc14d
RS
116
117To customize possible responses, change the \"bindings\" in `query-replace-map'."
151270f3 118 (interactive (query-replace-read-args "Query replace" nil))
99a7559f 119 (perform-replace from-string to-string t nil delimited nil nil start end))
7ef5c431 120
73fa8346 121(define-key esc-map "%" 'query-replace)
da44e784 122
47d72254 123(defun query-replace-regexp (regexp to-string &optional delimited start end)
da44e784
RM
124 "Replace some things after point matching REGEXP with TO-STRING.
125As each match is found, the user must type a character saying
126what to do with it. For directions, type \\[help-command] at that time.
127
7ef5c431
KH
128In Transient Mark mode, if the mark is active, operate on the contents
129of the region. Otherwise, operate from point to the end of the buffer.
130
151270f3
RS
131If `query-replace-interactive' is non-nil, the last incremental search
132regexp is used as REGEXP--you don't have to specify it with the
133minibuffer.
134
118a01c9 135Preserves case in each replacement if `case-replace' and `case-fold-search'
da44e784 136are non-nil and REGEXP has no uppercase letters.
47d72254 137
118a01c9 138Third arg DELIMITED (prefix arg if interactive), if non-nil, means replace
da44e784 139only matches surrounded by word boundaries.
47d72254
GM
140Fourth and fifth arg START and END specify the region to operate on.
141
118a01c9
RS
142In TO-STRING, `\\&' stands for whatever matched the whole of REGEXP,
143and `\\=\\N' (where N is a digit) stands for
144 whatever what matched the Nth `\\(...\\)' in REGEXP."
151270f3 145 (interactive (query-replace-read-args "Query replace regexp" t))
99a7559f 146 (perform-replace regexp to-string t t delimited nil nil start end))
cbc127de 147(define-key esc-map [?\C-%] 'query-replace-regexp)
da44e784 148
47d72254 149(defun query-replace-regexp-eval (regexp to-expr &optional delimited start end)
84482eb3
RS
150 "Replace some things after point matching REGEXP with the result of TO-EXPR.
151As each match is found, the user must type a character saying
152what to do with it. For directions, type \\[help-command] at that time.
153
154TO-EXPR is a Lisp expression evaluated to compute each replacement. It may
155reference `replace-count' to get the number of replacements already made.
156If the result of TO-EXPR is not a string, it is converted to one using
157`prin1-to-string' with the NOESCAPE argument (which see).
158
159For convenience, when entering TO-EXPR interactively, you can use `\\&' or
653479ad
AS
160`\\0' to stand for whatever matched the whole of REGEXP, and `\\N' (where
161N is a digit) to stand for whatever matched the Nth `\\(...\\)' in REGEXP.
84482eb3
RS
162Use `\\#&' or `\\#N' if you want a number instead of a string.
163
164In Transient Mark mode, if the mark is active, operate on the contents
165of the region. Otherwise, operate from point to the end of the buffer.
166
167If `query-replace-interactive' is non-nil, the last incremental search
168regexp is used as REGEXP--you don't have to specify it with the
169minibuffer.
170
171Preserves case in each replacement if `case-replace' and `case-fold-search'
172are non-nil and REGEXP has no uppercase letters.
47d72254 173
84482eb3 174Third arg DELIMITED (prefix arg if interactive), if non-nil, means replace
470bbe9b 175only matches that are surrounded by word boundaries.
47d72254 176Fourth and fifth arg START and END specify the region to operate on."
84482eb3 177 (interactive
47d72254
GM
178 (let (from to start end)
179 (when (and transient-mark-mode mark-active)
180 (setq start (region-beginning)
181 end (region-end)))
84482eb3
RS
182 (if query-replace-interactive
183 (setq from (car regexp-search-ring))
184 (setq from (read-from-minibuffer "Query replace regexp: "
185 nil nil nil
186 query-replace-from-history-variable
187 nil t)))
188 (setq to (list (read-from-minibuffer
189 (format "Query replace regexp %s with eval: " from)
190 nil nil t query-replace-to-history-variable from t)))
191 ;; We make TO a list because replace-match-string-symbols requires one,
192 ;; and the user might enter a single token.
193 (replace-match-string-symbols to)
43744cb9 194 (list from (car to) current-prefix-arg start end)))
d2ce3151 195 (perform-replace regexp (cons 'replace-eval-replacement to-expr)
99a7559f 196 t t delimited nil nil start end))
84482eb3 197
47d72254 198(defun map-query-replace-regexp (regexp to-strings &optional n start end)
da44e784 199 "Replace some matches for REGEXP with various strings, in rotation.
e730be7f
DL
200The second argument TO-STRINGS contains the replacement strings,
201separated by spaces. Third arg DELIMITED (prefix arg if interactive),
202if non-nil, means replace only matches surrounded by word boundaries.
203This command works like `query-replace-regexp' except that each
204successive replacement uses the next successive replacement string,
da44e784
RM
205wrapping around from the last such string to the first.
206
7ef5c431
KH
207In Transient Mark mode, if the mark is active, operate on the contents
208of the region. Otherwise, operate from point to the end of the buffer.
209
da44e784
RM
210Non-interactively, TO-STRINGS may be a list of replacement strings.
211
151270f3
RS
212If `query-replace-interactive' is non-nil, the last incremental search
213regexp is used as REGEXP--you don't have to specify it with the minibuffer.
214
da44e784 215A prefix argument N says to use each replacement string N times
47d72254
GM
216before rotating to the next.
217Fourth and fifth arg START and END specify the region to operate on."
770970cb 218 (interactive
47d72254
GM
219 (let (from to start end)
220 (when (and transient-mark-mode mark-active)
221 (setq start (region-beginning)
222 end (region-end)))
151270f3
RS
223 (setq from (if query-replace-interactive
224 (car regexp-search-ring)
225 (read-from-minibuffer "Map query replace (regexp): "
226 nil nil nil
fce31d51 227 'query-replace-history nil t)))
770970cb
RS
228 (setq to (read-from-minibuffer
229 (format "Query replace %s with (space-separated strings): "
230 from)
231 nil nil nil
4a8f3b3d 232 'query-replace-history from t))
47d72254 233 (list from to start end current-prefix-arg)))
da44e784
RM
234 (let (replacements)
235 (if (listp to-strings)
236 (setq replacements to-strings)
237 (while (/= (length to-strings) 0)
238 (if (string-match " " to-strings)
239 (setq replacements
240 (append replacements
241 (list (substring to-strings 0
242 (string-match " " to-strings))))
243 to-strings (substring to-strings
244 (1+ (string-match " " to-strings))))
245 (setq replacements (append replacements (list to-strings))
246 to-strings ""))))
99a7559f 247 (perform-replace regexp replacements t t nil n nil start end)))
da44e784 248
47d72254 249(defun replace-string (from-string to-string &optional delimited start end)
da44e784
RM
250 "Replace occurrences of FROM-STRING with TO-STRING.
251Preserve case in each match if `case-replace' and `case-fold-search'
252are non-nil and FROM-STRING has no uppercase letters.
9b0bf2b6
RS
253\(Preserving case means that if the string matched is all caps, or capitalized,
254then its replacement is upcased or capitalized.)
255
7ef5c431
KH
256In Transient Mark mode, if the mark is active, operate on the contents
257of the region. Otherwise, operate from point to the end of the buffer.
258
118a01c9 259Third arg DELIMITED (prefix arg if interactive), if non-nil, means replace
da44e784 260only matches surrounded by word boundaries.
47d72254 261Fourth and fifth arg START and END specify the region to operate on.
da44e784 262
151270f3
RS
263If `query-replace-interactive' is non-nil, the last incremental search
264string is used as FROM-STRING--you don't have to specify it with the
265minibuffer.
266
da44e784
RM
267This function is usually the wrong thing to use in a Lisp program.
268What you probably want is a loop like this:
118a01c9
RS
269 (while (search-forward FROM-STRING nil t)
270 (replace-match TO-STRING nil t))
87532fbe
RS
271which will run faster and will not set the mark or print anything.
272\(You may need a more complex loop if FROM-STRING can match the null string
273and TO-STRING is also null.)"
151270f3 274 (interactive (query-replace-read-args "Replace string" nil))
99a7559f 275 (perform-replace from-string to-string nil nil delimited nil nil start end))
da44e784 276
47d72254 277(defun replace-regexp (regexp to-string &optional delimited start end)
da44e784 278 "Replace things after point matching REGEXP with TO-STRING.
118a01c9 279Preserve case in each match if `case-replace' and `case-fold-search'
da44e784 280are non-nil and REGEXP has no uppercase letters.
47d72254
GM
281
282In Transient Mark mode, if the mark is active, operate on the contents
283of the region. Otherwise, operate from point to the end of the buffer.
284
118a01c9 285Third arg DELIMITED (prefix arg if interactive), if non-nil, means replace
da44e784 286only matches surrounded by word boundaries.
47d72254
GM
287Fourth and fifth arg START and END specify the region to operate on.
288
118a01c9
RS
289In TO-STRING, `\\&' stands for whatever matched the whole of REGEXP,
290and `\\=\\N' (where N is a digit) stands for
880f22a1 291 whatever what matched the Nth `\\(...\\)' in REGEXP.
da44e784 292
151270f3
RS
293If `query-replace-interactive' is non-nil, the last incremental search
294regexp is used as REGEXP--you don't have to specify it with the minibuffer.
295
da44e784
RM
296This function is usually the wrong thing to use in a Lisp program.
297What you probably want is a loop like this:
298 (while (re-search-forward REGEXP nil t)
118a01c9 299 (replace-match TO-STRING nil nil))
da44e784 300which will run faster and will not set the mark or print anything."
151270f3 301 (interactive (query-replace-read-args "Replace regexp" t))
99a7559f 302 (perform-replace regexp to-string nil t delimited nil nil start end))
e32eb3e6 303
4c53bd2b
RS
304\f
305(defvar regexp-history nil
306 "History list for some commands that read regular expressions.")
da44e784 307
e32eb3e6 308
31e1d920 309(defalias 'delete-non-matching-lines 'keep-lines)
e32eb3e6
GM
310(defalias 'delete-matching-lines 'flush-lines)
311(defalias 'count-matches 'how-many)
312
313
314(defun keep-lines-read-args (prompt)
315 "Read arguments for `keep-lines' and friends.
316Prompt for a regexp with PROMPT.
2ced751f
RS
317Value is a list, (REGEXP)."
318 (list (read-from-minibuffer prompt nil nil nil
319 'regexp-history nil t)))
e32eb3e6
GM
320
321(defun keep-lines (regexp &optional rstart rend)
698e1804
RS
322 "Delete all lines except those containing matches for REGEXP.
323A match split across lines preserves all the lines it lies in.
d2a0ee8b
RS
324Applies to all lines after point.
325
326If REGEXP contains upper case characters (excluding those preceded by `\\'),
e32eb3e6
GM
327the matching is case-sensitive.
328
329Second and third arg RSTART and REND specify the region to operate on.
330
2ced751f
RS
331Interactively, in Transient Mark mode when the mark is active, operate
332on the contents of the region. Otherwise, operate from point to the
333end of the buffer."
334
e32eb3e6
GM
335 (interactive
336 (keep-lines-read-args "Keep lines (containing match for regexp): "))
337 (if rstart
338 (goto-char (min rstart rend))
2ced751f
RS
339 (if (and transient-mark-mode mark-active)
340 (setq rstart (region-beginning)
341 rend (copy-marker (region-end)))
342 (setq rstart (point)
343 rend (point-max-marker)))
344 (goto-char rstart))
698e1804
RS
345 (save-excursion
346 (or (bolp) (forward-line 1))
d2a0ee8b
RS
347 (let ((start (point))
348 (case-fold-search (and case-fold-search
349 (isearch-no-upper-case-p regexp t))))
e32eb3e6 350 (while (< (point) rend)
698e1804 351 ;; Start is first char not preserved by previous match.
e32eb3e6
GM
352 (if (not (re-search-forward regexp rend 'move))
353 (delete-region start rend)
698e1804
RS
354 (let ((end (save-excursion (goto-char (match-beginning 0))
355 (beginning-of-line)
356 (point))))
357 ;; Now end is first char preserved by the new match.
358 (if (< start end)
359 (delete-region start end))))
e32eb3e6
GM
360
361 (setq start (save-excursion (forward-line 1) (point)))
698e1804 362 ;; If the match was empty, avoid matching again at same place.
e32eb3e6
GM
363 (and (< (point) rend)
364 (= (match-beginning 0) (match-end 0))
698e1804
RS
365 (forward-char 1))))))
366
e32eb3e6
GM
367
368(defun flush-lines (regexp &optional rstart rend)
698e1804
RS
369 "Delete lines containing matches for REGEXP.
370If a match is split across lines, all the lines it lies in are deleted.
d2a0ee8b
RS
371Applies to lines after point.
372
373If REGEXP contains upper case characters (excluding those preceded by `\\'),
e32eb3e6
GM
374the matching is case-sensitive.
375
376Second and third arg RSTART and REND specify the region to operate on.
377
2ced751f
RS
378Interactively, in Transient Mark mode when the mark is active, operate
379on the contents of the region. Otherwise, operate from point to the
380end of the buffer."
381
e32eb3e6
GM
382 (interactive
383 (keep-lines-read-args "Flush lines (containing match for regexp): "))
384 (if rstart
385 (goto-char (min rstart rend))
2ced751f
RS
386 (if (and transient-mark-mode mark-active)
387 (setq rstart (region-beginning)
388 rend (copy-marker (region-end)))
389 (setq rstart (point)
390 rend (point-max-marker)))
391 (goto-char rstart))
d2a0ee8b
RS
392 (let ((case-fold-search (and case-fold-search
393 (isearch-no-upper-case-p regexp t))))
394 (save-excursion
e32eb3e6
GM
395 (while (and (< (point) rend)
396 (re-search-forward regexp rend t))
d2a0ee8b
RS
397 (delete-region (save-excursion (goto-char (match-beginning 0))
398 (beginning-of-line)
399 (point))
400 (progn (forward-line 1) (point)))))))
698e1804 401
e32eb3e6
GM
402
403(defun how-many (regexp &optional rstart rend)
d2a0ee8b
RS
404 "Print number of matches for REGEXP following point.
405
406If REGEXP contains upper case characters (excluding those preceded by `\\'),
e32eb3e6
GM
407the matching is case-sensitive.
408
409Second and third arg RSTART and REND specify the region to operate on.
410
2ced751f
RS
411Interactively, in Transient Mark mode when the mark is active, operate
412on the contents of the region. Otherwise, operate from point to the
413end of the buffer."
414
e32eb3e6
GM
415 (interactive
416 (keep-lines-read-args "How many matches for (regexp): "))
f601efb0
SM
417 (save-excursion
418 (if rstart
419 (goto-char (min rstart rend))
2ced751f
RS
420 (if (and transient-mark-mode mark-active)
421 (setq rstart (region-beginning)
422 rend (copy-marker (region-end)))
423 (setq rstart (point)
424 rend (point-max-marker)))
425 (goto-char rstart))
f601efb0
SM
426 (let ((count 0)
427 opoint
428 (case-fold-search (and case-fold-search
429 (isearch-no-upper-case-p regexp t))))
430 (while (and (< (point) rend)
431 (progn (setq opoint (point))
432 (re-search-forward regexp rend t)))
433 (if (= opoint (point))
434 (forward-char 1)
435 (setq count (1+ count))))
436 (message "%d occurrences" count))))
e32eb3e6 437
4c53bd2b 438\f
f601efb0
SM
439(defvar occur-mode-map
440 (let ((map (make-sparse-keymap)))
441 (define-key map [mouse-2] 'occur-mode-mouse-goto)
442 (define-key map "\C-c\C-c" 'occur-mode-goto-occurrence)
443 (define-key map "\C-m" 'occur-mode-goto-occurrence)
029024e2 444 (define-key map "\o" 'occur-mode-goto-occurrence-other-window)
365486d6 445 (define-key map "\C-o" 'occur-mode-display-occurrence)
f601efb0
SM
446 (define-key map "\M-n" 'occur-next)
447 (define-key map "\M-p" 'occur-prev)
448 (define-key map "g" 'revert-buffer)
449 map)
450 "Keymap for `occur-mode'.")
698e1804 451
68608d9c 452(defvar occur-revert-properties nil)
698e1804 453
de3c9b09 454(put 'occur-mode 'mode-class 'special)
505847d4 455(defun occur-mode ()
698e1804 456 "Major mode for output from \\[occur].
0081c8a1
RS
457\\<occur-mode-map>Move point to one of the items in this buffer, then use
458\\[occur-mode-goto-occurrence] to go to the occurrence that the item refers to.
459Alternatively, click \\[occur-mode-mouse-goto] on an item to go to it.
460
698e1804 461\\{occur-mode-map}"
505847d4
RS
462 (kill-all-local-variables)
463 (use-local-map occur-mode-map)
464 (setq major-mode 'occur-mode)
465 (setq mode-name "Occur")
466 (make-local-variable 'revert-buffer-function)
68608d9c
CW
467 (set (make-local-variable 'font-lock-defaults)
468 '(nil t nil nil nil
469 (font-lock-fontify-region-function . occur-fontify-region-function)
470 (font-lock-unfontify-region-function . occur-unfontify-region-function)))
505847d4 471 (setq revert-buffer-function 'occur-revert-function)
f601efb0 472 (set (make-local-variable 'revert-buffer-function) 'occur-revert-function)
68608d9c 473 (make-local-variable 'occur-revert-properties)
505847d4 474 (run-hooks 'occur-mode-hook))
698e1804 475
a41284da 476(defun occur-revert-function (ignore1 ignore2)
f54701d1 477 "Handle `revert-buffer' for *Occur* buffers."
68608d9c 478 (apply 'occur-1 occur-revert-properties))
a41284da 479
78bead73
RS
480(defun occur-mode-mouse-goto (event)
481 "In Occur mode, go to the occurrence whose line you click on."
482 (interactive "e")
68608d9c
CW
483 (let ((buffer nil)
484 (pos nil))
78bead73
RS
485 (save-excursion
486 (set-buffer (window-buffer (posn-window (event-end event))))
487 (save-excursion
488 (goto-char (posn-point (event-end event)))
68608d9c
CW
489 (let ((props (occur-mode-find-occurrence)))
490 (setq buffer (car props))
491 (setq pos (cdr props)))))
78bead73
RS
492 (pop-to-buffer buffer)
493 (goto-char (marker-position pos))))
494
495(defun occur-mode-find-occurrence ()
68608d9c
CW
496 (let ((props (get-text-property (point) 'occur-target)))
497 (unless props
498 (error "No occurrence on this line"))
499 (unless (buffer-live-p (car props))
500 (error "Buffer in which occurrence was found is deleted"))
501 props))
78bead73
RS
502
503(defun occur-mode-goto-occurrence ()
504 "Go to the occurrence the current line describes."
505 (interactive)
68608d9c
CW
506 (let ((target (occur-mode-find-occurrence)))
507 (pop-to-buffer (car target))
508 (goto-char (marker-position (cdr target)))))
8d15583f 509
029024e2
RS
510(defun occur-mode-goto-occurrence-other-window ()
511 "Go to the occurrence the current line describes, in another window."
512 (interactive)
68608d9c
CW
513 (let ((target (occur-mode-find-occurrence)))
514 (switch-to-buffer-other-window (car target))
515 (goto-char (marker-position (cdr target)))))
029024e2 516
365486d6
RS
517(defun occur-mode-display-occurrence ()
518 "Display in another window the occurrence the current line describes."
519 (interactive)
68608d9c 520 (let ((target (occur-mode-find-occurrence))
365486d6
RS
521 same-window-buffer-names
522 same-window-regexps
523 window)
68608d9c 524 (setq window (display-buffer (car target)))
365486d6
RS
525 ;; This is the way to set point in the proper window.
526 (save-selected-window
527 (select-window window)
68608d9c 528 (goto-char (marker-position (cdr target))))))
365486d6 529
8d15583f
RS
530(defun occur-next (&optional n)
531 "Move to the Nth (default 1) next match in the *Occur* buffer."
532 (interactive "p")
533 (if (not n) (setq n 1))
534 (let ((r))
535 (while (> n 0)
536 (if (get-text-property (point) 'occur-point)
537 (forward-char 1))
538 (setq r (next-single-property-change (point) 'occur-point))
539 (if r
540 (goto-char r)
e730be7f 541 (error "No more matches"))
8d15583f
RS
542 (setq n (1- n)))))
543
8d15583f
RS
544(defun occur-prev (&optional n)
545 "Move to the Nth (default 1) previous match in the *Occur* buffer."
546 (interactive "p")
547 (if (not n) (setq n 1))
548 (let ((r))
549 (while (> n 0)
550
551 (setq r (get-text-property (point) 'occur-point))
552 (if r (forward-char -1))
553
554 (setq r (previous-single-property-change (point) 'occur-point))
555 (if r
556 (goto-char (- r 1))
e730be7f 557 (error "No earlier matches"))
8d15583f
RS
558
559 (setq n (1- n)))))
4c53bd2b 560\f
9d325ebf 561(defcustom list-matching-lines-default-context-lines 0
e730be7f
DL
562 "*Default number of context lines included around `list-matching-lines' matches.
563A negative number means to include that many lines before the match.
9d325ebf
RS
564A positive number means to include that many lines both before and after."
565 :type 'integer
566 :group 'matching)
698e1804 567
31e1d920 568(defalias 'list-matching-lines 'occur)
698e1804 569
68608d9c 570(defcustom list-matching-lines-face 'bold
e730be7f 571 "*Face used by \\[list-matching-lines] to show the text that matches.
68608d9c
CW
572If the value is nil, don't highlight the matching portions specially."
573 :type 'face
574 :group 'matching)
575
576(defcustom list-matching-lines-buffer-name-face 'underline
577 "*Face used by \\[list-matching-lines] to show the names of buffers.
578If the value is nil, don't highlight the buffer names specially."
579 :type 'face
580 :group 'matching)
581
9e2b2e30 582(defun occur-accumulate-lines (count &optional no-props)
68608d9c
CW
583 (save-excursion
584 (let ((forwardp (> count 0))
585 (result nil))
586 (while (not (or (zerop count)
587 (if forwardp
588 (eobp)
589 (bobp))))
590 (if forwardp
591 (decf count)
592 (incf count))
593 (push
9e2b2e30
CW
594 (funcall (if no-props
595 #'buffer-substring-no-properties
596 #'buffer-substring)
68608d9c
CW
597 (line-beginning-position)
598 (line-end-position))
599 result)
600 (forward-line (if forwardp 1 -1)))
601 (nreverse result))))
602
603(defun occur-read-primary-args ()
604 (list (let* ((default (car regexp-history))
605 (input
606 (read-from-minibuffer
607 (if default
608 (format "List lines matching regexp (default `%s'): "
609 default)
610 "List lines matching regexp: ")
611 nil
612 nil
613 nil
614 'regexp-history)))
615 (if (equal input "")
616 default
617 input))
618 current-prefix-arg))
c9daced0 619
698e1804 620(defun occur (regexp &optional nlines)
99976f85 621 "Show all lines in the current buffer containing a match for REGEXP.
da44e784
RM
622
623If a match spreads across multiple lines, all those lines are shown.
698e1804 624
da44e784
RM
625Each line is displayed with NLINES lines before and after, or -NLINES
626before if NLINES is negative.
627NLINES defaults to `list-matching-lines-default-context-lines'.
698e1804
RS
628Interactively it is the prefix arg.
629
4c53bd2b 630The lines are shown in a buffer named `*Occur*'.
698e1804 631It serves as a menu to find any of the occurrences in this buffer.
de3c9b09 632\\<occur-mode-map>\\[describe-mode] in that buffer will explain how.
9483d601 633
de3c9b09
RS
634If REGEXP contains upper case characters (excluding those preceded by `\\'),
635the matching is case-sensitive."
68608d9c
CW
636 (interactive (occur-read-primary-args))
637 (occur-1 regexp nlines (list (current-buffer))))
638
639(defun multi-occur (bufs regexp &optional nlines)
640 "Show all lines in buffers BUFS containing a match for REGEXP.
641This function acts on multiple buffers; otherwise, it is exactly like
642`occur'."
a5dfed3e 643 (interactive
68608d9c
CW
644 (cons
645 (let ((bufs (list (read-buffer "First buffer to search: "
646 (current-buffer) t)))
647 (buf nil))
648 (while (not (string-equal
649 (setq buf (read-buffer "Next buffer to search (RET to end): "
650 nil t))
651 ""))
652 (push buf bufs))
653 (nreverse (mapcar #'get-buffer bufs)))
654 (occur-read-primary-args)))
655 (occur-1 regexp nlines bufs))
656
657(defun multi-occur-by-filename-regexp (bufregexp regexp &optional nlines)
04fe158a 658 "Show all lines matching REGEXP in buffers named by BUFREGEXP.
68608d9c
CW
659See also `multi-occur'."
660 (interactive
661 (cons
662 (let* ((default (car regexp-history))
663 (input
664 (read-from-minibuffer
665 "List lines in buffers whose filename matches regexp: "
666 nil
667 nil
668 nil
669 'regexp-history)))
670 (if (equal input "")
671 default
672 input))
673 (occur-read-primary-args)))
674 (when bufregexp
675 (occur-1 regexp nlines
676 (delq nil
677 (mapcar (lambda (buf)
678 (when (and (buffer-file-name buf)
679 (string-match bufregexp
680 (buffer-file-name buf)))
681 buf))
682 (buffer-list))))))
683
684(defun occur-1 (regexp nlines bufs)
685 (let ((occur-buf (get-buffer-create "*Occur*")))
686 (with-current-buffer occur-buf
687 (setq buffer-read-only nil)
688 (occur-mode)
689 (erase-buffer)
690 (let ((count (occur-engine
691 regexp bufs occur-buf
692 (or nlines list-matching-lines-default-context-lines)
693 (and case-fold-search
694 (isearch-no-upper-case-p regexp t))
695 nil nil nil nil)))
696 (message "Searched %d buffers; %s matches for `%s'" (length bufs)
697 (if (zerop count)
698 "no"
699 (format "%d" count))
700 regexp)
701 (if (> count 0)
702 (display-buffer occur-buf)
703 (kill-buffer occur-buf)))
68608d9c
CW
704 (setq occur-revert-properties (list regexp nlines bufs)
705 buffer-read-only t))))
706
707;; Most of these are macros becuase if we used `flet', it wouldn't
708;; create a closure, so things would blow up at run time. Ugh. :(
709(macrolet ((insert-get-point (obj)
710 `(progn
711 (insert ,obj)
712 (point)))
713 (add-prefix (lines)
714 `(mapcar
715 #'(lambda (line)
716 (concat " :" line "\n"))
717 ,lines)))
718 (defun occur-engine (regexp buffers out-buf nlines case-fold-search
719 title-face prefix-face match-face keep-props)
720 (with-current-buffer out-buf
721 (setq buffer-read-only nil)
722 (let ((globalcount 0))
723 ;; Map over all the buffers
724 (dolist (buf buffers)
725 (when (buffer-live-p buf)
726 (let ((c 0) ;; count of matched lines
727 (l 1) ;; line count
728 (matchbeg 0)
729 (matchend 0)
730 (origpt nil)
731 (begpt nil)
732 (endpt nil)
733 (marker nil)
734 (curstring "")
735 (headerpt (with-current-buffer out-buf (point))))
736 (save-excursion
737 (set-buffer buf)
016c214f 738 (save-excursion
68608d9c
CW
739 (goto-char (point-min)) ;; begin searching in the buffer
740 (while (not (eobp))
741 (setq origpt (point))
742 (when (setq endpt (re-search-forward regexp nil t))
743 (incf c) ;; increment match count
744 (incf globalcount)
745 (setq matchbeg (match-beginning 0)
746 matchend (match-end 0))
747 (setq begpt (save-excursion
748 (goto-char matchbeg)
749 (line-beginning-position)))
750 (incf l (1- (count-lines origpt endpt)))
751 (setq marker (make-marker))
752 (set-marker marker matchbeg)
753 (setq curstring (buffer-substring begpt
754 (line-end-position)))
755 ;; Depropertize the string, and maybe
756 ;; highlight the matches
757 (let ((len (length curstring))
758 (start 0))
759 (unless keep-props
760 (set-text-properties 0 len nil curstring))
761 (while (and (< start len)
762 (string-match regexp curstring start))
763 (add-text-properties (match-beginning 0)
764 (match-end 0)
765 (append
766 '(occur-match t)
767 (when match-face
768 `(face ,match-face)))
769 curstring)
770 (setq start (match-end 0))))
771 ;; Generate the string to insert for this match
772 (let* ((out-line
773 (concat
ba485f2e 774 (apply #'propertize (format "%6d:" l)
68608d9c
CW
775 (append
776 (when prefix-face
777 `(face prefix-face))
778 '(occur-prefix t)))
779 curstring
780 "\n"))
781 (data
ac76acf7 782 (if (= nlines 0)
68608d9c
CW
783 ;; The simple display style
784 out-line
785 ;; The complex multi-line display
786 ;; style. Generate a list of lines,
787 ;; concatenate them all together.
788 (apply #'concat
789 (nconc
ac76acf7 790 (add-prefix (nreverse (cdr (occur-accumulate-lines (- (1+ nlines)) t))))
68608d9c 791 (list out-line)
ac76acf7 792 (add-prefix (cdr (occur-accumulate-lines (1+ nlines) t))))))))
68608d9c
CW
793 ;; Actually insert the match display data
794 (with-current-buffer out-buf
795 (let ((beg (point))
796 (end (insert-get-point data)))
ac76acf7 797 (unless (= nlines 0)
68608d9c
CW
798 (insert-get-point "-------\n"))
799 (add-text-properties
800 beg (1- end)
801 `(occur-target ,(cons buf marker)
802 mouse-face highlight help-echo
803 "mouse-2: go to this occurrence")))))
804 (goto-char endpt))
805 (incf l)
806 ;; On to the next match...
807 (forward-line 1))))
808 (when (not (zerop c)) ;; is the count zero?
809 (with-current-buffer out-buf
810 (goto-char headerpt)
811 (let ((beg (point))
812 (end (insert-get-point
813 (format "%d lines matching \"%s\" in buffer: %s\n"
814 c regexp (buffer-name buf)))))
815 (add-text-properties beg end
816 (append
817 (when title-face
818 `(face ,title-face))
819 `(occur-title ,buf))))
04fe158a 820 (goto-char (point-min)))))))
68608d9c
CW
821 ;; Return the number of matches
822 globalcount))))
823
824(defun occur-fontify-on-property (prop face beg end)
825 (let ((prop-beg (or (and (get-text-property (point) prop) (point))
826 (next-single-property-change (point) prop nil end))))
827 (when (and prop-beg (not (= prop-beg end)))
828 (let ((prop-end (next-single-property-change beg prop nil end)))
829 (when (and prop-end (not (= prop-end end)))
830 (put-text-property prop-beg prop-end 'face face)
831 prop-end)))))
832
833(defun occur-fontify-region-function (beg end &optional verbose)
834 (when verbose (message "Fontifying..."))
835 (let ((inhibit-read-only t))
836 (save-excursion
837 (dolist (e `((occur-title . ,list-matching-lines-buffer-name-face)
838 (occur-match . ,list-matching-lines-face)))
839 ; (occur-prefix . ,list-matching-lines-prefix-face)))
840 (goto-char beg)
841 (let ((change-end nil))
842 (while (setq change-end (occur-fontify-on-property (car e)
843 (cdr e)
844 (point)
845 end))
846 (goto-char change-end))))))
847 (when verbose (message "Fontifying...done")))
848
849(defun occur-unfontify-region-function (beg end)
850 (let ((inhibit-read-only t))
851 (remove-text-properties beg end '(face nil))))
852
698e1804 853\f
81bdc14d
RS
854;; It would be nice to use \\[...], but there is no reasonable way
855;; to make that display both SPC and Y.
698e1804
RS
856(defconst query-replace-help
857 "Type Space or `y' to replace one match, Delete or `n' to skip to next,
be44f62c 858RET or `q' to exit, Period to replace one match and exit,
698e1804
RS
859Comma to replace but not move point immediately,
860C-r to enter recursive edit (\\[exit-recursive-edit] to get out again),
861C-w to delete match and recursive edit,
862C-l to clear the screen, redisplay, and offer same replacement again,
863! to replace all remaining matches with no more questions,
7ce278f3
GM
864^ to move point back to previous match,
865E to edit the replacement string"
f54701d1 866 "Help message while in `query-replace'.")
698e1804 867
81bdc14d
RS
868(defvar query-replace-map (make-sparse-keymap)
869 "Keymap that defines the responses to questions in `query-replace'.
870The \"bindings\" in this map are not commands; they are answers.
871The valid answers include `act', `skip', `act-and-show',
872`exit', `act-and-exit', `edit', `delete-and-edit', `recenter',
d9121bc0 873`automatic', `backup', `exit-prefix', and `help'.")
81bdc14d
RS
874
875(define-key query-replace-map " " 'act)
876(define-key query-replace-map "\d" 'skip)
877(define-key query-replace-map [delete] 'skip)
9275e5e3 878(define-key query-replace-map [backspace] 'skip)
81bdc14d
RS
879(define-key query-replace-map "y" 'act)
880(define-key query-replace-map "n" 'skip)
633a305a
RS
881(define-key query-replace-map "Y" 'act)
882(define-key query-replace-map "N" 'skip)
34724fcb 883(define-key query-replace-map "e" 'edit-replacement)
7ce278f3 884(define-key query-replace-map "E" 'edit-replacement)
81bdc14d 885(define-key query-replace-map "," 'act-and-show)
81bdc14d 886(define-key query-replace-map "q" 'exit)
919592c0 887(define-key query-replace-map "\r" 'exit)
384c7da4 888(define-key query-replace-map [return] 'exit)
81bdc14d
RS
889(define-key query-replace-map "." 'act-and-exit)
890(define-key query-replace-map "\C-r" 'edit)
891(define-key query-replace-map "\C-w" 'delete-and-edit)
892(define-key query-replace-map "\C-l" 'recenter)
893(define-key query-replace-map "!" 'automatic)
894(define-key query-replace-map "^" 'backup)
895(define-key query-replace-map "\C-h" 'help)
e731045a
KH
896(define-key query-replace-map [f1] 'help)
897(define-key query-replace-map [help] 'help)
81bdc14d 898(define-key query-replace-map "?" 'help)
bc6312e1
RS
899(define-key query-replace-map "\C-g" 'quit)
900(define-key query-replace-map "\C-]" 'quit)
d9121bc0
RS
901(define-key query-replace-map "\e" 'exit-prefix)
902(define-key query-replace-map [escape] 'exit-prefix)
81bdc14d 903
84482eb3 904(defun replace-match-string-symbols (n)
e730be7f
DL
905 "Process a list (and any sub-lists), expanding certain symbols.
906Symbol Expands To
907N (match-string N) (where N is a string of digits)
908#N (string-to-number (match-string N))
909& (match-string 0)
910#& (string-to-number (match-string 0))
911
912Note that these symbols must be preceeded by a backslash in order to
913type them."
84482eb3
RS
914 (while n
915 (cond
916 ((consp (car n))
917 (replace-match-string-symbols (car n))) ;Process sub-list
918 ((symbolp (car n))
919 (let ((name (symbol-name (car n))))
920 (cond
921 ((string-match "^[0-9]+$" name)
922 (setcar n (list 'match-string (string-to-number name))))
923 ((string-match "^#[0-9]+$" name)
924 (setcar n (list 'string-to-number
925 (list 'match-string
926 (string-to-number (substring name 1))))))
927 ((string= "&" name)
928 (setcar n '(match-string 0)))
929 ((string= "#&" name)
930 (setcar n '(string-to-number (match-string 0))))))))
931 (setq n (cdr n))))
932
933(defun replace-eval-replacement (expression replace-count)
934 (let ((replacement (eval expression)))
935 (if (stringp replacement)
936 replacement
937 (prin1-to-string replacement t))))
938
939(defun replace-loop-through-replacements (data replace-count)
940 ;; DATA is a vector contaning the following values:
941 ;; 0 next-rotate-count
942 ;; 1 repeat-count
943 ;; 2 next-replacement
944 ;; 3 replacements
945 (if (= (aref data 0) replace-count)
946 (progn
947 (aset data 0 (+ replace-count (aref data 1)))
948 (let ((next (cdr (aref data 2))))
949 (aset data 2 (if (consp next) next (aref data 3))))))
950 (car (aref data 2)))
951
99a7559f 952(defun perform-replace (from-string replacements
698e1804 953 query-flag regexp-flag delimited-flag
99a7559f 954 &optional repeat-count map start end)
698e1804
RS
955 "Subroutine of `query-replace'. Its complexity handles interactive queries.
956Don't use this in your own program unless you want to query and set the mark
957just as `query-replace' does. Instead, write a simple loop like this:
698665d1
GM
958
959 (while (re-search-forward \"foo[ \\t]+bar\" nil t)
698e1804 960 (replace-match \"foobar\" nil nil))
698665d1
GM
961
962which will run faster and probably do exactly what you want. Please
963see the documentation of `replace-match' to find out how to simulate
964`case-replace'."
81bdc14d 965 (or map (setq map query-replace-map))
1c1dadab
RS
966 (and query-flag minibuffer-auto-raise
967 (raise-frame (window-frame (minibuffer-window))))
698e1804
RS
968 (let ((nocasify (not (and case-fold-search case-replace
969 (string-equal from-string
970 (downcase from-string)))))
5a78b471
KH
971 (case-fold-search (and case-fold-search
972 (string-equal from-string
973 (downcase from-string))))
698e1804
RS
974 (literal (not regexp-flag))
975 (search-function (if regexp-flag 're-search-forward 'search-forward))
976 (search-string from-string)
e5d77022 977 (real-match-data nil) ; the match data for the current match
698e1804 978 (next-replacement nil)
698e1804
RS
979 (keep-going t)
980 (stack nil)
698e1804 981 (replace-count 0)
5632eb27
PE
982 (nonempty-match nil)
983
7ef5c431
KH
984 ;; If non-nil, it is marker saying where in the buffer to stop.
985 (limit nil)
986
5632eb27
PE
987 ;; Data for the next match. If a cons, it has the same format as
988 ;; (match-data); otherwise it is t if a match is possible at point.
ae4eb03c 989 (match-again t)
5632eb27 990
02d95a27
RS
991 (message
992 (if query-flag
993 (substitute-command-keys
994 "Query replacing %s with %s: (\\<query-replace-map>\\[help] for help) "))))
7ef5c431
KH
995
996 ;; If region is active, in Transient Mark mode, operate on region.
47d72254
GM
997 (when start
998 (setq limit (copy-marker (max start end)))
999 (goto-char (min start end))
1000 (deactivate-mark))
84482eb3
RS
1001
1002 ;; REPLACEMENTS is either a string, a list of strings, or a cons cell
1003 ;; containing a function and its first argument. The function is
1004 ;; called to generate each replacement like this:
1005 ;; (funcall (car replacements) (cdr replacements) replace-count)
1006 ;; It must return a string.
1007 (cond
1008 ((stringp replacements)
1009 (setq next-replacement replacements
1010 replacements nil))
1011 ((stringp (car replacements)) ; If it isn't a string, it must be a cons
1012 (or repeat-count (setq repeat-count 1))
1013 (setq replacements (cons 'replace-loop-through-replacements
1014 (vector repeat-count repeat-count
1015 replacements replacements)))))
1016
698e1804
RS
1017 (if delimited-flag
1018 (setq search-function 're-search-forward
1019 search-string (concat "\\b"
1020 (if regexp-flag from-string
1021 (regexp-quote from-string))
1022 "\\b")))
1023 (push-mark)
1024 (undo-boundary)
e782e9f2
RS
1025 (unwind-protect
1026 ;; Loop finding occurrences that perhaps should be replaced.
1027 (while (and keep-going
1028 (not (eobp))
5632eb27
PE
1029 ;; Use the next match if it is already known;
1030 ;; otherwise, search for a match after moving forward
1031 ;; one char if progress is required.
1032 (setq real-match-data
1033 (if (consp match-again)
1034 (progn (goto-char (nth 1 match-again))
1035 match-again)
1036 (and (or match-again
889617de
GM
1037 ;; MATCH-AGAIN non-nil means we
1038 ;; accept an adjacent match. If
1039 ;; we don't, move one char to the
1040 ;; right. This takes us a
1041 ;; character too far at the end,
1042 ;; but this is undone after the
1043 ;; while-loop.
8f3ff96b 1044 (progn (forward-char 1) (not (eobp))))
7ef5c431 1045 (funcall search-function search-string limit t)
5632eb27
PE
1046 ;; For speed, use only integers and
1047 ;; reuse the list used last time.
1048 (match-data t real-match-data)))))
1c4fe319
RS
1049 ;; Optionally ignore matches that have a read-only property.
1050 (unless (and query-replace-skip-read-only
1051 (text-property-not-all
1052 (match-beginning 0) (match-end 0)
1053 'read-only nil))
1054
1055 ;; Record whether the match is nonempty, to avoid an infinite loop
1056 ;; repeatedly matching the same empty string.
1057 (setq nonempty-match
1058 (/= (nth 0 real-match-data) (nth 1 real-match-data)))
1059
1060 ;; If the match is empty, record that the next one can't be
1061 ;; adjacent.
1062
1063 ;; Otherwise, if matching a regular expression, do the next
1064 ;; match now, since the replacement for this match may
1065 ;; affect whether the next match is adjacent to this one.
1066 ;; If that match is empty, don't use it.
1067 (setq match-again
1068 (and nonempty-match
1069 (or (not regexp-flag)
1070 (and (looking-at search-string)
1071 (let ((match (match-data)))
1072 (and (/= (nth 0 match) (nth 1 match))
1073 match))))))
1074
1075 ;; Calculate the replacement string, if necessary.
1076 (when replacements
1077 (set-match-data real-match-data)
1078 (setq next-replacement
1079 (funcall (car replacements) (cdr replacements)
1080 replace-count)))
1081 (if (not query-flag)
1082 (let ((inhibit-read-only query-replace-skip-read-only))
1083 (set-match-data real-match-data)
1084 (replace-match next-replacement nocasify literal)
1085 (setq replace-count (1+ replace-count)))
1086 (undo-boundary)
1087 (let (done replaced key def)
1088 ;; Loop reading commands until one of them sets done,
1089 ;; which means it has finished handling this occurrence.
1090 (while (not done)
1091 (set-match-data real-match-data)
1092 (replace-highlight (match-beginning 0) (match-end 0))
1093 ;; Bind message-log-max so we don't fill up the message log
1094 ;; with a bunch of identical messages.
1095 (let ((message-log-max nil))
1096 (message message from-string next-replacement))
1097 (setq key (read-event))
1098 ;; Necessary in case something happens during read-event
1099 ;; that clobbers the match data.
1100 (set-match-data real-match-data)
1101 (setq key (vector key))
1102 (setq def (lookup-key map key))
1103 ;; Restore the match data while we process the command.
1104 (cond ((eq def 'help)
1105 (with-output-to-temp-buffer "*Help*"
1106 (princ
1107 (concat "Query replacing "
1108 (if regexp-flag "regexp " "")
1109 from-string " with "
1110 next-replacement ".\n\n"
1111 (substitute-command-keys
1112 query-replace-help)))
1113 (with-current-buffer standard-output
1114 (help-mode))))
1115 ((eq def 'exit)
1116 (setq keep-going nil)
1117 (setq done t))
1118 ((eq def 'backup)
1119 (if stack
1120 (let ((elt (car stack)))
1121 (goto-char (car elt))
1122 (setq replaced (eq t (cdr elt)))
1123 (or replaced
1124 (set-match-data (cdr elt)))
1125 (setq stack (cdr stack)))
1126 (message "No previous match")
1127 (ding 'no-terminate)
1128 (sit-for 1)))
1129 ((eq def 'act)
1130 (or replaced
1131 (progn
1132 (replace-match next-replacement nocasify literal)
1133 (setq replace-count (1+ replace-count))))
1134 (setq done t replaced t))
1135 ((eq def 'act-and-exit)
1136 (or replaced
1137 (progn
1138 (replace-match next-replacement nocasify literal)
1139 (setq replace-count (1+ replace-count))))
1140 (setq keep-going nil)
1141 (setq done t replaced t))
1142 ((eq def 'act-and-show)
1143 (if (not replaced)
1144 (progn
1145 (replace-match next-replacement nocasify literal)
1146 (setq replace-count (1+ replace-count))
1147 (setq replaced t))))
1148 ((eq def 'automatic)
1149 (or replaced
1150 (progn
1151 (replace-match next-replacement nocasify literal)
1152 (setq replace-count (1+ replace-count))))
1153 (setq done t query-flag nil replaced t))
1154 ((eq def 'skip)
1155 (setq done t))
1156 ((eq def 'recenter)
1157 (recenter nil))
1158 ((eq def 'edit)
1159 (let ((opos (point-marker)))
1160 (goto-char (match-beginning 0))
1161 (save-excursion
1162 (funcall search-function search-string limit t)
1163 (setq real-match-data (match-data)))
86914dcc
RS
1164 (save-excursion
1165 (save-window-excursion
1166 (recursive-edit)))
1c4fe319
RS
1167 (goto-char opos))
1168 (set-match-data real-match-data)
1169 ;; Before we make the replacement,
1170 ;; decide whether the search string
1171 ;; can match again just after this match.
1172 (if (and regexp-flag nonempty-match)
1173 (setq match-again (and (looking-at search-string)
1174 (match-data)))))
7ce278f3 1175
1c4fe319
RS
1176 ;; Edit replacement.
1177 ((eq def 'edit-replacement)
1178 (setq next-replacement
1179 (read-input "Edit replacement string: "
1180 next-replacement))
1181 (or replaced
1182 (replace-match next-replacement nocasify literal))
1183 (setq done t))
7ce278f3 1184
1c4fe319
RS
1185 ((eq def 'delete-and-edit)
1186 (delete-region (match-beginning 0) (match-end 0))
1187 (set-match-data
1188 (prog1 (match-data)
1189 (save-excursion (recursive-edit))))
1190 (setq replaced t))
1191 ;; Note: we do not need to treat `exit-prefix'
1192 ;; specially here, since we reread
1193 ;; any unrecognized character.
1194 (t
1195 (setq this-command 'mode-exited)
1196 (setq keep-going nil)
1197 (setq unread-command-events
1198 (append (listify-key-sequence key)
1199 unread-command-events))
1200 (setq done t))))
1201 ;; Record previous position for ^ when we move on.
1202 ;; Change markers to numbers in the match data
1203 ;; since lots of markers slow down editing.
1204 (setq stack
1205 (cons (cons (point)
1206 (or replaced (match-data t)))
1207 stack))))))
889617de
GM
1208
1209 ;; The code preventing adjacent regexp matches in the condition
1210 ;; of the while-loop above will haven taken us one character
1211 ;; beyond the last replacement. Undo that.
1212 (when (and regexp-flag (not match-again) (> replace-count 0))
1213 (backward-char 1))
1214
e782e9f2 1215 (replace-dehighlight))
4d33492a
RS
1216 (or unread-command-events
1217 (message "Replaced %d occurrence%s"
1218 replace-count
1219 (if (= replace-count 1) "" "s")))
1220 (and keep-going stack)))
698e1804 1221
95807e68 1222(defcustom query-replace-highlight t
9d325ebf
RS
1223 "*Non-nil means to highlight words during query replacement."
1224 :type 'boolean
1225 :group 'matching)
e782e9f2
RS
1226
1227(defvar replace-overlay nil)
1228
1229(defun replace-dehighlight ()
1230 (and replace-overlay
1231 (progn
1232 (delete-overlay replace-overlay)
1233 (setq replace-overlay nil))))
1234
1235(defun replace-highlight (start end)
1236 (and query-replace-highlight
1237 (progn
1238 (or replace-overlay
1239 (progn
1240 (setq replace-overlay (make-overlay start end))
1241 (overlay-put replace-overlay 'face
e730be7f 1242 (if (facep 'query-replace)
e782e9f2
RS
1243 'query-replace 'region))))
1244 (move-overlay replace-overlay start end (current-buffer)))))
1245
c88ab9ce 1246;;; replace.el ends here