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