(ido-file-extensions-order): New defcustom.
[bpt/emacs.git] / lisp / replace.el
CommitLineData
60370d40 1;;; replace.el --- replace commands for Emacs
c88ab9ce 2
6f1df6d9
SM
3;; Copyright (C) 1985, 1986, 1987, 1992, 1994, 1996, 1997, 2000, 2001, 2002,
4;; 2003, 2004 Free Software Foundation, Inc.
3a801d0c 5
30764597
PJ
6;; Maintainer: FSF
7
698e1804
RS
8;; This file is part of GNU Emacs.
9
10;; GNU Emacs is free software; you can redistribute it and/or modify
11;; it under the terms of the GNU General Public License as published by
e5d77022 12;; the Free Software Foundation; either version 2, or (at your option)
698e1804
RS
13;; any later version.
14
15;; GNU Emacs is distributed in the hope that it will be useful,
16;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18;; GNU General Public License for more details.
19
20;; You should have received a copy of the GNU General Public License
b578f267
EN
21;; along with GNU Emacs; see the file COPYING. If not, write to the
22;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23;; Boston, MA 02111-1307, USA.
698e1804 24
d9ecc911
ER
25;;; Commentary:
26
27;; This package supplies the string and regular-expression replace functions
28;; documented in the Emacs user's manual.
29
4f4b8eff 30;;; Code:
698e1804 31
9d325ebf 32(defcustom case-replace t
f54701d1 33 "*Non-nil means `query-replace' should preserve case in replacements."
9d325ebf
RS
34 :type 'boolean
35 :group 'matching)
77176e73 36
770970cb
RS
37(defvar query-replace-history nil)
38
5291cbca 39(defvar query-replace-interactive nil
151270f3 40 "Non-nil means `query-replace' uses the last search string.
5291cbca 41That becomes the \"string to replace\".")
151270f3 42
bdb1c08f 43(defcustom query-replace-from-history-variable 'query-replace-history
f54701d1 44 "History list to use for the FROM argument of `query-replace' commands.
bdb1c08f
RS
45The value of this variable should be a symbol; that symbol
46is used as a variable to hold a history list for the strings
47or patterns to be replaced."
48 :group 'matching
cd32a7ba
DN
49 :type 'symbol
50 :version "20.3")
bdb1c08f
RS
51
52(defcustom query-replace-to-history-variable 'query-replace-history
f54701d1 53 "History list to use for the TO argument of `query-replace' commands.
bdb1c08f
RS
54The value of this variable should be a symbol; that symbol
55is used as a variable to hold a history list for replacement
56strings or patterns."
57 :group 'matching
cd32a7ba
DN
58 :type 'symbol
59 :version "20.3")
bdb1c08f 60
1c4fe319
RS
61(defcustom query-replace-skip-read-only nil
62 "*Non-nil means `query-replace' and friends ignore read-only matches."
63 :type 'boolean
64 :group 'matching
89e7ad59 65 :version "21.4")
1c4fe319 66
afd33362
JL
67(defcustom query-replace-highlight t
68 "*Non-nil means to highlight matches during query replacement."
69 :type 'boolean
70 :group 'matching)
71
72(defcustom query-replace-lazy-highlight t
73 "*Controls the lazy-highlighting during query replacements.
74When non-nil, all text in the buffer matching the current match
75is highlighted lazily using isearch lazy highlighting (see
76`lazy-highlight-initial-delay' and `lazy-highlight-interval')."
77 :type 'boolean
78 :group 'lazy-highlight
79 :group 'matching
80 :version "21.4")
81
82(defface query-replace
83 '((t (:inherit isearch)))
84 "Face for highlighting query replacement matches."
85 :group 'matching
86 :version "21.4")
87
6f1df6d9
SM
88(defun query-replace-descr (string)
89 (mapconcat 'isearch-text-char-description string ""))
90
1606466a 91(defun query-replace-read-from (string regexp-flag)
6f1df6d9
SM
92 "Query and return the `from' argument of a query-replace operation.
93The return value can also be a pair (FROM . TO) indicating that the user
94wants to replace FROM with TO."
1606466a
SM
95 (if query-replace-interactive
96 (car (if regexp-flag regexp-search-ring search-ring))
6f1df6d9
SM
97 (let* ((lastfrom (car (symbol-value query-replace-from-history-variable)))
98 (lastto (car (symbol-value query-replace-to-history-variable)))
99 (from
1606466a
SM
100 ;; The save-excursion here is in case the user marks and copies
101 ;; a region in order to specify the minibuffer input.
102 ;; That should not clobber the region for the query-replace itself.
103 (save-excursion
6f1df6d9
SM
104 (when (equal lastfrom lastto)
105 ;; Typically, this is because the two histlists are shared.
106 (setq lastfrom (cadr (symbol-value
107 query-replace-from-history-variable))))
1606466a 108 (read-from-minibuffer
6f1df6d9
SM
109 (if (and lastto lastfrom)
110 (format "%s (default %s -> %s): " string
111 (query-replace-descr lastfrom)
112 (query-replace-descr lastto))
113 (format "%s: " string))
1606466a
SM
114 nil nil nil
115 query-replace-from-history-variable
bbe9f175 116 nil t t))))
6f1df6d9 117 (if (and (zerop (length from)) lastto lastfrom)
78629844
DK
118 (cons lastfrom
119 (query-replace-compile-replacement lastto regexp-flag))
6f1df6d9
SM
120 ;; Warn if user types \n or \t, but don't reject the input.
121 (and regexp-flag
122 (string-match "\\(\\`\\|[^\\]\\)\\(\\\\\\\\\\)*\\(\\\\[nt]\\)" from)
123 (let ((match (match-string 3 from)))
124 (cond
125 ((string= match "\\n")
126 (message "Note: `\\n' here doesn't match a newline; to do that, type C-q C-j instead"))
127 ((string= match "\\t")
128 (message "Note: `\\t' here doesn't match a tab; to do that, just type TAB")))
129 (sit-for 2)))
130 from))))
1606466a 131
78629844
DK
132(defun query-replace-compile-replacement (to regexp-flag)
133 "Maybe convert a regexp replacement TO to Lisp.
134Returns a list suitable for `perform-replace' if necessary,
135the original string if not."
136 (if (and regexp-flag
137 (string-match "\\(\\`\\|[^\\]\\)\\(\\\\\\\\\\)*\\\\[,#]" to))
7c1c02ac
DK
138 (let (pos list char)
139 (while
140 (progn
141 (setq pos (match-end 0))
142 (push (substring to 0 (- pos 2)) list)
143 (setq char (aref to (1- pos))
144 to (substring to pos))
145 (cond ((eq char ?\#)
146 (push '(number-to-string replace-count) list))
147 ((eq char ?\,)
148 (setq pos (read-from-string to))
149 (push `(replace-quote ,(car pos)) list)
9e5d1b63
RS
150 (let ((end
151 ;; Swallow a space after a symbol
152 ;; if there is a space.
153 (if (and (or (symbolp (car pos))
154 ;; Swallow a space after 'foo
155 ;; but not after (quote foo).
156 (and (eq (car-safe (car pos)) 'quote)
f1f6079c
JL
157 (not (= ?\( (aref to 0)))))
158 (eq (string-match " " to (cdr pos))
159 (cdr pos)))
9e5d1b63
RS
160 (1+ (cdr pos))
161 (cdr pos))))
162 (setq to (substring to end)))))
7c1c02ac 163 (string-match "\\(\\`\\|[^\\]\\)\\(\\\\\\\\\\)*\\\\[,#]" to)))
78629844
DK
164 (setq to (nreverse (delete "" (cons to list))))
165 (replace-match-string-symbols to)
166 (cons 'replace-eval-replacement
167 (if (cdr to)
168 (cons 'concat to)
169 (car to))))
1606466a
SM
170 to))
171
78629844
DK
172
173(defun query-replace-read-to (from string regexp-flag)
174 "Query and return the `to' argument of a query-replace operation."
175 (query-replace-compile-replacement
176 (save-excursion
177 (read-from-minibuffer
178 (format "%s %s with: " string (query-replace-descr from))
179 nil nil nil
bbe9f175 180 query-replace-to-history-variable from t t))
78629844
DK
181 regexp-flag))
182
1606466a
SM
183(defun query-replace-read-args (string regexp-flag &optional noerror)
184 (unless noerror
185 (barf-if-buffer-read-only))
186 (let* ((from (query-replace-read-from string regexp-flag))
6f1df6d9
SM
187 (to (if (consp from) (prog1 (cdr from) (setq from (car from)))
188 (query-replace-read-to from string regexp-flag))))
10784bac 189 (list from to current-prefix-arg)))
770970cb 190
47d72254 191(defun query-replace (from-string to-string &optional delimited start end)
da44e784
RM
192 "Replace some occurrences of FROM-STRING with TO-STRING.
193As each match is found, the user must type a character saying
194what to do with it. For directions, type \\[help-command] at that time.
195
7ef5c431
KH
196In Transient Mark mode, if the mark is active, operate on the contents
197of the region. Otherwise, operate from point to the end of the buffer.
198
151270f3
RS
199If `query-replace-interactive' is non-nil, the last incremental search
200string is used as FROM-STRING--you don't have to specify it with the
201minibuffer.
202
446d9629
RS
203Matching is independent of case if `case-fold-search' is non-nil and
204FROM-STRING has no uppercase letters. Replacement transfers the case
205pattern of the old text to the new text, if `case-replace' and
206`case-fold-search' are non-nil and FROM-STRING has no uppercase
207letters. \(Transferring the case pattern means that if the old text
208matched is all caps, or capitalized, then its replacement is upcased
209or capitalized.)
9b0bf2b6 210
118a01c9 211Third arg DELIMITED (prefix arg if interactive), if non-nil, means replace
81bdc14d 212only matches surrounded by word boundaries.
47d72254 213Fourth and fifth arg START and END specify the region to operate on.
81bdc14d
RS
214
215To customize possible responses, change the \"bindings\" in `query-replace-map'."
10784bac
RS
216 (interactive (let ((common
217 (query-replace-read-args "Query replace" nil)))
218 (list (nth 0 common) (nth 1 common) (nth 2 common)
219 ;; These are done separately here
220 ;; so that command-history will record these expressions
221 ;; rather than the values they had this time.
222 (if (and transient-mark-mode mark-active)
223 (region-beginning))
224 (if (and transient-mark-mode mark-active)
225 (region-end)))))
99a7559f 226 (perform-replace from-string to-string t nil delimited nil nil start end))
7ef5c431 227
73fa8346 228(define-key esc-map "%" 'query-replace)
da44e784 229
47d72254 230(defun query-replace-regexp (regexp to-string &optional delimited start end)
da44e784
RM
231 "Replace some things after point matching REGEXP with TO-STRING.
232As each match is found, the user must type a character saying
233what to do with it. For directions, type \\[help-command] at that time.
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
151270f3
RS
238If `query-replace-interactive' is non-nil, the last incremental search
239regexp is used as REGEXP--you don't have to specify it with the
240minibuffer.
241
446d9629
RS
242Matching is independent of case if `case-fold-search' is non-nil and
243REGEXP has no uppercase letters. Replacement transfers the case
244pattern of the old text to the new text, if `case-replace' and
245`case-fold-search' are non-nil and REGEXP has no uppercase letters.
246\(Transferring the case pattern means that if the old text matched is
247all caps, or capitalized, then its replacement is upcased or
248capitalized.)
47d72254 249
118a01c9 250Third arg DELIMITED (prefix arg if interactive), if non-nil, means replace
da44e784 251only matches surrounded by word boundaries.
47d72254
GM
252Fourth and fifth arg START and END specify the region to operate on.
253
118a01c9
RS
254In TO-STRING, `\\&' stands for whatever matched the whole of REGEXP,
255and `\\=\\N' (where N is a digit) stands for
2f57bf85 256whatever what matched the Nth `\\(...\\)' in REGEXP.
7c1c02ac
DK
257`\\?' lets you edit the replacement text in the minibuffer
258at the given position for each replacement.
259
ba8d15f9
RS
260In interactive calls, the replacement text can contain `\\,'
261followed by a Lisp expression. Each
262replacement evaluates that expression to compute the replacement
263string. Inside of that expression, `\\&' is a string denoting the
f1f6079c 264whole match as a string, `\\N' for a partial match, `\\#&' and `\\#N'
ba8d15f9
RS
265for the whole or a partial match converted to a number with
266`string-to-number', and `\\#' itself for the number of replacements
267done so far (starting with zero).
7c1c02ac 268
ba8d15f9
RS
269If the replacement expression is a symbol, write a space after it
270to terminate it. One space there, if any, will be discarded.
7c1c02ac
DK
271
272When using those Lisp features interactively in the replacement
273text, TO-STRING is actually made a list instead of a string.
274Use \\[repeat-complex-command] after this command for details."
10784bac
RS
275 (interactive
276 (let ((common
277 (query-replace-read-args "Query replace regexp" t)))
7c1c02ac
DK
278 (list (nth 0 common) (nth 1 common) (nth 2 common)
279 ;; These are done separately here
280 ;; so that command-history will record these expressions
281 ;; rather than the values they had this time.
282 (if (and transient-mark-mode mark-active)
283 (region-beginning))
284 (if (and transient-mark-mode mark-active)
285 (region-end)))))
99a7559f 286 (perform-replace regexp to-string t t delimited nil nil start end))
2f57bf85 287
cbc127de 288(define-key esc-map [?\C-%] 'query-replace-regexp)
da44e784 289
47d72254 290(defun query-replace-regexp-eval (regexp to-expr &optional delimited start end)
84482eb3
RS
291 "Replace some things after point matching REGEXP with the result of TO-EXPR.
292As each match is found, the user must type a character saying
293what to do with it. For directions, type \\[help-command] at that time.
294
295TO-EXPR is a Lisp expression evaluated to compute each replacement. It may
296reference `replace-count' to get the number of replacements already made.
297If the result of TO-EXPR is not a string, it is converted to one using
298`prin1-to-string' with the NOESCAPE argument (which see).
299
300For convenience, when entering TO-EXPR interactively, you can use `\\&' or
653479ad
AS
301`\\0' to stand for whatever matched the whole of REGEXP, and `\\N' (where
302N is a digit) to stand for whatever matched the Nth `\\(...\\)' in REGEXP.
84482eb3 303Use `\\#&' or `\\#N' if you want a number instead of a string.
2f57bf85 304In interactive use, `\\#' in itself stands for `replace-count'.
84482eb3
RS
305
306In Transient Mark mode, if the mark is active, operate on the contents
307of the region. Otherwise, operate from point to the end of the buffer.
308
309If `query-replace-interactive' is non-nil, the last incremental search
310regexp is used as REGEXP--you don't have to specify it with the
311minibuffer.
312
313Preserves case in each replacement if `case-replace' and `case-fold-search'
314are non-nil and REGEXP has no uppercase letters.
47d72254 315
84482eb3 316Third arg DELIMITED (prefix arg if interactive), if non-nil, means replace
470bbe9b 317only matches that are surrounded by word boundaries.
47d72254 318Fourth and fifth arg START and END specify the region to operate on."
84482eb3 319 (interactive
cc0aea1a 320 (progn
6f1df6d9
SM
321 (barf-if-buffer-read-only)
322 (let* ((from
323 ;; Let-bind the history var to disable the "foo -> bar" default.
324 ;; Maybe we shouldn't disable this default, but for now I'll
325 ;; leave it off. --Stef
326 (let ((query-replace-to-history-variable nil))
327 (query-replace-read-from "Query replace regexp" t)))
5291cbca 328 (to (list (read-from-minibuffer
6f1df6d9
SM
329 (format "Query replace regexp %s with eval: "
330 (query-replace-descr from))
5291cbca 331 nil nil t query-replace-to-history-variable from t))))
84482eb3
RS
332 ;; We make TO a list because replace-match-string-symbols requires one,
333 ;; and the user might enter a single token.
334 (replace-match-string-symbols to)
10784bac
RS
335 (list from (car to) current-prefix-arg
336 (if (and transient-mark-mode mark-active)
337 (region-beginning))
338 (if (and transient-mark-mode mark-active)
cc0aea1a 339 (region-end))))))
d2ce3151 340 (perform-replace regexp (cons 'replace-eval-replacement to-expr)
d83a97ab 341 t 'literal delimited nil nil start end))
84482eb3 342
47d72254 343(defun map-query-replace-regexp (regexp to-strings &optional n start end)
da44e784 344 "Replace some matches for REGEXP with various strings, in rotation.
e730be7f
DL
345The second argument TO-STRINGS contains the replacement strings,
346separated by spaces. Third arg DELIMITED (prefix arg if interactive),
347if non-nil, means replace only matches surrounded by word boundaries.
348This command works like `query-replace-regexp' except that each
349successive replacement uses the next successive replacement string,
da44e784
RM
350wrapping around from the last such string to the first.
351
7ef5c431
KH
352In Transient Mark mode, if the mark is active, operate on the contents
353of the region. Otherwise, operate from point to the end of the buffer.
354
da44e784
RM
355Non-interactively, TO-STRINGS may be a list of replacement strings.
356
151270f3
RS
357If `query-replace-interactive' is non-nil, the last incremental search
358regexp is used as REGEXP--you don't have to specify it with the minibuffer.
359
da44e784 360A prefix argument N says to use each replacement string N times
47d72254
GM
361before rotating to the next.
362Fourth and fifth arg START and END specify the region to operate on."
770970cb 363 (interactive
5291cbca 364 (let* ((from (if query-replace-interactive
151270f3
RS
365 (car regexp-search-ring)
366 (read-from-minibuffer "Map query replace (regexp): "
367 nil nil nil
fce31d51 368 'query-replace-history nil t)))
5291cbca 369 (to (read-from-minibuffer
770970cb 370 (format "Query replace %s with (space-separated strings): "
6f1df6d9 371 (query-replace-descr from))
770970cb 372 nil nil nil
5291cbca 373 'query-replace-history from t)))
2f2f7e58
RS
374 (list from to
375 (and current-prefix-arg
376 (prefix-numeric-value current-prefix-arg))
10784bac
RS
377 (if (and transient-mark-mode mark-active)
378 (region-beginning))
379 (if (and transient-mark-mode mark-active)
380 (region-end)))))
da44e784
RM
381 (let (replacements)
382 (if (listp to-strings)
383 (setq replacements to-strings)
384 (while (/= (length to-strings) 0)
385 (if (string-match " " to-strings)
386 (setq replacements
387 (append replacements
388 (list (substring to-strings 0
389 (string-match " " to-strings))))
390 to-strings (substring to-strings
391 (1+ (string-match " " to-strings))))
392 (setq replacements (append replacements (list to-strings))
393 to-strings ""))))
99a7559f 394 (perform-replace regexp replacements t t nil n nil start end)))
da44e784 395
47d72254 396(defun replace-string (from-string to-string &optional delimited start end)
da44e784
RM
397 "Replace occurrences of FROM-STRING with TO-STRING.
398Preserve case in each match if `case-replace' and `case-fold-search'
399are non-nil and FROM-STRING has no uppercase letters.
9b0bf2b6
RS
400\(Preserving case means that if the string matched is all caps, or capitalized,
401then its replacement is upcased or capitalized.)
402
7ef5c431
KH
403In Transient Mark mode, if the mark is active, operate on the contents
404of the region. Otherwise, operate from point to the end of the buffer.
405
118a01c9 406Third arg DELIMITED (prefix arg if interactive), if non-nil, means replace
da44e784 407only matches surrounded by word boundaries.
47d72254 408Fourth and fifth arg START and END specify the region to operate on.
da44e784 409
151270f3
RS
410If `query-replace-interactive' is non-nil, the last incremental search
411string is used as FROM-STRING--you don't have to specify it with the
412minibuffer.
413
da44e784
RM
414This function is usually the wrong thing to use in a Lisp program.
415What you probably want is a loop like this:
118a01c9
RS
416 (while (search-forward FROM-STRING nil t)
417 (replace-match TO-STRING nil t))
87532fbe
RS
418which will run faster and will not set the mark or print anything.
419\(You may need a more complex loop if FROM-STRING can match the null string
420and TO-STRING is also null.)"
10784bac
RS
421 (interactive
422 (let ((common
423 (query-replace-read-args "Replace string" nil)))
424 (list (nth 0 common) (nth 1 common) (nth 2 common)
425 (if (and transient-mark-mode mark-active)
426 (region-beginning))
427 (if (and transient-mark-mode mark-active)
428 (region-end)))))
99a7559f 429 (perform-replace from-string to-string nil nil delimited nil nil start end))
da44e784 430
47d72254 431(defun replace-regexp (regexp to-string &optional delimited start end)
da44e784 432 "Replace things after point matching REGEXP with TO-STRING.
118a01c9 433Preserve case in each match if `case-replace' and `case-fold-search'
da44e784 434are non-nil and REGEXP has no uppercase letters.
47d72254
GM
435
436In Transient Mark mode, if the mark is active, operate on the contents
437of the region. Otherwise, operate from point to the end of the buffer.
438
118a01c9 439Third arg DELIMITED (prefix arg if interactive), if non-nil, means replace
da44e784 440only matches surrounded by word boundaries.
47d72254
GM
441Fourth and fifth arg START and END specify the region to operate on.
442
118a01c9
RS
443In TO-STRING, `\\&' stands for whatever matched the whole of REGEXP,
444and `\\=\\N' (where N is a digit) stands for
7c1c02ac
DK
445whatever what matched the Nth `\\(...\\)' in REGEXP.
446`\\?' lets you edit the replacement text in the minibuffer
447at the given position for each replacement.
448
449In interactive calls, the replacement text may contain `\\,'
450followed by a Lisp expression used as part of the replacement
451text. Inside of that expression, `\\&' is a string denoting the
452whole match, `\\N' a partial matches, `\\#&' and `\\#N' the
453respective numeric values from `string-to-number', and `\\#'
454itself for `replace-count', the number of replacements occured so
455far.
456
457If your Lisp expression is an identifier and the next letter in
458the replacement string would be interpreted as part of it, you
459can wrap it with an expression like `\\,(or \\#)'. Incidentally,
460for this particular case you may also enter `\\#' in the
461replacement text directly.
462
463When using those Lisp features interactively in the replacement
464text, TO-STRING is actually made a list instead of a string.
465Use \\[repeat-complex-command] after this command for details.
da44e784 466
151270f3
RS
467If `query-replace-interactive' is non-nil, the last incremental search
468regexp is used as REGEXP--you don't have to specify it with the minibuffer.
469
da44e784
RM
470This function is usually the wrong thing to use in a Lisp program.
471What you probably want is a loop like this:
472 (while (re-search-forward REGEXP nil t)
118a01c9 473 (replace-match TO-STRING nil nil))
da44e784 474which will run faster and will not set the mark or print anything."
10784bac
RS
475 (interactive
476 (let ((common
477 (query-replace-read-args "Replace regexp" t)))
478 (list (nth 0 common) (nth 1 common) (nth 2 common)
479 (if (and transient-mark-mode mark-active)
480 (region-beginning))
481 (if (and transient-mark-mode mark-active)
482 (region-end)))))
99a7559f 483 (perform-replace regexp to-string nil t delimited nil nil start end))
e32eb3e6 484
4c53bd2b
RS
485\f
486(defvar regexp-history nil
487 "History list for some commands that read regular expressions.")
da44e784 488
e32eb3e6 489
31e1d920 490(defalias 'delete-non-matching-lines 'keep-lines)
e32eb3e6
GM
491(defalias 'delete-matching-lines 'flush-lines)
492(defalias 'count-matches 'how-many)
493
494
495(defun keep-lines-read-args (prompt)
496 "Read arguments for `keep-lines' and friends.
497Prompt for a regexp with PROMPT.
2ced751f
RS
498Value is a list, (REGEXP)."
499 (list (read-from-minibuffer prompt nil nil nil
500 'regexp-history nil t)))
e32eb3e6
GM
501
502(defun keep-lines (regexp &optional rstart rend)
698e1804
RS
503 "Delete all lines except those containing matches for REGEXP.
504A match split across lines preserves all the lines it lies in.
d2a0ee8b
RS
505Applies to all lines after point.
506
507If REGEXP contains upper case characters (excluding those preceded by `\\'),
e32eb3e6
GM
508the matching is case-sensitive.
509
510Second and third arg RSTART and REND specify the region to operate on.
511
2ced751f
RS
512Interactively, in Transient Mark mode when the mark is active, operate
513on the contents of the region. Otherwise, operate from point to the
514end of the buffer."
515
e32eb3e6 516 (interactive
98faf1bb
RS
517 (progn
518 (barf-if-buffer-read-only)
519 (keep-lines-read-args "Keep lines (containing match for regexp): ")))
e32eb3e6 520 (if rstart
119831da
RS
521 (progn
522 (goto-char (min rstart rend))
523 (setq rend (copy-marker (max rstart rend))))
2ced751f
RS
524 (if (and transient-mark-mode mark-active)
525 (setq rstart (region-beginning)
526 rend (copy-marker (region-end)))
527 (setq rstart (point)
528 rend (point-max-marker)))
529 (goto-char rstart))
698e1804
RS
530 (save-excursion
531 (or (bolp) (forward-line 1))
d2a0ee8b
RS
532 (let ((start (point))
533 (case-fold-search (and case-fold-search
534 (isearch-no-upper-case-p regexp t))))
e32eb3e6 535 (while (< (point) rend)
698e1804 536 ;; Start is first char not preserved by previous match.
e32eb3e6
GM
537 (if (not (re-search-forward regexp rend 'move))
538 (delete-region start rend)
698e1804
RS
539 (let ((end (save-excursion (goto-char (match-beginning 0))
540 (beginning-of-line)
541 (point))))
542 ;; Now end is first char preserved by the new match.
543 (if (< start end)
544 (delete-region start end))))
d99118b0 545
e32eb3e6 546 (setq start (save-excursion (forward-line 1) (point)))
698e1804 547 ;; If the match was empty, avoid matching again at same place.
e32eb3e6
GM
548 (and (< (point) rend)
549 (= (match-beginning 0) (match-end 0))
698e1804
RS
550 (forward-char 1))))))
551
e32eb3e6
GM
552
553(defun flush-lines (regexp &optional rstart rend)
698e1804
RS
554 "Delete lines containing matches for REGEXP.
555If a match is split across lines, all the lines it lies in are deleted.
d2a0ee8b
RS
556Applies to lines after point.
557
558If REGEXP contains upper case characters (excluding those preceded by `\\'),
e32eb3e6
GM
559the matching is case-sensitive.
560
561Second and third arg RSTART and REND specify the region to operate on.
562
2ced751f
RS
563Interactively, in Transient Mark mode when the mark is active, operate
564on the contents of the region. Otherwise, operate from point to the
565end of the buffer."
566
e32eb3e6 567 (interactive
98faf1bb
RS
568 (progn
569 (barf-if-buffer-read-only)
570 (keep-lines-read-args "Flush lines (containing match for regexp): ")))
e32eb3e6 571 (if rstart
119831da
RS
572 (progn
573 (goto-char (min rstart rend))
574 (setq rend (copy-marker (max rstart rend))))
2ced751f
RS
575 (if (and transient-mark-mode mark-active)
576 (setq rstart (region-beginning)
577 rend (copy-marker (region-end)))
578 (setq rstart (point)
579 rend (point-max-marker)))
580 (goto-char rstart))
d2a0ee8b
RS
581 (let ((case-fold-search (and case-fold-search
582 (isearch-no-upper-case-p regexp t))))
583 (save-excursion
e32eb3e6
GM
584 (while (and (< (point) rend)
585 (re-search-forward regexp rend t))
d2a0ee8b
RS
586 (delete-region (save-excursion (goto-char (match-beginning 0))
587 (beginning-of-line)
588 (point))
589 (progn (forward-line 1) (point)))))))
698e1804 590
e32eb3e6
GM
591
592(defun how-many (regexp &optional rstart rend)
d2a0ee8b
RS
593 "Print number of matches for REGEXP following point.
594
595If REGEXP contains upper case characters (excluding those preceded by `\\'),
e32eb3e6
GM
596the matching is case-sensitive.
597
598Second and third arg RSTART and REND specify the region to operate on.
599
2ced751f
RS
600Interactively, in Transient Mark mode when the mark is active, operate
601on the contents of the region. Otherwise, operate from point to the
602end of the buffer."
603
e32eb3e6
GM
604 (interactive
605 (keep-lines-read-args "How many matches for (regexp): "))
f601efb0
SM
606 (save-excursion
607 (if rstart
608 (goto-char (min rstart rend))
2ced751f
RS
609 (if (and transient-mark-mode mark-active)
610 (setq rstart (region-beginning)
611 rend (copy-marker (region-end)))
612 (setq rstart (point)
613 rend (point-max-marker)))
614 (goto-char rstart))
f601efb0
SM
615 (let ((count 0)
616 opoint
617 (case-fold-search (and case-fold-search
618 (isearch-no-upper-case-p regexp t))))
619 (while (and (< (point) rend)
620 (progn (setq opoint (point))
621 (re-search-forward regexp rend t)))
622 (if (= opoint (point))
623 (forward-char 1)
624 (setq count (1+ count))))
625 (message "%d occurrences" count))))
e32eb3e6 626
4c53bd2b 627\f
f601efb0
SM
628(defvar occur-mode-map
629 (let ((map (make-sparse-keymap)))
630 (define-key map [mouse-2] 'occur-mode-mouse-goto)
631 (define-key map "\C-c\C-c" 'occur-mode-goto-occurrence)
632 (define-key map "\C-m" 'occur-mode-goto-occurrence)
daae70bf 633 (define-key map "o" 'occur-mode-goto-occurrence-other-window)
365486d6 634 (define-key map "\C-o" 'occur-mode-display-occurrence)
f601efb0
SM
635 (define-key map "\M-n" 'occur-next)
636 (define-key map "\M-p" 'occur-prev)
d99118b0
SS
637 (define-key map "r" 'occur-rename-buffer)
638 (define-key map "c" 'clone-buffer)
f601efb0 639 (define-key map "g" 'revert-buffer)
d99118b0
SS
640 (define-key map "q" 'quit-window)
641 (define-key map "z" 'kill-this-buffer)
b938735a 642 (define-key map "\C-c\C-f" 'next-error-follow-minor-mode)
f601efb0
SM
643 map)
644 "Keymap for `occur-mode'.")
698e1804 645
46b3d18e
RS
646(defvar occur-revert-arguments nil
647 "Arguments to pass to `occur-1' to revert an Occur mode buffer.
648See `occur-revert-function'.")
698e1804 649
c9ae8cbb
JB
650(defcustom occur-mode-hook '(turn-on-font-lock)
651 "Hook run when entering Occur mode."
652 :type 'hook
653 :group 'matching)
654
655(defcustom occur-hook nil
656 "Hook run when `occur' is called."
daae70bf
CW
657 :type 'hook
658 :group 'matching)
659
de3c9b09 660(put 'occur-mode 'mode-class 'special)
505847d4 661(defun occur-mode ()
698e1804 662 "Major mode for output from \\[occur].
0081c8a1
RS
663\\<occur-mode-map>Move point to one of the items in this buffer, then use
664\\[occur-mode-goto-occurrence] to go to the occurrence that the item refers to.
665Alternatively, click \\[occur-mode-mouse-goto] on an item to go to it.
666
698e1804 667\\{occur-mode-map}"
ce8b7b9f 668 (interactive)
505847d4
RS
669 (kill-all-local-variables)
670 (use-local-map occur-mode-map)
671 (setq major-mode 'occur-mode)
672 (setq mode-name "Occur")
f601efb0 673 (set (make-local-variable 'revert-buffer-function) 'occur-revert-function)
c9ae8cbb 674 (make-local-variable 'occur-revert-arguments)
fbefbe33 675 (add-hook 'change-major-mode-hook 'font-lock-defontify nil t)
423e4de7 676 (setq next-error-function 'occur-next-error)
c9ae8cbb 677 (run-hooks 'occur-mode-hook))
698e1804 678
a41284da 679(defun occur-revert-function (ignore1 ignore2)
46b3d18e 680 "Handle `revert-buffer' for Occur mode buffers."
e1690783 681 (apply 'occur-1 (append occur-revert-arguments (list (buffer-name)))))
a41284da 682
78bead73
RS
683(defun occur-mode-mouse-goto (event)
684 "In Occur mode, go to the occurrence whose line you click on."
685 (interactive "e")
46b3d18e 686 (let (pos)
78bead73
RS
687 (save-excursion
688 (set-buffer (window-buffer (posn-window (event-end event))))
689 (save-excursion
690 (goto-char (posn-point (event-end event)))
46b3d18e
RS
691 (setq pos (occur-mode-find-occurrence))))
692 (pop-to-buffer (marker-buffer pos))
693 (goto-char pos)))
78bead73
RS
694
695(defun occur-mode-find-occurrence ()
46b3d18e
RS
696 (let ((pos (get-text-property (point) 'occur-target)))
697 (unless pos
68608d9c 698 (error "No occurrence on this line"))
46b3d18e
RS
699 (unless (buffer-live-p (marker-buffer pos))
700 (error "Buffer for this occurrence was killed"))
701 pos))
78bead73
RS
702
703(defun occur-mode-goto-occurrence ()
704 "Go to the occurrence the current line describes."
705 (interactive)
46b3d18e
RS
706 (let ((pos (occur-mode-find-occurrence)))
707 (pop-to-buffer (marker-buffer pos))
708 (goto-char pos)))
8d15583f 709
029024e2
RS
710(defun occur-mode-goto-occurrence-other-window ()
711 "Go to the occurrence the current line describes, in another window."
712 (interactive)
46b3d18e
RS
713 (let ((pos (occur-mode-find-occurrence)))
714 (switch-to-buffer-other-window (marker-buffer pos))
715 (goto-char pos)))
029024e2 716
365486d6
RS
717(defun occur-mode-display-occurrence ()
718 "Display in another window the occurrence the current line describes."
719 (interactive)
46b3d18e
RS
720 (let ((pos (occur-mode-find-occurrence))
721 window
722 ;; Bind these to ensure `display-buffer' puts it in another window.
365486d6 723 same-window-buffer-names
46b3d18e
RS
724 same-window-regexps)
725 (setq window (display-buffer (marker-buffer pos)))
365486d6
RS
726 ;; This is the way to set point in the proper window.
727 (save-selected-window
728 (select-window window)
46b3d18e 729 (goto-char pos))))
365486d6 730
123d5548 731(defun occur-find-match (n search message)
8d15583f
RS
732 (if (not n) (setq n 1))
733 (let ((r))
734 (while (> n 0)
123d5548
JB
735 (setq r (funcall search (point) 'occur-match))
736 (and r
737 (get-text-property r 'occur-match)
738 (setq r (funcall search r 'occur-match)))
8d15583f 739 (if r
123d5548
JB
740 (goto-char r)
741 (error message))
8d15583f
RS
742 (setq n (1- n)))))
743
123d5548
JB
744(defun occur-next (&optional n)
745 "Move to the Nth (default 1) next match in an Occur mode buffer."
746 (interactive "p")
747 (occur-find-match n #'next-single-property-change "No more matches"))
748
8d15583f 749(defun occur-prev (&optional n)
46b3d18e 750 "Move to the Nth (default 1) previous match in an Occur mode buffer."
8d15583f 751 (interactive "p")
123d5548 752 (occur-find-match n #'previous-single-property-change "No earlier matches"))
423e4de7
KS
753
754(defun occur-next-error (&optional argp reset)
755 "Move to the Nth (default 1) next match in an Occur mode buffer.
756Compatibility function for \\[next-error] invocations."
757 (interactive "p")
5f9e0ca5 758 ;; we need to run occur-find-match from within the Occur buffer
f1f007dc 759 (with-current-buffer
f42a241b 760 ;; Choose the buffer and make it current.
5f9e0ca5
TZ
761 (if (next-error-buffer-p (current-buffer))
762 (current-buffer)
f42a241b
RS
763 (next-error-find-buffer nil nil
764 (lambda ()
765 (eq major-mode 'occur-mode))))
f1f007dc
JL
766
767 (goto-char (cond (reset (point-min))
768 ((< argp 0) (line-beginning-position))
769 ((line-end-position))))
5f9e0ca5 770 (occur-find-match
f1f007dc
JL
771 (abs argp)
772 (if (> 0 argp)
5f9e0ca5
TZ
773 #'previous-single-property-change
774 #'next-single-property-change)
775 "No more matches")
776 ;; In case the *Occur* buffer is visible in a nonselected window.
777 (set-window-point (get-buffer-window (current-buffer)) (point))
778 (occur-mode-goto-occurrence)))
4c53bd2b 779\f
aaaecfcd
JL
780(defface match
781 '((((class color) (min-colors 88) (background light))
782 :background "Tan")
783 (((class color) (min-colors 88) (background dark))
784 :background "RoyalBlue4")
785 (((class color) (min-colors 8))
786 :background "blue" :foreground "white")
787 (((type tty) (class mono))
788 :inverse-video t)
789 (t :background "gray"))
790 "Face used to highlight matches permanently."
791 :group 'matching
792 :version "21.4")
793
9d325ebf 794(defcustom list-matching-lines-default-context-lines 0
e730be7f
DL
795 "*Default number of context lines included around `list-matching-lines' matches.
796A negative number means to include that many lines before the match.
9d325ebf
RS
797A positive number means to include that many lines both before and after."
798 :type 'integer
799 :group 'matching)
698e1804 800
31e1d920 801(defalias 'list-matching-lines 'occur)
698e1804 802
aaaecfcd 803(defcustom list-matching-lines-face 'match
e730be7f 804 "*Face used by \\[list-matching-lines] to show the text that matches.
68608d9c
CW
805If the value is nil, don't highlight the matching portions specially."
806 :type 'face
807 :group 'matching)
808
809(defcustom list-matching-lines-buffer-name-face 'underline
810 "*Face used by \\[list-matching-lines] to show the names of buffers.
811If the value is nil, don't highlight the buffer names specially."
812 :type 'face
813 :group 'matching)
814
2f4e3f7a 815(defun occur-accumulate-lines (count &optional keep-props)
68608d9c
CW
816 (save-excursion
817 (let ((forwardp (> count 0))
f1f007dc 818 result beg end)
68608d9c
CW
819 (while (not (or (zerop count)
820 (if forwardp
821 (eobp)
822 (bobp))))
b0523087 823 (setq count (+ count (if forwardp -1 1)))
f1f007dc
JL
824 (setq beg (line-beginning-position)
825 end (line-end-position))
99769096 826 (if (and keep-props (if (boundp 'jit-lock-mode) jit-lock-mode)
f1f007dc 827 (text-property-not-all beg end 'fontified t))
99769096
RS
828 (if (fboundp 'jit-lock-fontify-now)
829 (jit-lock-fontify-now beg end)))
68608d9c 830 (push
2f4e3f7a
RS
831 (funcall (if keep-props
832 #'buffer-substring
833 #'buffer-substring-no-properties)
f1f007dc 834 beg end)
68608d9c
CW
835 result)
836 (forward-line (if forwardp 1 -1)))
837 (nreverse result))))
838
839(defun occur-read-primary-args ()
840 (list (let* ((default (car regexp-history))
841 (input
842 (read-from-minibuffer
843 (if default
844 (format "List lines matching regexp (default `%s'): "
6f1df6d9 845 (query-replace-descr default))
68608d9c
CW
846 "List lines matching regexp: ")
847 nil
848 nil
849 nil
442a68c7
RS
850 'regexp-history
851 default)))
68608d9c
CW
852 (if (equal input "")
853 default
854 input))
bda1925e
CW
855 (when current-prefix-arg
856 (prefix-numeric-value current-prefix-arg))))
c9daced0 857
d99118b0
SS
858(defun occur-rename-buffer (&optional unique-p)
859 "Rename the current *Occur* buffer to *Occur: original-buffer-name*.
860Here `original-buffer-name' is the buffer name were occur was originally run.
861When given the prefix argument, the renaming will not clobber the existing
862buffer(s) of that name, but use `generate-new-buffer-name' instead.
2fb79329 863You can add this to `occur-hook' if you always want a separate *Occur*
d99118b0
SS
864buffer for each buffer where you invoke `occur'."
865 (interactive "P")
866 (with-current-buffer
867 (if (eq major-mode 'occur-mode) (current-buffer) (get-buffer "*Occur*"))
868 (rename-buffer (concat "*Occur: "
869 (mapconcat #'buffer-name
870 (car (cddr occur-revert-arguments)) "/")
871 "*")
872 unique-p)))
873
698e1804 874(defun occur (regexp &optional nlines)
99976f85 875 "Show all lines in the current buffer containing a match for REGEXP.
da44e784
RM
876
877If a match spreads across multiple lines, all those lines are shown.
698e1804 878
da44e784
RM
879Each line is displayed with NLINES lines before and after, or -NLINES
880before if NLINES is negative.
881NLINES defaults to `list-matching-lines-default-context-lines'.
698e1804
RS
882Interactively it is the prefix arg.
883
4c53bd2b 884The lines are shown in a buffer named `*Occur*'.
698e1804 885It serves as a menu to find any of the occurrences in this buffer.
de3c9b09 886\\<occur-mode-map>\\[describe-mode] in that buffer will explain how.
9483d601 887
de3c9b09
RS
888If REGEXP contains upper case characters (excluding those preceded by `\\'),
889the matching is case-sensitive."
68608d9c
CW
890 (interactive (occur-read-primary-args))
891 (occur-1 regexp nlines (list (current-buffer))))
892
893(defun multi-occur (bufs regexp &optional nlines)
894 "Show all lines in buffers BUFS containing a match for REGEXP.
895This function acts on multiple buffers; otherwise, it is exactly like
896`occur'."
a5dfed3e 897 (interactive
68608d9c 898 (cons
52698d45
KS
899 (let* ((bufs (list (read-buffer "First buffer to search: "
900 (current-buffer) t)))
901 (buf nil)
902 (ido-ignore-item-temp-list bufs))
68608d9c 903 (while (not (string-equal
f1180544 904 (setq buf (read-buffer
52698d45
KS
905 (if (eq read-buffer-function 'ido-read-buffer)
906 "Next buffer to search (C-j to end): "
907 "Next buffer to search (RET to end): ")
908 nil t))
68608d9c 909 ""))
52698d45
KS
910 (add-to-list 'bufs buf)
911 (setq ido-ignore-item-temp-list bufs))
68608d9c
CW
912 (nreverse (mapcar #'get-buffer bufs)))
913 (occur-read-primary-args)))
914 (occur-1 regexp nlines bufs))
915
916(defun multi-occur-by-filename-regexp (bufregexp regexp &optional nlines)
04fe158a 917 "Show all lines matching REGEXP in buffers named by BUFREGEXP.
68608d9c
CW
918See also `multi-occur'."
919 (interactive
920 (cons
921 (let* ((default (car regexp-history))
922 (input
923 (read-from-minibuffer
924 "List lines in buffers whose filename matches regexp: "
925 nil
926 nil
927 nil
928 'regexp-history)))
929 (if (equal input "")
930 default
931 input))
932 (occur-read-primary-args)))
933 (when bufregexp
934 (occur-1 regexp nlines
935 (delq nil
936 (mapcar (lambda (buf)
937 (when (and (buffer-file-name buf)
938 (string-match bufregexp
939 (buffer-file-name buf)))
940 buf))
941 (buffer-list))))))
942
e1690783
CW
943(defun occur-1 (regexp nlines bufs &optional buf-name)
944 (unless buf-name
945 (setq buf-name "*Occur*"))
f42a241b 946 (let (occur-buf
70ed2a76
CW
947 (active-bufs (delq nil (mapcar #'(lambda (buf)
948 (when (buffer-live-p buf) buf))
949 bufs))))
950 ;; Handle the case where one of the buffers we're searching is the
f42a241b
RS
951 ;; output buffer. Just rename it.
952 (when (member buf-name (mapcar 'buffer-name active-bufs))
953 (with-current-buffer (get-buffer buf-name)
954 (rename-uniquely)))
955
956 ;; Now find or create the output buffer.
957 ;; If we just renamed that buffer, we will make a new one here.
958 (setq occur-buf (get-buffer-create buf-name))
959
68608d9c
CW
960 (with-current-buffer occur-buf
961 (setq buffer-read-only nil)
962 (occur-mode)
963 (erase-buffer)
964 (let ((count (occur-engine
70ed2a76 965 regexp active-bufs occur-buf
68608d9c
CW
966 (or nlines list-matching-lines-default-context-lines)
967 (and case-fold-search
968 (isearch-no-upper-case-p regexp t))
506a6d7e 969 list-matching-lines-buffer-name-face
2f4e3f7a 970 nil list-matching-lines-face t)))
b2043e7b
AS
971 (let* ((bufcount (length active-bufs))
972 (diff (- (length bufs) bufcount)))
973 (message "Searched %d buffer%s%s; %s match%s for `%s'"
974 bufcount (if (= bufcount 1) "" "s")
975 (if (zerop diff) "" (format " (%d killed)" diff))
976 (if (zerop count) "no" (format "%d" count))
977 (if (= count 1) "" "es")
978 regexp))
70ed2a76
CW
979 (setq occur-revert-arguments (list regexp nlines bufs)
980 buffer-read-only t)
68608d9c 981 (if (> count 0)
423e4de7
KS
982 (progn
983 (display-buffer occur-buf)
984 (setq next-error-last-buffer occur-buf))
2fb79329
JB
985 (kill-buffer occur-buf)))
986 (run-hooks 'occur-hook))))
68608d9c 987
46b3d18e
RS
988(defun occur-engine-add-prefix (lines)
989 (mapcar
990 #'(lambda (line)
261cca88 991 (concat " :" line "\n"))
46b3d18e
RS
992 lines))
993
994(defun occur-engine (regexp buffers out-buf nlines case-fold-search
995 title-face prefix-face match-face keep-props)
996 (with-current-buffer out-buf
997 (setq buffer-read-only nil)
5cb4031d
KH
998 (let ((globalcount 0)
999 (coding nil))
46b3d18e
RS
1000 ;; Map over all the buffers
1001 (dolist (buf buffers)
1002 (when (buffer-live-p buf)
1003 (let ((matches 0) ;; count of matched lines
1004 (lines 1) ;; line count
1005 (matchbeg 0)
46b3d18e
RS
1006 (origpt nil)
1007 (begpt nil)
1008 (endpt nil)
1009 (marker nil)
1010 (curstring "")
1011 (headerpt (with-current-buffer out-buf (point))))
1012 (save-excursion
1013 (set-buffer buf)
5cb4031d
KH
1014 (or coding
1015 ;; Set CODING only if the current buffer locally
1016 ;; binds buffer-file-coding-system.
1017 (not (local-variable-p 'buffer-file-coding-system))
1018 (setq coding buffer-file-coding-system))
68608d9c 1019 (save-excursion
46b3d18e
RS
1020 (goto-char (point-min)) ;; begin searching in the buffer
1021 (while (not (eobp))
1022 (setq origpt (point))
1023 (when (setq endpt (re-search-forward regexp nil t))
1024 (setq matches (1+ matches)) ;; increment match count
5291cbca 1025 (setq matchbeg (match-beginning 0))
46b3d18e 1026 (setq lines (+ lines (1- (count-lines origpt endpt))))
f1f007dc
JL
1027 (save-excursion
1028 (goto-char matchbeg)
1029 (setq begpt (line-beginning-position)
1030 endpt (line-end-position)))
46b3d18e
RS
1031 (setq marker (make-marker))
1032 (set-marker marker matchbeg)
99769096
RS
1033 (if (and keep-props
1034 (if (boundp 'jit-lock-mode) jit-lock-mode)
f1f007dc 1035 (text-property-not-all begpt endpt 'fontified t))
99769096
RS
1036 (if (fboundp 'jit-lock-fontify-now)
1037 (jit-lock-fontify-now begpt endpt)))
f1f007dc 1038 (setq curstring (buffer-substring begpt endpt))
46b3d18e
RS
1039 ;; Depropertize the string, and maybe
1040 ;; highlight the matches
1041 (let ((len (length curstring))
1042 (start 0))
1043 (unless keep-props
1044 (set-text-properties 0 len nil curstring))
1045 (while (and (< start len)
1046 (string-match regexp curstring start))
f1f007dc
JL
1047 (add-text-properties
1048 (match-beginning 0) (match-end 0)
1049 (append
1050 `(occur-match t)
1051 (when match-face
1052 ;; Use `face' rather than `font-lock-face' here
1053 ;; so as to override faces copied from the buffer.
1054 `(face ,match-face)))
1055 curstring)
46b3d18e
RS
1056 (setq start (match-end 0))))
1057 ;; Generate the string to insert for this match
1058 (let* ((out-line
1059 (concat
261cca88
RS
1060 ;; Using 7 digits aligns tabs properly.
1061 (apply #'propertize (format "%7d:" lines)
46b3d18e
RS
1062 (append
1063 (when prefix-face
506a6d7e 1064 `(font-lock-face prefix-face))
46b3d18e 1065 '(occur-prefix t)))
f1f007dc
JL
1066 ;; We don't put `mouse-face' on the newline,
1067 ;; because that loses. And don't put it
1068 ;; on context lines to reduce flicker.
1069 (propertize curstring 'mouse-face 'highlight)
46b3d18e
RS
1070 "\n"))
1071 (data
1072 (if (= nlines 0)
1073 ;; The simple display style
1074 out-line
1075 ;; The complex multi-line display
1076 ;; style. Generate a list of lines,
1077 ;; concatenate them all together.
1078 (apply #'concat
1079 (nconc
f22a7406 1080 (occur-engine-add-prefix (nreverse (cdr (occur-accumulate-lines (- (1+ (abs nlines))) keep-props))))
46b3d18e 1081 (list out-line)
f22a7406
RS
1082 (if (> nlines 0)
1083 (occur-engine-add-prefix
1084 (cdr (occur-accumulate-lines (1+ nlines) keep-props)))))))))
46b3d18e
RS
1085 ;; Actually insert the match display data
1086 (with-current-buffer out-buf
1087 (let ((beg (point))
1088 (end (progn (insert data) (point))))
1089 (unless (= nlines 0)
1090 (insert "-------\n"))
1091 (add-text-properties
e8e2a88c 1092 beg end
f1f007dc 1093 `(occur-target ,marker help-echo "mouse-2: go to this occurrence")))))
46b3d18e 1094 (goto-char endpt))
e1690783
CW
1095 (if endpt
1096 (progn
1097 (setq lines (1+ lines))
1098 ;; On to the next match...
1099 (forward-line 1))
1100 (goto-char (point-max))))))
46b3d18e 1101 (when (not (zerop matches)) ;; is the count zero?
daae70bf 1102 (setq globalcount (+ globalcount matches))
46b3d18e
RS
1103 (with-current-buffer out-buf
1104 (goto-char headerpt)
1105 (let ((beg (point))
1106 end)
dbed3cd3
JPW
1107 (insert (format "%d match%s for \"%s\" in buffer: %s\n"
1108 matches (if (= matches 1) "" "es")
1109 regexp (buffer-name buf)))
46b3d18e
RS
1110 (setq end (point))
1111 (add-text-properties beg end
1112 (append
1113 (when title-face
506a6d7e
CW
1114 `(font-lock-face ,title-face))
1115 `(occur-title ,buf))))
46b3d18e 1116 (goto-char (point-min)))))))
5cb4031d
KH
1117 (if coding
1118 ;; CODING is buffer-file-coding-system of the first buffer
1119 ;; that locally binds it. Let's use it also for the output
1120 ;; buffer.
1121 (set-buffer-file-coding-system coding))
46b3d18e
RS
1122 ;; Return the number of matches
1123 globalcount)))
68608d9c 1124
698e1804 1125\f
81bdc14d
RS
1126;; It would be nice to use \\[...], but there is no reasonable way
1127;; to make that display both SPC and Y.
698e1804
RS
1128(defconst query-replace-help
1129 "Type Space or `y' to replace one match, Delete or `n' to skip to next,
be44f62c 1130RET or `q' to exit, Period to replace one match and exit,
698e1804
RS
1131Comma to replace but not move point immediately,
1132C-r to enter recursive edit (\\[exit-recursive-edit] to get out again),
1133C-w to delete match and recursive edit,
1134C-l to clear the screen, redisplay, and offer same replacement again,
1135! to replace all remaining matches with no more questions,
7ce278f3
GM
1136^ to move point back to previous match,
1137E to edit the replacement string"
f54701d1 1138 "Help message while in `query-replace'.")
698e1804 1139
81bdc14d
RS
1140(defvar query-replace-map (make-sparse-keymap)
1141 "Keymap that defines the responses to questions in `query-replace'.
1142The \"bindings\" in this map are not commands; they are answers.
1143The valid answers include `act', `skip', `act-and-show',
1144`exit', `act-and-exit', `edit', `delete-and-edit', `recenter',
d9121bc0 1145`automatic', `backup', `exit-prefix', and `help'.")
81bdc14d
RS
1146
1147(define-key query-replace-map " " 'act)
1148(define-key query-replace-map "\d" 'skip)
1149(define-key query-replace-map [delete] 'skip)
9275e5e3 1150(define-key query-replace-map [backspace] 'skip)
81bdc14d
RS
1151(define-key query-replace-map "y" 'act)
1152(define-key query-replace-map "n" 'skip)
633a305a
RS
1153(define-key query-replace-map "Y" 'act)
1154(define-key query-replace-map "N" 'skip)
34724fcb 1155(define-key query-replace-map "e" 'edit-replacement)
7ce278f3 1156(define-key query-replace-map "E" 'edit-replacement)
81bdc14d 1157(define-key query-replace-map "," 'act-and-show)
81bdc14d 1158(define-key query-replace-map "q" 'exit)
919592c0 1159(define-key query-replace-map "\r" 'exit)
384c7da4 1160(define-key query-replace-map [return] 'exit)
81bdc14d
RS
1161(define-key query-replace-map "." 'act-and-exit)
1162(define-key query-replace-map "\C-r" 'edit)
1163(define-key query-replace-map "\C-w" 'delete-and-edit)
1164(define-key query-replace-map "\C-l" 'recenter)
1165(define-key query-replace-map "!" 'automatic)
1166(define-key query-replace-map "^" 'backup)
1167(define-key query-replace-map "\C-h" 'help)
e731045a
KH
1168(define-key query-replace-map [f1] 'help)
1169(define-key query-replace-map [help] 'help)
81bdc14d 1170(define-key query-replace-map "?" 'help)
bc6312e1
RS
1171(define-key query-replace-map "\C-g" 'quit)
1172(define-key query-replace-map "\C-]" 'quit)
d9121bc0
RS
1173(define-key query-replace-map "\e" 'exit-prefix)
1174(define-key query-replace-map [escape] 'exit-prefix)
81bdc14d 1175
84482eb3 1176(defun replace-match-string-symbols (n)
e730be7f
DL
1177 "Process a list (and any sub-lists), expanding certain symbols.
1178Symbol Expands To
1179N (match-string N) (where N is a string of digits)
1180#N (string-to-number (match-string N))
1181& (match-string 0)
1182#& (string-to-number (match-string 0))
2f57bf85 1183# replace-count
e730be7f
DL
1184
1185Note that these symbols must be preceeded by a backslash in order to
1186type them."
84482eb3
RS
1187 (while n
1188 (cond
1189 ((consp (car n))
1190 (replace-match-string-symbols (car n))) ;Process sub-list
1191 ((symbolp (car n))
1192 (let ((name (symbol-name (car n))))
1193 (cond
1194 ((string-match "^[0-9]+$" name)
1195 (setcar n (list 'match-string (string-to-number name))))
1196 ((string-match "^#[0-9]+$" name)
1197 (setcar n (list 'string-to-number
1198 (list 'match-string
1199 (string-to-number (substring name 1))))))
1200 ((string= "&" name)
1201 (setcar n '(match-string 0)))
1202 ((string= "#&" name)
2f57bf85
DK
1203 (setcar n '(string-to-number (match-string 0))))
1204 ((string= "#" name)
1205 (setcar n 'replace-count))))))
84482eb3
RS
1206 (setq n (cdr n))))
1207
1208(defun replace-eval-replacement (expression replace-count)
1209 (let ((replacement (eval expression)))
1210 (if (stringp replacement)
1211 replacement
1212 (prin1-to-string replacement t))))
1213
2f57bf85
DK
1214(defun replace-quote (replacement)
1215 "Quote a replacement string.
1216This just doubles all backslashes in REPLACEMENT and
1217returns the resulting string. If REPLACEMENT is not
1218a string, it is first passed through `prin1-to-string'
1219with the `noescape' argument set.
1220
1221`match-data' is preserved across the call."
1222 (save-match-data
1223 (replace-regexp-in-string "\\\\" "\\\\"
1224 (if (stringp replacement)
1225 replacement
1226 (prin1-to-string replacement t))
1227 t t)))
1228
84482eb3
RS
1229(defun replace-loop-through-replacements (data replace-count)
1230 ;; DATA is a vector contaning the following values:
1231 ;; 0 next-rotate-count
1232 ;; 1 repeat-count
1233 ;; 2 next-replacement
1234 ;; 3 replacements
1235 (if (= (aref data 0) replace-count)
1236 (progn
1237 (aset data 0 (+ replace-count (aref data 1)))
1238 (let ((next (cdr (aref data 2))))
1239 (aset data 2 (if (consp next) next (aref data 3))))))
1240 (car (aref data 2)))
1241
7c1c02ac
DK
1242(defun replace-match-data (integers reuse &optional new)
1243 "Like `match-data', but markers in REUSE get invalidated.
1244If NEW is non-NIL, it is set and returned instead of fresh data,
1245but coerced to the correct value of INTEGERS."
1246 (or (and new
1247 (progn
1248 (set-match-data new)
1249 (and (eq new reuse)
1250 (eq (null integers) (markerp (car reuse)))
1251 new)))
1252 (match-data integers
1253 (prog1 reuse
1254 (while reuse
1255 (if (markerp (car reuse))
1256 (set-marker (car reuse) nil))
1257 (setq reuse (cdr reuse)))))))
1258
1259(defun replace-match-maybe-edit (newtext fixedcase literal noedit match-data)
1260 "Make a replacement with `replace-match', editing `\\?'.
15fd7d5d 1261NEWTEXT, FIXEDCASE, LITERAL are just passed on. If NOEDIT is true, no
7c1c02ac
DK
1262check for `\\?' is made to save time. MATCH-DATA is used for the
1263replacement. In case editing is done, it is changed to use markers.
1264
1265The return value is non-NIL if there has been no `\\?' or NOEDIT was
1266passed in. If LITERAL is set, no checking is done, anyway."
1267 (unless (or literal noedit)
1268 (setq noedit t)
1269 (while (string-match "\\(\\`\\|[^\\]\\)\\(\\\\\\\\\\)*\\(\\\\\\?\\)"
1270 newtext)
1271 (setq newtext
1272 (read-input "Edit replacement string: "
1273 (prog1
1274 (cons
1275 (replace-match "" t t newtext 3)
1276 (1+ (match-beginning 3)))
1277 (setq match-data
1278 (replace-match-data
1279 nil match-data match-data))))
1280 noedit nil)))
1281 (set-match-data match-data)
1282 (replace-match newtext fixedcase literal)
1283 noedit)
1284
d99118b0 1285(defun perform-replace (from-string replacements
698e1804 1286 query-flag regexp-flag delimited-flag
99a7559f 1287 &optional repeat-count map start end)
698e1804
RS
1288 "Subroutine of `query-replace'. Its complexity handles interactive queries.
1289Don't use this in your own program unless you want to query and set the mark
1290just as `query-replace' does. Instead, write a simple loop like this:
698665d1
GM
1291
1292 (while (re-search-forward \"foo[ \\t]+bar\" nil t)
698e1804 1293 (replace-match \"foobar\" nil nil))
698665d1
GM
1294
1295which will run faster and probably do exactly what you want. Please
1296see the documentation of `replace-match' to find out how to simulate
588c915a
CW
1297`case-replace'.
1298
1299This function returns nil if and only if there were no matches to
1300make, or the user didn't cancel the call."
81bdc14d 1301 (or map (setq map query-replace-map))
1c1dadab
RS
1302 (and query-flag minibuffer-auto-raise
1303 (raise-frame (window-frame (minibuffer-window))))
698e1804
RS
1304 (let ((nocasify (not (and case-fold-search case-replace
1305 (string-equal from-string
1306 (downcase from-string)))))
5a78b471
KH
1307 (case-fold-search (and case-fold-search
1308 (string-equal from-string
1309 (downcase from-string))))
d83a97ab 1310 (literal (or (not regexp-flag) (eq regexp-flag 'literal)))
698e1804
RS
1311 (search-function (if regexp-flag 're-search-forward 'search-forward))
1312 (search-string from-string)
e5d77022 1313 (real-match-data nil) ; the match data for the current match
698e1804 1314 (next-replacement nil)
7c1c02ac 1315 (noedit nil)
698e1804
RS
1316 (keep-going t)
1317 (stack nil)
698e1804 1318 (replace-count 0)
5632eb27
PE
1319 (nonempty-match nil)
1320
7ef5c431
KH
1321 ;; If non-nil, it is marker saying where in the buffer to stop.
1322 (limit nil)
1323
5632eb27
PE
1324 ;; Data for the next match. If a cons, it has the same format as
1325 ;; (match-data); otherwise it is t if a match is possible at point.
ae4eb03c 1326 (match-again t)
5632eb27 1327
35d59c0f
JL
1328 (isearch-string isearch-string)
1329 (isearch-regexp isearch-regexp)
ccec9764 1330 (isearch-case-fold-search isearch-case-fold-search)
02d95a27
RS
1331 (message
1332 (if query-flag
1333 (substitute-command-keys
1334 "Query replacing %s with %s: (\\<query-replace-map>\\[help] for help) "))))
7ef5c431
KH
1335
1336 ;; If region is active, in Transient Mark mode, operate on region.
47d72254
GM
1337 (when start
1338 (setq limit (copy-marker (max start end)))
1339 (goto-char (min start end))
1340 (deactivate-mark))
84482eb3
RS
1341
1342 ;; REPLACEMENTS is either a string, a list of strings, or a cons cell
1343 ;; containing a function and its first argument. The function is
1344 ;; called to generate each replacement like this:
1345 ;; (funcall (car replacements) (cdr replacements) replace-count)
1346 ;; It must return a string.
1347 (cond
1348 ((stringp replacements)
1349 (setq next-replacement replacements
1350 replacements nil))
1351 ((stringp (car replacements)) ; If it isn't a string, it must be a cons
1352 (or repeat-count (setq repeat-count 1))
1353 (setq replacements (cons 'replace-loop-through-replacements
1354 (vector repeat-count repeat-count
1355 replacements replacements)))))
1356
698e1804
RS
1357 (if delimited-flag
1358 (setq search-function 're-search-forward
1359 search-string (concat "\\b"
1360 (if regexp-flag from-string
1361 (regexp-quote from-string))
1362 "\\b")))
ccec9764
JL
1363 (when query-replace-lazy-highlight
1364 (setq isearch-string search-string
1365 isearch-regexp (or delimited-flag regexp-flag)
1366 isearch-case-fold-search case-fold-search
1367 isearch-lazy-highlight-last-string nil))
35d59c0f 1368
698e1804
RS
1369 (push-mark)
1370 (undo-boundary)
e782e9f2
RS
1371 (unwind-protect
1372 ;; Loop finding occurrences that perhaps should be replaced.
1373 (while (and keep-going
22a76778 1374 (not (or (eobp) (and limit (>= (point) limit))))
5632eb27
PE
1375 ;; Use the next match if it is already known;
1376 ;; otherwise, search for a match after moving forward
1377 ;; one char if progress is required.
1378 (setq real-match-data
1379 (if (consp match-again)
1380 (progn (goto-char (nth 1 match-again))
7c1c02ac
DK
1381 (replace-match-data t
1382 real-match-data
1383 match-again))
5632eb27 1384 (and (or match-again
889617de
GM
1385 ;; MATCH-AGAIN non-nil means we
1386 ;; accept an adjacent match. If
1387 ;; we don't, move one char to the
1388 ;; right. This takes us a
1389 ;; character too far at the end,
1390 ;; but this is undone after the
1391 ;; while-loop.
22a76778
JL
1392 (progn
1393 (forward-char 1)
1394 (not (or (eobp)
1395 (and limit (>= (point) limit))))))
7ef5c431 1396 (funcall search-function search-string limit t)
5632eb27
PE
1397 ;; For speed, use only integers and
1398 ;; reuse the list used last time.
7c1c02ac 1399 (replace-match-data t real-match-data)))))
1c4fe319
RS
1400 ;; Optionally ignore matches that have a read-only property.
1401 (unless (and query-replace-skip-read-only
1402 (text-property-not-all
1403 (match-beginning 0) (match-end 0)
1404 'read-only nil))
1405
1406 ;; Record whether the match is nonempty, to avoid an infinite loop
1407 ;; repeatedly matching the same empty string.
1408 (setq nonempty-match
1409 (/= (nth 0 real-match-data) (nth 1 real-match-data)))
1410
1411 ;; If the match is empty, record that the next one can't be
1412 ;; adjacent.
1413
1414 ;; Otherwise, if matching a regular expression, do the next
1415 ;; match now, since the replacement for this match may
1416 ;; affect whether the next match is adjacent to this one.
1417 ;; If that match is empty, don't use it.
1418 (setq match-again
1419 (and nonempty-match
1420 (or (not regexp-flag)
1421 (and (looking-at search-string)
1422 (let ((match (match-data)))
1423 (and (/= (nth 0 match) (nth 1 match))
1424 match))))))
1425
1426 ;; Calculate the replacement string, if necessary.
1427 (when replacements
1428 (set-match-data real-match-data)
1429 (setq next-replacement
1430 (funcall (car replacements) (cdr replacements)
7c1c02ac
DK
1431 replace-count)
1432 noedit nil))
1c4fe319 1433 (if (not query-flag)
cb05ca25
DK
1434 (let ((inhibit-read-only
1435 query-replace-skip-read-only))
15fd7d5d 1436 (unless (or literal noedit)
cb05ca25
DK
1437 (replace-highlight (nth 0 real-match-data)
1438 (nth 1 real-match-data)))
7c1c02ac
DK
1439 (setq noedit
1440 (replace-match-maybe-edit
1441 next-replacement nocasify literal
1442 noedit real-match-data)
1443 replace-count (1+ replace-count)))
1c4fe319
RS
1444 (undo-boundary)
1445 (let (done replaced key def)
1446 ;; Loop reading commands until one of them sets done,
7c1c02ac
DK
1447 ;; which means it has finished handling this
1448 ;; occurrence. Any command that sets `done' should
1449 ;; leave behind proper match data for the stack.
1450 ;; Commands not setting `done' need to adjust
1451 ;; `real-match-data'.
1c4fe319
RS
1452 (while (not done)
1453 (set-match-data real-match-data)
1454 (replace-highlight (match-beginning 0) (match-end 0))
1455 ;; Bind message-log-max so we don't fill up the message log
1456 ;; with a bunch of identical messages.
1457 (let ((message-log-max nil))
b938735a
JL
1458 (message message
1459 (query-replace-descr from-string)
1460 (query-replace-descr next-replacement)))
1c4fe319
RS
1461 (setq key (read-event))
1462 ;; Necessary in case something happens during read-event
1463 ;; that clobbers the match data.
1464 (set-match-data real-match-data)
1465 (setq key (vector key))
1466 (setq def (lookup-key map key))
1467 ;; Restore the match data while we process the command.
1468 (cond ((eq def 'help)
1469 (with-output-to-temp-buffer "*Help*"
1470 (princ
1471 (concat "Query replacing "
1472 (if regexp-flag "regexp " "")
1473 from-string " with "
1474 next-replacement ".\n\n"
1475 (substitute-command-keys
1476 query-replace-help)))
1477 (with-current-buffer standard-output
1478 (help-mode))))
1479 ((eq def 'exit)
1480 (setq keep-going nil)
1481 (setq done t))
1482 ((eq def 'backup)
1483 (if stack
588c915a 1484 (let ((elt (pop stack)))
7c1c02ac
DK
1485 (goto-char (nth 0 elt))
1486 (setq replaced (nth 1 elt)
1487 real-match-data
1488 (replace-match-data
1489 t real-match-data
1490 (nth 2 elt))))
1c4fe319
RS
1491 (message "No previous match")
1492 (ding 'no-terminate)
1493 (sit-for 1)))
1494 ((eq def 'act)
1495 (or replaced
7c1c02ac
DK
1496 (setq noedit
1497 (replace-match-maybe-edit
1498 next-replacement nocasify literal
1499 noedit real-match-data)
1500 replace-count (1+ replace-count)))
1c4fe319
RS
1501 (setq done t replaced t))
1502 ((eq def 'act-and-exit)
1503 (or replaced
7c1c02ac 1504 (setq noedit
da6eb51c 1505 (replace-match-maybe-edit
7c1c02ac
DK
1506 next-replacement nocasify literal
1507 noedit real-match-data)
1508 replace-count (1+ replace-count)))
1c4fe319
RS
1509 (setq keep-going nil)
1510 (setq done t replaced t))
1511 ((eq def 'act-and-show)
1512 (if (not replaced)
7c1c02ac
DK
1513 (setq noedit
1514 (replace-match-maybe-edit
1515 next-replacement nocasify literal
1516 noedit real-match-data)
1517 replace-count (1+ replace-count)
1518 real-match-data (replace-match-data
1519 t real-match-data)
1520 replaced t)))
1c4fe319
RS
1521 ((eq def 'automatic)
1522 (or replaced
7c1c02ac
DK
1523 (setq noedit
1524 (replace-match-maybe-edit
1525 next-replacement nocasify literal
1526 noedit real-match-data)
1527 replace-count (1+ replace-count)))
1c4fe319
RS
1528 (setq done t query-flag nil replaced t))
1529 ((eq def 'skip)
1530 (setq done t))
1531 ((eq def 'recenter)
1532 (recenter nil))
1533 ((eq def 'edit)
1534 (let ((opos (point-marker)))
7c1c02ac
DK
1535 (setq real-match-data (replace-match-data
1536 nil real-match-data
1537 real-match-data))
1c4fe319 1538 (goto-char (match-beginning 0))
86914dcc
RS
1539 (save-excursion
1540 (save-window-excursion
1541 (recursive-edit)))
7c1c02ac
DK
1542 (goto-char opos)
1543 (set-marker opos nil))
1c4fe319
RS
1544 ;; Before we make the replacement,
1545 ;; decide whether the search string
1546 ;; can match again just after this match.
1547 (if (and regexp-flag nonempty-match)
1548 (setq match-again (and (looking-at search-string)
1549 (match-data)))))
1c4fe319
RS
1550 ;; Edit replacement.
1551 ((eq def 'edit-replacement)
7c1c02ac
DK
1552 (setq real-match-data (replace-match-data
1553 nil real-match-data
1554 real-match-data)
1555 next-replacement
1c4fe319 1556 (read-input "Edit replacement string: "
7c1c02ac
DK
1557 next-replacement)
1558 noedit nil)
1559 (if replaced
1560 (set-match-data real-match-data)
1561 (setq noedit
1562 (replace-match-maybe-edit
1563 next-replacement nocasify literal noedit
1564 real-match-data)
1565 replaced t))
1c4fe319 1566 (setq done t))
d99118b0 1567
1c4fe319 1568 ((eq def 'delete-and-edit)
7c1c02ac
DK
1569 (replace-match "" t t)
1570 (setq real-match-data (replace-match-data
1571 nil real-match-data))
1572 (replace-dehighlight)
1573 (save-excursion (recursive-edit))
1c4fe319
RS
1574 (setq replaced t))
1575 ;; Note: we do not need to treat `exit-prefix'
1576 ;; specially here, since we reread
1577 ;; any unrecognized character.
1578 (t
1579 (setq this-command 'mode-exited)
1580 (setq keep-going nil)
1581 (setq unread-command-events
1582 (append (listify-key-sequence key)
1583 unread-command-events))
35d59c0f 1584 (setq done t)))
ccec9764
JL
1585 (when query-replace-lazy-highlight
1586 ;; Restore isearch data for lazy highlighting
1587 ;; in case of isearching during recursive edit
35d59c0f 1588 (setq isearch-string search-string
ccec9764
JL
1589 isearch-regexp (or delimited-flag regexp-flag)
1590 isearch-case-fold-search case-fold-search)
1591 ;; Force lazy rehighlighting only after replacements
1592 (if (not (memq def '(skip backup)))
1593 (setq isearch-lazy-highlight-last-string nil))))
1c4fe319
RS
1594 ;; Record previous position for ^ when we move on.
1595 ;; Change markers to numbers in the match data
1596 ;; since lots of markers slow down editing.
7c1c02ac
DK
1597 (push (list (point) replaced
1598;;; If the replacement has already happened, all we need is the
1599;;; current match start and end. We could get this with a trivial
1600;;; match like
1601;;; (save-excursion (goto-char (match-beginning 0))
1602;;; (search-forward (match-string 0))
1603;;; (match-data t))
1604;;; if we really wanted to avoid manually constructing match data.
1605;;; Adding current-buffer is necessary so that match-data calls can
1606;;; return markers which are appropriate for editing.
1607 (if replaced
1608 (list
1609 (match-beginning 0)
1610 (match-end 0)
1611 (current-buffer))
1612 (match-data t)))
1613 stack)))))
889617de
GM
1614
1615 ;; The code preventing adjacent regexp matches in the condition
1616 ;; of the while-loop above will haven taken us one character
1617 ;; beyond the last replacement. Undo that.
1618 (when (and regexp-flag (not match-again) (> replace-count 0))
1619 (backward-char 1))
d99118b0 1620
e782e9f2 1621 (replace-dehighlight))
4d33492a
RS
1622 (or unread-command-events
1623 (message "Replaced %d occurrence%s"
1624 replace-count
1625 (if (= replace-count 1) "" "s")))
1626 (and keep-going stack)))
698e1804 1627
e782e9f2
RS
1628(defvar replace-overlay nil)
1629
ccec9764
JL
1630(defun replace-highlight (beg end)
1631 (if query-replace-highlight
1632 (if replace-overlay
1633 (move-overlay replace-overlay beg end (current-buffer))
1634 (setq replace-overlay (make-overlay beg end))
1635 (overlay-put replace-overlay 'priority 1) ;higher than lazy overlays
1636 (overlay-put replace-overlay 'face 'query-replace)))
1637 (if query-replace-lazy-highlight
1638 (isearch-lazy-highlight-new-loop)))
1639
e782e9f2 1640(defun replace-dehighlight ()
ccec9764
JL
1641 (when replace-overlay
1642 (delete-overlay replace-overlay))
1643 (when query-replace-lazy-highlight
f42a241b 1644 (isearch-lazy-highlight-cleanup lazy-highlight-cleanup)
ccec9764 1645 (setq isearch-lazy-highlight-last-string nil)))
e782e9f2 1646
6f1df6d9 1647;; arch-tag: 16b4cd61-fd40-497b-b86f-b667c4cf88e4
c88ab9ce 1648;;; replace.el ends here