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