Fix bug #14558 with turning off mouse-highlight during highlight.
[bpt/emacs.git] / lisp / isearch.el
CommitLineData
55535639 1;;; isearch.el --- incremental search minor mode
3b1e4dd1 2
ab422c4d 3;; Copyright (C) 1992-1997, 1999-2013 Free Software Foundation, Inc.
3a801d0c 4
3b1e4dd1 5;; Author: Daniel LaLiberte <liberte@cs.uiuc.edu>
0f09b616 6;; Maintainer: FSF
117132a6 7;; Keywords: matching
bd78fa1d 8;; Package: emacs
8e1cae6d 9
54d2ecd3 10;; This file is part of GNU Emacs.
8e1cae6d 11
eb3fa2cf 12;; GNU Emacs is free software: you can redistribute it and/or modify
59243403 13;; it under the terms of the GNU General Public License as published by
eb3fa2cf
GM
14;; the Free Software Foundation, either version 3 of the License, or
15;; (at your option) any later version.
59243403 16
8e1cae6d 17;; GNU Emacs is distributed in the hope that it will be useful,
59243403
RS
18;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20;; GNU General Public License for more details.
21
22;; You should have received a copy of the GNU General Public License
eb3fa2cf 23;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
8e1cae6d 24
3b1e4dd1
ER
25;;; Commentary:
26
8e1cae6d
JB
27;; Instructions
28
08200510
RS
29;; For programmed use of isearch-mode, e.g. calling (isearch-forward),
30;; isearch-mode behaves modally and does not return until the search
117132a6 31;; is completed. It uses a recursive-edit to behave this way.
08200510 32
8e1cae6d 33;; The key bindings active within isearch-mode are defined below in
08200510
RS
34;; `isearch-mode-map' which is given bindings close to the default
35;; characters of the original isearch.el. With `isearch-mode',
36;; however, you can bind multi-character keys and it should be easier
37;; to add new commands. One bug though: keys with meta-prefix cannot
38;; be longer than two chars. Also see minibuffer-local-isearch-map
39;; for bindings active during `isearch-edit-string'.
40
117132a6
DL
41;; isearch-mode should work even if you switch windows with the mouse,
42;; in which case isearch-mode is terminated automatically before the
43;; switch.
08200510
RS
44
45;; The search ring and completion commands automatically put you in
46;; the minibuffer to edit the string. This gives you a chance to
47;; modify the search string before executing the search. There are
48;; three commands to terminate the editing: C-s and C-r exit the
49;; minibuffer and search forward and reverse respectively, while C-m
6d65486d 50;; exits and searches in the last search direction.
08200510
RS
51
52;; Exiting immediately from isearch uses isearch-edit-string instead
53;; of nonincremental-search, if search-nonincremental-instead is non-nil.
54;; The name of this option should probably be changed if we decide to
55;; keep the behavior. No point in forcing nonincremental search until
56;; the last possible moment.
57
3b1e4dd1 58;;; Code:
08200510 59
7c2dc8bd 60(eval-when-compile (require 'cl-lib))
08200510 61\f
191f025a 62;; Some additional options and constants.
08200510 63
9c1db929
RS
64(defgroup isearch nil
65 "Incremental search minor mode."
117132a6
DL
66 :link '(emacs-commentary-link "isearch")
67 :link '(custom-manual "(emacs)Incremental Search")
9c1db929
RS
68 :prefix "isearch-"
69 :prefix "search-"
70 :group 'matching)
fdf5afa4 71
9c1db929
RS
72
73(defcustom search-exit-option t
4b65254d 74 "Non-nil means random control characters terminate incremental search."
9c1db929
RS
75 :type 'boolean
76 :group 'isearch)
77
78(defcustom search-slow-window-lines 1
4b65254d 79 "Number of lines in slow search display windows.
fdf5afa4
RS
80These are the short windows used during incremental search on slow terminals.
81Negative means put the slow search window at the top (normally it's at bottom)
9c1db929
RS
82and the value is minus the number of lines."
83 :type 'integer
84 :group 'isearch)
fdf5afa4 85
9c1db929 86(defcustom search-slow-speed 1200
4b65254d 87 "Highest terminal speed at which to use \"slow\" style incremental search.
fdf5afa4 88This is the style where a one-line window is created to show the line
9c1db929
RS
89that the search has reached."
90 :type 'integer
91 :group 'isearch)
8e1cae6d 92
9c1db929 93(defcustom search-upper-case 'not-yanks
4b65254d 94 "If non-nil, upper case chars disable case fold searching.
08200510
RS
95That is, upper and lower case chars must match exactly.
96This applies no matter where the chars come from, but does not
fdf5afa4 97apply to chars in regexps that are prefixed with `\\'.
6e3057bb
JL
98If this value is `not-yanks', text yanked into the search string
99in Isearch mode is always downcased."
9c1db929
RS
100 :type '(choice (const :tag "off" nil)
101 (const not-yanks)
5786b3ed 102 (other :tag "on" t))
9c1db929 103 :group 'isearch)
8e1cae6d 104
9c1db929 105(defcustom search-nonincremental-instead t
0e6038be 106 "If non-nil, do a nonincremental search instead of exiting immediately.
08200510 107Actually, `isearch-edit-string' is called to let you enter the search
9c1db929
RS
108string, and RET terminates editing and does a nonincremental search."
109 :type 'boolean
110 :group 'isearch)
8e1cae6d 111
1e8780b1 112(defcustom search-whitespace-regexp (purecopy "\\s-+")
4b65254d 113 "If non-nil, regular expression to match a sequence of whitespace chars.
dd7ffad6
CY
114When you enter a space or spaces in the incremental search, it
115will match any sequence matched by this regexp. As an exception,
116spaces are treated normally in regexp incremental search if they
117occur in a regexp construct like [...] or *, + or ?.
118
63dd1c6f
JL
119If the value is a string, it applies to both ordinary and
120regexp incremental search. If the value is nil, or
121`isearch-lax-whitespace' is nil for ordinary incremental search, or
122`isearch-regexp-lax-whitespace' is nil for regexp incremental search,
123then each space you type matches literally, against one space.
dd7ffad6 124
ab67e8b6
RS
125You might want to use something like \"[ \\t\\r\\n]+\" instead.
126In the Customization buffer, that is `[' followed by a space,
dd7ffad6 127a tab, a carriage return (control-M), a newline, and `]+'."
63dd1c6f 128 :type '(choice (const :tag "Match Spaces Literally" nil)
51a77cba 129 regexp)
dd7ffad6
CY
130 :group 'isearch
131 :version "24.3")
8e1cae6d 132
0352b205
RS
133(defcustom search-invisible 'open
134 "If t incremental search can match hidden text.
6480c508
JB
135A nil value means don't match invisible text.
136When the value is `open', if the text matched is made invisible by
0352b205
RS
137an overlay having an `invisible' property and that overlay has a property
138`isearch-open-invisible', then incremental search will show the contents.
cd59ea72
SM
139\(This applies when using `outline.el' and `hideshow.el'.)
140See also `reveal-mode' if you want overlays to automatically be opened
141whenever point is in one of them."
0352b205
RS
142 :type '(choice (const :tag "Match hidden text" t)
143 (const :tag "Open overlays" open)
79c7a4fa 144 (const :tag "Don't match hidden text" nil))
0352b205
RS
145 :group 'isearch)
146
147(defcustom isearch-hide-immediately t
68c18d6d
RS
148 "If non-nil, re-hide an invisible match right away.
149This variable makes a difference when `search-invisible' is set to `open'.
150It means that after search makes some invisible text visible
151to show the match, it makes the text invisible again when the match moves.
b48ca14f
JB
152Ordinarily the text becomes invisible again at the end of the search."
153 :type 'boolean
0352b205 154 :group 'isearch)
86bfaffe 155
00098b01 156(defcustom isearch-resume-in-command-history nil
4b65254d
JB
157 "If non-nil, `isearch-resume' commands are added to the command history.
158This allows you to resume earlier Isearch sessions through the
00098b01 159command history."
6848c9f1
KS
160 :type 'boolean
161 :group 'isearch)
162
08200510
RS
163(defvar isearch-mode-hook nil
164 "Function(s) to call after starting up an incremental search.")
165
a6020335
MH
166(defvar isearch-update-post-hook nil
167 "Function(s) to call after isearch has found matches in the buffer.")
168
08200510 169(defvar isearch-mode-end-hook nil
87e97673
RS
170 "Function(s) to call after terminating an incremental search.
171When these functions are called, `isearch-mode-end-hook-quit'
4974aba8 172is non-nil if the user quits the search.")
87e97673
RS
173
174(defvar isearch-mode-end-hook-quit nil
4974aba8 175 "Non-nil while running `isearch-mode-end-hook' if the user quits the search.")
08200510 176
ac475d3a
JL
177(defvar isearch-message-function nil
178 "Function to call to display the search prompt.
9fc9a531 179If nil, use function `isearch-message'.")
ac475d3a 180
6a18e4e7
JL
181(defvar isearch-wrap-function nil
182 "Function to call to wrap the search when search is failed.
183If nil, move point to the beginning of the buffer for a forward search,
184or to the end of the buffer for a backward search.")
185
186(defvar isearch-push-state-function nil
4b65254d 187 "Function to save a function restoring the mode-specific Isearch state
6a18e4e7
JL
188to the search status stack.")
189
66fc57e3
JL
190(defvar isearch-filter-predicates nil
191 "Predicates that filter the search hits that would normally be available.
192Search hits that dissatisfy the list of predicates are skipped.
193Each function in this list has two arguments: the positions of
194start and end of text matched by the search.
195The search loop uses `run-hook-with-args-until-failure' to call
196each predicate in order, and when one of the predicates returns nil,
197skips this match and continues searching for the next match.
198When the list of predicates is empty, `run-hook-with-args-until-failure'
199returns non-nil that means that the found match is accepted.
200The property `isearch-message-prefix' put on the predicate's symbol
f5d43027 201specifies the prefix string displayed in the search message.")
66fc57e3
JL
202(define-obsolete-variable-alias 'isearch-filter-predicate
203 'isearch-filter-predicates
204 "24.4")
5e189f39 205
191f025a 206;; Search ring.
8e1cae6d
JB
207
208(defvar search-ring nil
209 "List of search string sequences.")
08200510 210(defvar regexp-search-ring nil
8e1cae6d
JB
211 "List of regular expression search string sequences.")
212
9c1db929 213(defcustom search-ring-max 16
4b65254d 214 "Maximum length of search ring before oldest elements are thrown away."
9c1db929
RS
215 :type 'integer
216 :group 'isearch)
217(defcustom regexp-search-ring-max 16
4b65254d 218 "Maximum length of regexp search ring before oldest elements are thrown away."
9c1db929
RS
219 :type 'integer
220 :group 'isearch)
8e1cae6d
JB
221
222(defvar search-ring-yank-pointer nil
a5dcf63f 223 "Index in `search-ring' of last string reused.
6480c508 224It is nil if none yet.")
08200510 225(defvar regexp-search-ring-yank-pointer nil
a5dcf63f 226 "Index in `regexp-search-ring' of last string reused.
6480c508 227It is nil if none yet.")
8e1cae6d 228
9c1db929 229(defcustom search-ring-update nil
4b65254d 230 "Non-nil if advancing or retreating in the search ring should cause search.
9c1db929
RS
231Default value, nil, means edit the string instead."
232 :type 'boolean
233 :group 'isearch)
08200510 234
bca92193
JL
235;;; isearch highlight customization.
236
237(defcustom search-highlight t
4b65254d 238 "Non-nil means incremental search highlights the current match."
bca92193
JL
239 :type 'boolean
240 :group 'isearch)
241
242(defface isearch
243 '((((class color) (min-colors 88) (background light))
244 ;; The background must not be too dark, for that means
245 ;; the character is hard to see when the cursor is there.
a49ff341 246 (:background "magenta3" :foreground "lightskyblue1"))
bca92193
JL
247 (((class color) (min-colors 88) (background dark))
248 (:background "palevioletred2" :foreground "brown4"))
249 (((class color) (min-colors 16))
250 (:background "magenta4" :foreground "cyan1"))
251 (((class color) (min-colors 8))
252 (:background "magenta4" :foreground "cyan1"))
253 (t (:inverse-video t)))
254 "Face for highlighting Isearch matches."
92cc4a30
JL
255 :group 'isearch
256 :group 'basic-faces)
4be520fb 257(defvar isearch-face 'isearch)
bca92193 258
d8891294
JL
259(defface isearch-fail
260 '((((class color) (min-colors 88) (background light))
261 (:background "RosyBrown1"))
262 (((class color) (min-colors 88) (background dark))
263 (:background "red4"))
264 (((class color) (min-colors 16))
265 (:background "red"))
266 (((class color) (min-colors 8))
267 (:background "red"))
268 (((class color grayscale))
269 :foreground "grey")
270 (t (:inverse-video t)))
723e5b84 271 "Face for highlighting failed part in Isearch echo-area message."
d8891294 272 :version "23.1"
723e5b84
BG
273 :group 'isearch)
274
bca92193 275(defcustom isearch-lazy-highlight t
4b65254d 276 "Controls the lazy-highlighting during incremental search.
bca92193
JL
277When non-nil, all text in the buffer matching the current search
278string is highlighted lazily (see `lazy-highlight-initial-delay'
279and `lazy-highlight-interval')."
280 :type 'boolean
281 :group 'lazy-highlight
282 :group 'isearch)
283
c1bc6bb6 284;;; Lazy highlight customization.
bca92193 285
c1bc6bb6
RS
286(defgroup lazy-highlight nil
287 "Lazy highlighting feature for matching strings."
288 :prefix "lazy-highlight-"
289 :version "21.1"
290 :group 'isearch
bca92193 291 :group 'matching)
c1bc6bb6 292
af3ccb5c
GM
293(define-obsolete-variable-alias 'isearch-lazy-highlight-cleanup
294 'lazy-highlight-cleanup
295 "22.1")
296
c1bc6bb6 297(defcustom lazy-highlight-cleanup t
4b65254d 298 "Controls whether to remove extra highlighting after a search.
c1bc6bb6 299If this is nil, extra highlighting can be \"manually\" removed with
46fe9018 300\\[lazy-highlight-cleanup]."
c1bc6bb6
RS
301 :type 'boolean
302 :group 'lazy-highlight)
af3ccb5c
GM
303
304(define-obsolete-variable-alias 'isearch-lazy-highlight-initial-delay
305 'lazy-highlight-initial-delay
6480c508 306 "22.1")
c1bc6bb6
RS
307
308(defcustom lazy-highlight-initial-delay 0.25
4b65254d 309 "Seconds to wait before beginning to lazily highlight all matches."
c1bc6bb6
RS
310 :type 'number
311 :group 'lazy-highlight)
af3ccb5c
GM
312
313(define-obsolete-variable-alias 'isearch-lazy-highlight-interval
314 'lazy-highlight-interval
6480c508 315 "22.1")
c1bc6bb6
RS
316
317(defcustom lazy-highlight-interval 0 ; 0.0625
4b65254d 318 "Seconds between lazily highlighting successive matches."
c1bc6bb6
RS
319 :type 'number
320 :group 'lazy-highlight)
af3ccb5c
GM
321
322(define-obsolete-variable-alias 'isearch-lazy-highlight-max-at-a-time
323 'lazy-highlight-max-at-a-time
6480c508 324 "22.1")
c1bc6bb6
RS
325
326(defcustom lazy-highlight-max-at-a-time 20
4b65254d
JB
327 "Maximum matches to highlight at a time (for `lazy-highlight').
328Larger values may reduce Isearch's responsiveness to user input;
c1bc6bb6
RS
329smaller values make matches highlight slowly.
330A value of nil means highlight all matches."
331 :type '(choice (const :tag "All" nil)
332 (integer :tag "Some"))
333 :group 'lazy-highlight)
334
e3cde0c7 335(defface lazy-highlight
c1bc6bb6
RS
336 '((((class color) (min-colors 88) (background light))
337 (:background "paleturquoise"))
338 (((class color) (min-colors 88) (background dark))
339 (:background "paleturquoise4"))
340 (((class color) (min-colors 16))
341 (:background "turquoise3"))
342 (((class color) (min-colors 8))
343 (:background "turquoise3"))
344 (t (:underline t)))
345 "Face for lazy highlighting of matches other than the current one."
92cc4a30
JL
346 :group 'lazy-highlight
347 :group 'basic-faces)
c4f6e489 348(define-obsolete-face-alias 'isearch-lazy-highlight-face 'lazy-highlight "22.1")
6480c508
JB
349(define-obsolete-variable-alias 'isearch-lazy-highlight-face
350 'lazy-highlight-face
351 "22.1")
af3ccb5c 352(defvar lazy-highlight-face 'lazy-highlight)
c1bc6bb6 353\f
b92368b4
JL
354;; Define isearch help map.
355
356(defvar isearch-help-map
4b65254d 357 (let ((map (make-sparse-keymap)))
b92368b4
JL
358 (define-key map [t] 'isearch-other-control-char)
359 (define-key map (char-to-string help-char) 'isearch-help-for-help)
360 (define-key map [help] 'isearch-help-for-help)
361 (define-key map [f1] 'isearch-help-for-help)
362 (define-key map "?" 'isearch-help-for-help)
363 (define-key map "b" 'isearch-describe-bindings)
364 (define-key map "k" 'isearch-describe-key)
365 (define-key map "m" 'isearch-describe-mode)
366 (define-key map "q" 'help-quit)
367 map)
4b65254d 368 "Keymap for characters following the Help key for Isearch mode.")
b92368b4
JL
369
370(eval-when-compile (require 'help-macro))
371
372(make-help-screen isearch-help-for-help-internal
ce9a0ccb 373 (purecopy "Type a help option: [bkm] or ?")
b92368b4
JL
374 "You have typed %THIS-KEY%, the help character. Type a Help option:
375\(Type \\<help-map>\\[help-quit] to exit the Help command.)
376
4b65254d
JB
377b Display all Isearch key bindings.
378k KEYS Display full documentation of Isearch key sequence.
379m Display documentation of Isearch mode.
b92368b4
JL
380
381You can't type here other help keys available in the global help map,
4b65254d
JB
382but outside of this help window when you type them in Isearch mode,
383they exit Isearch mode before displaying global help."
b92368b4
JL
384 isearch-help-map)
385
85178ca1
JL
386(defvar isearch--display-help-action '(nil (inhibit-same-window . t)))
387
b92368b4 388(defun isearch-help-for-help ()
4b65254d 389 "Display Isearch help menu."
b92368b4 390 (interactive)
85178ca1 391 (let ((display-buffer-overriding-action isearch--display-help-action))
b92368b4
JL
392 (isearch-help-for-help-internal))
393 (isearch-update))
394
395(defun isearch-describe-bindings ()
4b65254d
JB
396 "Show a list of all keys defined in Isearch mode, and their definitions.
397This is like `describe-bindings', but displays only Isearch keys."
b92368b4 398 (interactive)
85178ca1 399 (let ((display-buffer-overriding-action isearch--display-help-action))
b92368b4
JL
400 (with-help-window "*Help*"
401 (with-current-buffer standard-output
402 (princ "Isearch Mode Bindings:\n")
403 (princ (substitute-command-keys "\\{isearch-mode-map}"))))))
404
405(defun isearch-describe-key ()
406 "Display documentation of the function invoked by isearch key."
407 (interactive)
85178ca1 408 (let ((display-buffer-overriding-action isearch--display-help-action))
b92368b4
JL
409 (call-interactively 'describe-key))
410 (isearch-update))
411
412(defun isearch-describe-mode ()
4b65254d 413 "Display documentation of Isearch mode."
b92368b4 414 (interactive)
85178ca1 415 (let ((display-buffer-overriding-action isearch--display-help-action))
b92368b4
JL
416 (describe-function 'isearch-forward))
417 (isearch-update))
418
419(defalias 'isearch-mode-help 'isearch-describe-mode)
420
421\f
191f025a 422;; Define isearch-mode keymap.
8e1cae6d 423
02b2f510 424(defvar isearch-mode-map
4b65254d
JB
425 (let ((i 0)
426 (map (make-keymap)))
8f924df7 427 (or (char-table-p (nth 1 map))
02b2f510
SM
428 (error "The initialization of isearch-mode-map must be updated"))
429 ;; Make all multibyte characters search for themselves.
6b61353c 430 (set-char-table-range (nth 1 map) (cons #x100 (max-char))
5760cdc1 431 'isearch-printing-char)
6b61353c
KH
432 ;; Make function keys, etc, which aren't bound to a scrolling-function
433 ;; exit the search.
02b2f510 434 (define-key map [t] 'isearch-other-control-char)
02b2f510
SM
435
436 ;; Single-byte printing chars extend the search string by default.
6480c508 437 (setq i ?\s)
02b2f510
SM
438 (while (< i 256)
439 (define-key map (vector i) 'isearch-printing-char)
440 (setq i (1+ i)))
441
442 ;; To handle local bindings with meta char prefix keys, define
443 ;; another full keymap. This must be done for any other prefix
444 ;; keys as well, one full keymap per char of the prefix key. It
445 ;; would be simpler to disable the global keymap, and/or have a
446 ;; default local key binding for any key not otherwise bound.
447 (let ((meta-map (make-sparse-keymap)))
448 (define-key map (char-to-string meta-prefix-char) meta-map)
7c2dc8bd
SM
449 (define-key map [escape] meta-map)
450 (define-key meta-map [t] 'isearch-other-meta-char))
02b2f510
SM
451
452 ;; Several non-printing chars change the searching behavior.
453 (define-key map "\C-s" 'isearch-repeat-forward)
454 (define-key map "\C-r" 'isearch-repeat-backward)
455 ;; Define M-C-s and M-C-r like C-s and C-r so that the same key
456 ;; combinations can be used to repeat regexp isearches that can
457 ;; be used to start these searches.
458 (define-key map "\M-\C-s" 'isearch-repeat-forward)
459 (define-key map "\M-\C-r" 'isearch-repeat-backward)
460 (define-key map "\177" 'isearch-delete-char)
461 (define-key map "\C-g" 'isearch-abort)
1f4139d5 462
02b2f510
SM
463 ;; This assumes \e is the meta-prefix-char.
464 (or (= ?\e meta-prefix-char)
465 (error "Inconsistency in isearch.el"))
466 (define-key map "\e\e\e" 'isearch-cancel)
467 (define-key map [escape escape escape] 'isearch-cancel)
b48ca14f 468
02b2f510 469 (define-key map "\C-q" 'isearch-quote-char)
08200510 470
02b2f510
SM
471 (define-key map "\r" 'isearch-exit)
472 (define-key map "\C-j" 'isearch-printing-char)
473 (define-key map "\t" 'isearch-printing-char)
ec06d344 474 (define-key map [?\S-\ ] 'isearch-printing-char)
b48ca14f 475
74820eb5
JL
476 (define-key map "\C-w" 'isearch-yank-word-or-char)
477 (define-key map "\M-\C-w" 'isearch-del-char)
478 (define-key map "\M-\C-y" 'isearch-yank-char)
892777ba
CY
479 (define-key map "\C-y" 'isearch-yank-kill)
480 (define-key map "\M-s\C-e" 'isearch-yank-line)
08200510 481
50de6a38
JL
482 (define-key map (char-to-string help-char) isearch-help-map)
483 (define-key map [help] isearch-help-map)
484 (define-key map [f1] isearch-help-map)
08200510 485
02b2f510
SM
486 (define-key map "\M-n" 'isearch-ring-advance)
487 (define-key map "\M-p" 'isearch-ring-retreat)
25666126 488 (define-key map "\M-y" 'isearch-yank-pop)
02b2f510
SM
489
490 (define-key map "\M-\t" 'isearch-complete)
491
492 ;; Pass frame events transparently so they won't exit the search.
493 ;; In particular, if we have more than one display open, then a
494 ;; switch-frame might be generated by someone typing at another keyboard.
495 (define-key map [switch-frame] nil)
496 (define-key map [delete-frame] nil)
497 (define-key map [iconify-frame] nil)
498 (define-key map [make-frame-visible] nil)
3f482bc0 499 (define-key map [mouse-movement] nil)
76197e28
JR
500 (define-key map [language-change] nil)
501
02b2f510
SM
502 ;; For searching multilingual text.
503 (define-key map "\C-\\" 'isearch-toggle-input-method)
504 (define-key map "\C-^" 'isearch-toggle-specified-input-method)
505
506 ;; People expect to be able to paste with the mouse.
e4af1426 507 (define-key map [mouse-2] #'isearch-mouse-2)
02b2f510
SM
508 (define-key map [down-mouse-2] nil)
509
510 ;; Some bindings you may want to put in your isearch-mode-hook.
511 ;; Suggest some alternates...
512 (define-key map "\M-c" 'isearch-toggle-case-fold)
513 (define-key map "\M-r" 'isearch-toggle-regexp)
514 (define-key map "\M-e" 'isearch-edit-string)
515
b9cb2387 516 (define-key map "\M-sc" 'isearch-toggle-case-fold)
01dea85f 517 (define-key map "\M-si" 'isearch-toggle-invisible)
70bc5268
JL
518 (define-key map "\M-sr" 'isearch-toggle-regexp)
519 (define-key map "\M-sw" 'isearch-toggle-word)
b9cb2387 520 (define-key map "\M-s_" 'isearch-toggle-symbol)
63dd1c6f 521 (define-key map "\M-s " 'isearch-toggle-lax-whitespace)
70bc5268 522
81e898ea
SM
523 (define-key map [?\M-%] 'isearch-query-replace)
524 (define-key map [?\C-\M-%] 'isearch-query-replace-regexp)
6e3057bb 525 (define-key map "\M-so" 'isearch-occur)
11c9f489 526 (define-key map "\M-shr" 'isearch-highlight-regexp)
74820eb5 527
da547b32
JL
528 ;; The key translations defined in the C-x 8 prefix should add
529 ;; characters to the search string. See iso-transl.el.
439f7677
CY
530 (define-key map "\C-x" nil)
531 (define-key map [?\C-x t] 'isearch-other-control-char)
532 (define-key map "\C-x8" nil)
da547b32 533 (define-key map "\C-x8\r" 'isearch-char-by-name)
439f7677 534
02b2f510
SM
535 map)
536 "Keymap for `isearch-mode'.")
537
538(defvar minibuffer-local-isearch-map
539 (let ((map (make-sparse-keymap)))
540 (set-keymap-parent map minibuffer-local-map)
6d65486d 541 (define-key map "\r" 'exit-minibuffer)
02b2f510 542 (define-key map "\M-\t" 'isearch-complete-edit)
74820eb5
JL
543 (define-key map "\C-s" 'isearch-forward-exit-minibuffer)
544 (define-key map "\C-r" 'isearch-reverse-exit-minibuffer)
545 (define-key map "\C-f" 'isearch-yank-char-in-minibuffer)
546 (define-key map [right] 'isearch-yank-char-in-minibuffer)
02b2f510 547 map)
4b65254d 548 "Keymap for editing Isearch strings in the minibuffer.")
8e1cae6d 549
8e1cae6d 550;; Internal variables declared globally for byte-compiler.
08200510
RS
551;; These are all set with setq while isearching
552;; and bound locally while editing the search string.
553
554(defvar isearch-forward nil) ; Searching in the forward direction.
555(defvar isearch-regexp nil) ; Searching for a regexp.
d5e61c1c 556(defvar isearch-word nil
b9cb2387 557 "Regexp-based search mode for words/symbols.
d5e61c1c 558If t, do incremental search for a sequence of words, ignoring punctuation.
b9cb2387
JL
559If the value is a function (e.g. `isearch-symbol-regexp'), it is called to
560convert the search string to a regexp used by regexp search functions.
561The property `isearch-message-prefix' put on this function specifies the
48d1354e 562prefix string displayed in the search message.")
08200510 563
63dd1c6f
JL
564(defvar isearch-lax-whitespace t
565 "If non-nil, a space will match a sequence of whitespace chars.
566When you enter a space or spaces in ordinary incremental search, it
567will match any sequence matched by the regexp defined by the variable
568`search-whitespace-regexp'. If the value is nil, each space you type
569matches literally, against one space. You can toggle the value of this
570variable by the command `isearch-toggle-lax-whitespace'.")
571
572(defvar isearch-regexp-lax-whitespace nil
573 "If non-nil, a space will match a sequence of whitespace chars.
574When you enter a space or spaces in regexp incremental search, it
575will match any sequence matched by the regexp defined by the variable
576`search-whitespace-regexp'. If the value is nil, each space you type
577matches literally, against one space. You can toggle the value of this
578variable by the command `isearch-toggle-lax-whitespace'.")
579
191f025a
SM
580(defvar isearch-cmds nil
581 "Stack of search status sets.
08e3de69
EZ
582Each set is a vector of the form:
583 [STRING MESSAGE POINT SUCCESS FORWARD OTHER-END WORD
584 INVALID-REGEXP WRAPPED BARRIER WITHIN-BRACKETS CASE-FOLD-SEARCH]")
191f025a 585
08200510
RS
586(defvar isearch-string "") ; The current search string.
587(defvar isearch-message "") ; text-char-description version of isearch-string
588
c7015153
JB
589(defvar isearch-message-prefix-add nil) ; Additional text for the message prefix
590(defvar isearch-message-suffix-add nil) ; Additional text for the message suffix
ee9b85a8 591
ba653a53
JL
592(defvar isearch-success t) ; Searching is currently successful.
593(defvar isearch-error nil) ; Error message for failed search.
08200510
RS
594(defvar isearch-other-end nil) ; Start (end) of match if forward (backward).
595(defvar isearch-wrapped nil) ; Searching restarted from the top (bottom).
478615cc
LMI
596(defvar isearch-barrier 0
597 "Recorded minimum/maximal point for the current search.")
99ae9e9f 598(defvar isearch-just-started nil)
ddbe3d5f 599(defvar isearch-start-hscroll 0) ; hscroll when starting the search.
8e1cae6d 600
00c42405
SM
601;; case-fold-search while searching.
602;; either nil, t, or 'yes. 'yes means the same as t except that mixed
603;; case in the search string is ignored.
4453091d 604(defvar isearch-case-fold-search nil)
8e1cae6d 605
01dea85f
JL
606;; search-invisible while searching.
607;; either nil, t, or 'open. 'open means the same as t except that
608;; opens hidden overlays.
609(defvar isearch-invisible search-invisible)
610
67085aba
GM
611(defvar isearch-last-case-fold-search nil)
612
ae1a21c6
MB
613;; Used to save default value while isearch is active
614(defvar isearch-original-minibuffer-message-timeout nil)
615
8e1cae6d
JB
616(defvar isearch-adjusted nil)
617(defvar isearch-slow-terminal-mode nil)
191f025a 618;; If t, using a small window.
08200510 619(defvar isearch-small-window nil)
8e1cae6d 620(defvar isearch-opoint 0)
191f025a 621;; The window configuration active at the beginning of the search.
08200510 622(defvar isearch-window-configuration nil)
8e1cae6d 623
08200510
RS
624;; Flag to indicate a yank occurred, so don't move the cursor.
625(defvar isearch-yank-flag nil)
8e1cae6d 626
191f025a
SM
627;; A function to be called after each input character is processed.
628;; (It is not called after characters that exit the search.)
629;; It is only set from an optional argument to `isearch-mode'.
08200510 630(defvar isearch-op-fun nil)
8e1cae6d 631
191f025a 632;; Is isearch-mode in a recursive edit for modal searching.
08200510 633(defvar isearch-recursive-edit nil)
8e1cae6d 634
191f025a 635;; Should isearch be terminated after doing one search?
08200510
RS
636(defvar isearch-nonincremental nil)
637
638;; New value of isearch-forward after isearch-edit-string.
639(defvar isearch-new-forward nil)
8e1cae6d 640
0352b205
RS
641;; Accumulate here the overlays opened during searching.
642(defvar isearch-opened-overlays nil)
8e1cae6d 643
d5e61c1c
JL
644;; Non-nil if the string exists but is invisible.
645(defvar isearch-hidden nil)
646
99848db0
KH
647;; The value of input-method-function when isearch is invoked.
648(defvar isearch-input-method-function nil)
649
650;; A flag to tell if input-method-function is locally bound when
651;; isearch is invoked.
652(defvar isearch-input-method-local-p nil)
653
8e1cae6d
JB
654;; Minor-mode-alist changes - kind of redundant with the
655;; echo area, but if isearching in multiple windows, it can be useful.
656
657(or (assq 'isearch-mode minor-mode-alist)
658 (nconc minor-mode-alist
659 (list '(isearch-mode isearch-mode))))
660
08200510 661(defvar isearch-mode nil) ;; Name of the minor mode, if non-nil.
8e1cae6d
JB
662(make-variable-buffer-local 'isearch-mode)
663
fdf5afa4
RS
664(define-key global-map "\C-s" 'isearch-forward)
665(define-key esc-map "\C-s" 'isearch-forward-regexp)
666(define-key global-map "\C-r" 'isearch-backward)
667(define-key esc-map "\C-r" 'isearch-backward-regexp)
70bc5268 668(define-key search-map "w" 'isearch-forward-word)
b9cb2387 669(define-key search-map "_" 'isearch-forward-symbol)
e5e4a942 670(define-key search-map "." 'isearch-forward-symbol-at-point)
fdf5afa4 671
191f025a 672;; Entry points to isearch-mode.
8e1cae6d 673
eceee2c0 674(defun isearch-forward (&optional regexp-p no-recursive-edit)
8e1cae6d
JB
675 "\
676Do incremental search forward.
08200510
RS
677With a prefix argument, do an incremental regular expression search instead.
678\\<isearch-mode-map>
8e1cae6d 679As you type characters, they add to the search string and are found.
b48ca14f 680The following non-printing keys are bound in `isearch-mode-map'.
8e1cae6d 681
35904fd3 682Type \\[isearch-delete-char] to cancel last input item from end of search string.
8e1cae6d 683Type \\[isearch-exit] to exit, leaving point at location found.
08200510
RS
684Type LFD (C-j) to match end of line.
685Type \\[isearch-repeat-forward] to search again forward,\
686 \\[isearch-repeat-backward] to search again backward.
0ae85960
RS
687Type \\[isearch-yank-word-or-char] to yank next word or character in buffer
688 onto the end of the search string, and search for it.
74820eb5
JL
689Type \\[isearch-del-char] to delete character from end of search string.
690Type \\[isearch-yank-char] to yank char from buffer onto end of search\
08200510
RS
691 string and search for it.
692Type \\[isearch-yank-line] to yank rest of line onto end of search string\
693 and search for it.
75a07f2c 694Type \\[isearch-yank-kill] to yank the last string of killed text.
25666126
LL
695Type \\[isearch-yank-pop] to replace string just yanked into search prompt
696 with string killed before it.
8e1cae6d 697Type \\[isearch-quote-char] to quote control character to search for it.
da547b32
JL
698Type \\[isearch-char-by-name] to add a character to search by Unicode name,\
699 with completion.
08200510
RS
700\\[isearch-abort] while searching or when search has failed cancels input\
701 back to what has
702 been found successfully.
703\\[isearch-abort] when search is successful aborts and moves point to\
704 starting point.
8e1cae6d 705
b3612973
RS
706If you try to exit with the search string still empty, it invokes
707 nonincremental search.
708
6c551d4e 709Type \\[isearch-toggle-case-fold] to toggle search case-sensitivity.
01dea85f 710Type \\[isearch-toggle-invisible] to toggle search in invisible text.
6c551d4e 711Type \\[isearch-toggle-regexp] to toggle regular-expression mode.
70bc5268 712Type \\[isearch-toggle-word] to toggle word mode.
b9cb2387 713Type \\[isearch-toggle-symbol] to toggle symbol mode.
63dd1c6f
JL
714
715Type \\[isearch-toggle-lax-whitespace] to toggle whitespace matching.
716In incremental searches, a space or spaces normally matches any whitespace
717defined by the variable `search-whitespace-regexp'; see also the variables
718`isearch-lax-whitespace' and `isearch-regexp-lax-whitespace'.
719
6c551d4e
EZ
720Type \\[isearch-edit-string] to edit the search string in the minibuffer.
721
8e1cae6d
JB
722Also supported is a search ring of the previous 16 search strings.
723Type \\[isearch-ring-advance] to search for the next item in the search ring.
08200510
RS
724Type \\[isearch-ring-retreat] to search for the previous item in the search\
725 ring.
726Type \\[isearch-complete] to complete the search string using the search ring.
8e1cae6d 727
70bc5268
JL
728Type \\[isearch-query-replace] to run `query-replace' with string to\
729 replace from last search string.
730Type \\[isearch-query-replace-regexp] to run `query-replace-regexp'\
731 with the last search string.
732Type \\[isearch-occur] to run `occur' that shows\
733 the last search string.
734Type \\[isearch-highlight-regexp] to run `highlight-regexp'\
735 that highlights the last search string.
736
4b65254d
JB
737Type \\[isearch-describe-bindings] to display all Isearch key bindings.
738Type \\[isearch-describe-key] to display documentation of Isearch key.
739Type \\[isearch-describe-mode] to display documentation of Isearch mode.
b92368b4 740
1e14b095 741If an input method is turned on in the current buffer, that input
4b65254d
JB
742method is also active while you are typing characters to search.
743To toggle the input method, type \\[isearch-toggle-input-method]. \
744It also toggles the input
745method in the current buffer.
37b20c9b 746
4b65254d
JB
747To use a different input method for searching, type \
748\\[isearch-toggle-specified-input-method],
749and specify an input method you want to use.
37b20c9b 750
b48ca14f 751The above keys, bound in `isearch-mode-map', are often controlled by
35904fd3 752 options; do \\[apropos] on search-.* to find them.
8e1cae6d 753Other control and meta characters terminate the search
08200510 754 and are then executed normally (depending on `search-exit-option').
fd49e05c 755Likewise for function keys and mouse button events.
8e1cae6d 756
ac63ec89
JL
757If this function is called non-interactively with a nil NO-RECURSIVE-EDIT,
758it does not return to the calling function until the search is done.
759See the function `isearch-mode' for more information."
8e1cae6d 760
eceee2c0 761 (interactive "P\np")
4ae7d00a 762 (isearch-mode t (not (null regexp-p)) nil (not no-recursive-edit)))
8e1cae6d 763
eceee2c0 764(defun isearch-forward-regexp (&optional not-regexp no-recursive-edit)
dd7ffad6 765 "Do incremental search forward for regular expression.
08200510 766With a prefix argument, do a regular string search instead.
8938c0bc
JL
767Like ordinary incremental search except that your input is treated
768as a regexp. See the command `isearch-forward' for more information.
ab67e8b6 769
dd7ffad6 770In incremental searches, a space or spaces normally matches any
63dd1c6f
JL
771whitespace defined by the variable `search-whitespace-regexp'.
772To search for a literal space and nothing else, enter C-q SPC.
773To toggle whitespace matching, use `isearch-toggle-lax-whitespace'."
eceee2c0 774 (interactive "P\np")
4ae7d00a 775 (isearch-mode t (null not-regexp) nil (not no-recursive-edit)))
8e1cae6d 776
70bc5268 777(defun isearch-forward-word (&optional not-word no-recursive-edit)
dd7ffad6 778 "Do incremental search forward for a sequence of words.
70bc5268 779With a prefix argument, do a regular string search instead.
8938c0bc
JL
780Like ordinary incremental search except that your input is treated
781as a sequence of words without regard to how the words are separated.
782See the command `isearch-forward' for more information."
70bc5268
JL
783 (interactive "P\np")
784 (isearch-mode t nil nil (not no-recursive-edit) (null not-word)))
785
b9cb2387 786(defun isearch-forward-symbol (&optional not-symbol no-recursive-edit)
dd7ffad6 787 "Do incremental search forward for a symbol.
b9cb2387
JL
788The prefix argument is currently unused.
789Like ordinary incremental search except that your input is treated
790as a symbol surrounded by symbol boundary constructs \\_< and \\_>.
791See the command `isearch-forward' for more information."
792 (interactive "P\np")
793 (isearch-mode t nil nil (not no-recursive-edit) 'isearch-symbol-regexp))
794
eceee2c0 795(defun isearch-backward (&optional regexp-p no-recursive-edit)
dd7ffad6 796 "Do incremental search backward.
08200510 797With a prefix argument, do a regular expression search instead.
8938c0bc 798See the command `isearch-forward' for more information."
eceee2c0 799 (interactive "P\np")
4ae7d00a 800 (isearch-mode nil (not (null regexp-p)) nil (not no-recursive-edit)))
8e1cae6d 801
eceee2c0 802(defun isearch-backward-regexp (&optional not-regexp no-recursive-edit)
dd7ffad6 803 "Do incremental search backward for regular expression.
08200510 804With a prefix argument, do a regular string search instead.
8938c0bc
JL
805Like ordinary incremental search except that your input is treated
806as a regexp. See the command `isearch-forward' for more information."
eceee2c0 807 (interactive "P\np")
4ae7d00a 808 (isearch-mode nil (null not-regexp) nil (not no-recursive-edit)))
08200510 809
e5e4a942
JL
810(defun isearch-forward-symbol-at-point ()
811 "Do incremental search forward for a symbol found near point.
812Like ordinary incremental search except that the symbol found at point
813is added to the search string initially as a regexp surrounded
814by symbol boundary constructs \\_< and \\_>.
815See the command `isearch-forward-symbol' for more information."
816 (interactive)
817 (isearch-forward-symbol nil 1)
818 (let ((bounds (find-tag-default-bounds)))
819 (cond
820 (bounds
821 (when (< (car bounds) (point))
822 (goto-char (car bounds)))
823 (isearch-yank-string
824 (buffer-substring-no-properties (car bounds) (cdr bounds))))
825 (t
826 (setq isearch-error "No symbol at point")
827 (isearch-update)))))
828
8e1cae6d 829\f
8e1cae6d
JB
830;; isearch-mode only sets up incremental search for the minor mode.
831;; All the work is done by the isearch-mode commands.
832
08200510 833;; Not used yet:
d7fa5aa2 834;;(defvar isearch-commands '(isearch-forward isearch-backward
08200510
RS
835;; isearch-forward-regexp isearch-backward-regexp)
836;; "List of commands for which isearch-mode does not recursive-edit.")
b48ca14f 837
08200510 838
d5e61c1c 839(defun isearch-mode (forward &optional regexp op-fun recursive-edit word)
4b65254d 840 "Start Isearch minor mode.
ac63ec89
JL
841It is called by the function `isearch-forward' and other related functions.
842
843The non-nil arg FORWARD means searching in the forward direction.
844
845The non-nil arg REGEXP does an incremental regular expression search.
846
847The arg OP-FUN is a function to be called after each input character
848is processed. (It is not called after characters that exit the search.)
849
850When the arg RECURSIVE-EDIT is non-nil, this function behaves modally and
851does not return to the calling function until the search is completed.
852To behave this way it enters a recursive-edit and exits it when done
853isearching.
854
855The arg WORD, if t, does incremental search for a sequence of words,
856ignoring punctuation. If the value is a function, it is called to
857convert the search string to a regexp used by regexp search functions."
8e1cae6d
JB
858
859 ;; Initialize global vars.
860 (setq isearch-forward forward
861 isearch-regexp regexp
d5e61c1c 862 isearch-word word
8e1cae6d 863 isearch-op-fun op-fun
67085aba 864 isearch-last-case-fold-search isearch-case-fold-search
8e1cae6d 865 isearch-case-fold-search case-fold-search
01dea85f 866 isearch-invisible search-invisible
8e1cae6d
JB
867 isearch-string ""
868 isearch-message ""
869 isearch-cmds nil
870 isearch-success t
871 isearch-wrapped nil
872 isearch-barrier (point)
873 isearch-adjusted nil
874 isearch-yank-flag nil
ba653a53 875 isearch-error nil
0cabad13 876 isearch-slow-terminal-mode (and (<= baud-rate search-slow-speed)
8e1cae6d 877 (> (window-height)
fad241d3
RS
878 (* 4
879 (abs search-slow-window-lines))))
8e1cae6d
JB
880 isearch-other-end nil
881 isearch-small-window nil
99ae9e9f 882 isearch-just-started t
ddbe3d5f 883 isearch-start-hscroll (window-hscroll)
8e1cae6d 884
8e1cae6d 885 isearch-opoint (point)
a5dcf63f 886 search-ring-yank-pointer nil
0352b205 887 isearch-opened-overlays nil
99848db0
KH
888 isearch-input-method-function input-method-function
889 isearch-input-method-local-p (local-variable-p 'input-method-function)
ae1a21c6
MB
890 regexp-search-ring-yank-pointer nil
891
892 ;; Save the original value of `minibuffer-message-timeout', and
893 ;; set it to nil so that isearch's messages don't get timed out.
894 isearch-original-minibuffer-message-timeout minibuffer-message-timeout
895 minibuffer-message-timeout nil)
99848db0
KH
896
897 ;; We must bypass input method while reading key. When a user type
898 ;; printable character, appropriate input method is turned on in
380866a2 899 ;; minibuffer to read multibyte characters.
99848db0
KH
900 (or isearch-input-method-local-p
901 (make-local-variable 'input-method-function))
902 (setq input-method-function nil)
903
99ae9e9f 904 (looking-at "")
292a8dff
RS
905 (setq isearch-window-configuration
906 (if isearch-slow-terminal-mode (current-window-configuration) nil))
70418205 907
18ce7555
RS
908 ;; Maybe make minibuffer frame visible and/or raise it.
909 (let ((frame (window-frame (minibuffer-window))))
360e0dd5
RS
910 (unless (memq (frame-live-p frame) '(nil t))
911 (unless (frame-visible-p frame)
912 (make-frame-visible frame))
913 (if minibuffer-auto-raise
914 (raise-frame frame))))
18ce7555 915
8e1cae6d 916 (setq isearch-mode " Isearch") ;; forward? regexp?
1cd3732c 917 (force-mode-line-update)
8e1cae6d 918
c7955222 919 (setq overriding-terminal-local-map isearch-mode-map)
8e1cae6d 920 (run-hooks 'isearch-mode-hook)
40637410
JL
921
922 ;; Pushing the initial state used to be before running isearch-mode-hook,
923 ;; but a hook might set `isearch-push-state-function' used in
924 ;; `isearch-push-state' to save mode-specific initial state. (Bug#4994)
925 (isearch-push-state)
926
5ce25ae7 927 (isearch-update)
08200510 928
bfe2d334 929 (add-hook 'mouse-leave-buffer-hook 'isearch-done)
7e56ea04 930 (add-hook 'kbd-macro-termination-hook 'isearch-done)
6e5cd0ae 931
b48ca14f
JB
932 ;; isearch-mode can be made modal (in the sense of not returning to
933 ;; the calling function until searching is completed) by entering
08200510 934 ;; a recursive-edit and exiting it when done isearching.
130bf67c
RS
935 (if recursive-edit
936 (let ((isearch-recursive-edit t))
937 (recursive-edit)))
0daa17f3 938 isearch-success)
8e1cae6d
JB
939
940
8e1cae6d
JB
941;; Some high level utilities. Others below.
942
943(defun isearch-update ()
987a0a16
GM
944 "This is called after every isearch command to update the display.
945The last thing it does is to run `isearch-update-post-hook'."
4c01d4bd
RS
946 (if (and (null unread-command-events)
947 (null executing-kbd-macro))
8e1cae6d 948 (progn
c982ab21 949 (if (not (input-pending-p))
ac475d3a
JL
950 (if isearch-message-function
951 (funcall isearch-message-function)
952 (isearch-message)))
c982ab21 953 (if (and isearch-slow-terminal-mode
b48ca14f 954 (not (or isearch-small-window
c982ab21
GM
955 (pos-visible-in-window-p))))
956 (let ((found-point (point)))
957 (setq isearch-small-window t)
958 (move-to-window-line 0)
959 (let ((window-min-height 1))
960 (split-window nil (if (< search-slow-window-lines 0)
961 (1+ (- search-slow-window-lines))
962 (- (window-height)
963 (1+ search-slow-window-lines)))))
964 (if (< search-slow-window-lines 0)
965 (progn (vertical-motion (- 1 search-slow-window-lines))
966 (set-window-start (next-window) (point))
967 (set-window-hscroll (next-window)
968 (window-hscroll))
969 (set-window-hscroll (selected-window) 0))
970 (other-window 1))
ddbe3d5f
RS
971 (goto-char found-point))
972 ;; Keep same hscrolling as at the start of the search when possible
973 (let ((current-scroll (window-hscroll)))
974 (set-window-hscroll (selected-window) isearch-start-hscroll)
975 (unless (pos-visible-in-window-p)
976 (set-window-hscroll (selected-window) current-scroll))))
977 (if isearch-other-end
c982ab21
GM
978 (if (< isearch-other-end (point)) ; isearch-forward?
979 (isearch-highlight isearch-other-end (point))
980 (isearch-highlight (point) isearch-other-end))
dd7ffad6 981 (isearch-dehighlight))))
8e1cae6d
JB
982 (setq ;; quit-flag nil not for isearch-mode
983 isearch-adjusted nil
984 isearch-yank-flag nil)
f9114cec 985 (when isearch-lazy-highlight
d9dd1f33 986 (isearch-lazy-highlight-new-loop))
a5cc922e
KH
987 ;; We must prevent the point moving to the end of composition when a
988 ;; part of the composition has just been searched.
a6020335
MH
989 (setq disable-point-adjustment t)
990 (run-hooks 'isearch-update-post-hook))
8e1cae6d 991
0daa17f3 992(defun isearch-done (&optional nopush edit)
87e97673
RS
993 "Exit Isearch mode.
994For successful search, pass no args.
995For a failing search, NOPUSH is t.
996For going to the minibuffer to edit the search string,
997NOPUSH is t and EDIT is t."
998
00098b01 999 (if isearch-resume-in-command-history
6848c9f1
KS
1000 (let ((command `(isearch-resume ,isearch-string ,isearch-regexp
1001 ,isearch-word ,isearch-forward
1002 ,isearch-message
1003 ',isearch-case-fold-search)))
1004 (unless (equal (car command-history) command)
1005 (setq command-history (cons command command-history)))))
c40a4de1 1006
bfe2d334 1007 (remove-hook 'mouse-leave-buffer-hook 'isearch-done)
7e56ea04 1008 (remove-hook 'kbd-macro-termination-hook 'isearch-done)
d196f58d
GM
1009 (setq isearch-lazy-highlight-start nil)
1010
8e1cae6d 1011 ;; Called by all commands that terminate isearch-mode.
35a4d143 1012 ;; If NOPUSH is non-nil, we don't push the string on the search ring.
c7955222 1013 (setq overriding-terminal-local-map nil)
08200510 1014 ;; (setq pre-command-hook isearch-old-pre-command-hook) ; for lemacs
ae1a21c6 1015 (setq minibuffer-message-timeout isearch-original-minibuffer-message-timeout)
4dbbcb46 1016 (isearch-dehighlight)
46fe9018 1017 (lazy-highlight-cleanup lazy-highlight-cleanup)
08200510
RS
1018 (let ((found-start (window-start (selected-window)))
1019 (found-point (point)))
d8a4fc44
RS
1020 (when isearch-window-configuration
1021 (set-window-configuration isearch-window-configuration)
1022 (if isearch-small-window
1023 (goto-char found-point)
1024 ;; set-window-configuration clobbers window-start; restore it.
1025 ;; This has an annoying side effect of clearing the last_modiff
1026 ;; field of the window, which can cause unwanted scrolling,
1027 ;; so don't do it unless truly necessary.
1028 (set-window-start (selected-window) found-start t))))
08200510
RS
1029
1030 (setq isearch-mode nil)
99848db0
KH
1031 (if isearch-input-method-local-p
1032 (setq input-method-function isearch-input-method-function)
1033 (kill-local-variable 'input-method-function))
1034
1cd3732c 1035 (force-mode-line-update)
8e1cae6d 1036
df01192b
RS
1037 ;; If we ended in the middle of some intangible text,
1038 ;; move to the further end of that intangible text.
1039 (let ((after (if (eobp) nil
1040 (get-text-property (point) 'intangible)))
1041 (before (if (bobp) nil
1042 (get-text-property (1- (point)) 'intangible))))
1043 (when (and before after (eq before after))
7c2dc8bd
SM
1044 (goto-char
1045 (if isearch-forward
1046 (next-single-property-change (point) 'intangible)
1047 (previous-single-property-change (point) 'intangible)))))
df01192b 1048
35a4d143 1049 (if (and (> (length isearch-string) 0) (not nopush))
8e1cae6d 1050 ;; Update the ring data.
73d2bf95 1051 (isearch-update-ring isearch-string isearch-regexp))
8e1cae6d 1052
87e97673
RS
1053 (let ((isearch-mode-end-hook-quit (and nopush (not edit))))
1054 (run-hooks 'isearch-mode-end-hook))
071fdd66
JL
1055
1056 ;; If there was movement, mark the starting position.
4837b516 1057 ;; Maybe should test difference between and set mark only if > threshold.
071fdd66
JL
1058 (if (/= (point) isearch-opoint)
1059 (or (and transient-mark-mode mark-active)
1060 (progn
1061 (push-mark isearch-opoint t)
9a45d6c3 1062 (or executing-kbd-macro (> (minibuffer-depth) 0) edit
071fdd66
JL
1063 (message "Mark saved where search started")))))
1064
0daa17f3 1065 (and (not edit) isearch-recursive-edit (exit-recursive-edit)))
08200510 1066
73d2bf95
RS
1067(defun isearch-update-ring (string &optional regexp)
1068 "Add STRING to the beginning of the search ring.
a2b7dcc7 1069REGEXP if non-nil says use the regexp search ring."
77e5aef9
KS
1070 (add-to-history
1071 (if regexp 'regexp-search-ring 'search-ring)
1072 string
1073 (if regexp regexp-search-ring-max search-ring-max)))
73d2bf95 1074
191f025a
SM
1075;; Switching buffers should first terminate isearch-mode.
1076;; ;; For Emacs 19, the frame switch event is handled.
1077;; (defun isearch-switch-frame-handler ()
1078;; (interactive) ;; Is this necessary?
1079;; ;; First terminate isearch-mode.
1080;; (isearch-done)
1081;; (isearch-clean-overlays)
8989a920 1082;; (handle-switch-frame (car (cdr last-command-event))))
08200510 1083
8e1cae6d 1084\f
08e3de69
EZ
1085;; The search status structure and stack.
1086
7c2dc8bd
SM
1087(cl-defstruct (isearch--state
1088 (:constructor nil)
1089 (:copier nil)
1090 (:constructor isearch--get-state
1091 (&aux
1092 (string isearch-string)
1093 (message isearch-message)
1094 (point (point))
1095 (success isearch-success)
1096 (forward isearch-forward)
1097 (other-end isearch-other-end)
1098 (word isearch-word)
1099 (error isearch-error)
1100 (wrapped isearch-wrapped)
1101 (barrier isearch-barrier)
1102 (case-fold-search isearch-case-fold-search)
1103 (pop-fun (if isearch-push-state-function
1104 (funcall isearch-push-state-function))))))
1105 (string :read-only t)
1106 (message :read-only t)
1107 (point :read-only t)
1108 (success :read-only t)
1109 (forward :read-only t)
1110 (other-end :read-only t)
1111 (word :read-only t)
1112 (error :read-only t)
1113 (wrapped :read-only t)
1114 (barrier :read-only t)
1115 (case-fold-search :read-only t)
1116 (pop-fun :read-only t))
1117
1118(defun isearch--set-state (cmd)
1119 (setq isearch-string (isearch--state-string cmd)
1120 isearch-message (isearch--state-message cmd)
1121 isearch-success (isearch--state-success cmd)
1122 isearch-forward (isearch--state-forward cmd)
1123 isearch-other-end (isearch--state-other-end cmd)
1124 isearch-word (isearch--state-word cmd)
1125 isearch-error (isearch--state-error cmd)
1126 isearch-wrapped (isearch--state-wrapped cmd)
1127 isearch-barrier (isearch--state-barrier cmd)
1128 isearch-case-fold-search (isearch--state-case-fold-search cmd))
1129 (if (functionp (isearch--state-pop-fun cmd))
1130 (funcall (isearch--state-pop-fun cmd) cmd))
1131 (goto-char (isearch--state-point cmd)))
08e3de69
EZ
1132
1133(defun isearch-pop-state ()
1134 (setq isearch-cmds (cdr isearch-cmds))
7c2dc8bd 1135 (isearch--set-state (car isearch-cmds)))
08e3de69
EZ
1136
1137(defun isearch-push-state ()
7c2dc8bd 1138 (push (isearch--get-state) isearch-cmds))
08e3de69
EZ
1139
1140\f
8e1cae6d
JB
1141;; Commands active while inside of the isearch minor mode.
1142
1143(defun isearch-exit ()
1144 "Exit search normally.
1145However, if this is the first command after starting incremental
1146search and `search-nonincremental-instead' is non-nil, do a
08200510 1147nonincremental search instead via `isearch-edit-string'."
8e1cae6d 1148 (interactive)
b48ca14f 1149 (if (and search-nonincremental-instead
8e1cae6d 1150 (= 0 (length isearch-string)))
08200510
RS
1151 (let ((isearch-nonincremental t))
1152 (isearch-edit-string)))
0352b205
RS
1153 (isearch-done)
1154 (isearch-clean-overlays))
8e1cae6d 1155
06b60517 1156(defvar minibuffer-history-symbol) ;; from external package gmhist.el
8e1cae6d 1157
72779976
JL
1158(defun isearch-fail-pos (&optional msg)
1159 "Return position of first mismatch in search string, or nil if none.
9fc9a531 1160If MSG is non-nil, use variable `isearch-message', otherwise `isearch-string'."
72779976
JL
1161 (let ((cmds isearch-cmds)
1162 (curr-msg (if msg isearch-message isearch-string))
1163 succ-msg)
1164 (when (or (not isearch-success) isearch-error)
3ffa2d4f
DH
1165 (while (and cmds
1166 (or (not (isearch--state-success (car cmds)))
1167 (isearch--state-error (car cmds))))
b74aa22b 1168 (pop cmds))
7c2dc8bd
SM
1169 (setq succ-msg (and cmds (if msg (isearch--state-message (car cmds))
1170 (isearch--state-string (car cmds)))))
72779976
JL
1171 (if (and (stringp succ-msg)
1172 (< (length succ-msg) (length curr-msg))
1173 (equal succ-msg
1174 (substring curr-msg 0 (length succ-msg))))
1175 (length succ-msg)
1176 0))))
b74aa22b 1177
279f9b06
JL
1178(defmacro with-isearch-suspended (&rest body)
1179 "Exit Isearch mode, run BODY, and reinvoke the pending search.
1180You can update the global isearch variables by setting new values to
1181`isearch-new-string', `isearch-new-message', `isearch-new-forward',
1182`isearch-new-word', `isearch-new-case-fold'."
08200510
RS
1183 ;; This code is very hairy for several reasons, explained in the code.
1184 ;; Mainly, isearch-mode must be terminated while editing and then restarted.
1185 ;; If there were a way to catch any change of buffer from the minibuffer,
1186 ;; this could be simplified greatly.
eb8c3be9 1187 ;; Editing doesn't back up the search point. Should it?
279f9b06 1188 `(condition-case nil
e900ced4
RS
1189 (progn
1190 (let ((isearch-nonincremental isearch-nonincremental)
1191
1192 ;; Locally bind all isearch global variables to protect them
1193 ;; from recursive isearching.
1194 ;; isearch-string -message and -forward are not bound
1195 ;; so they may be changed. Instead, save the values.
1196 (isearch-new-string isearch-string)
1197 (isearch-new-message isearch-message)
1198 (isearch-new-forward isearch-forward)
1199 (isearch-new-word isearch-word)
80302a81 1200 (isearch-new-case-fold isearch-case-fold-search)
e900ced4
RS
1201
1202 (isearch-regexp isearch-regexp)
1203 (isearch-op-fun isearch-op-fun)
1204 (isearch-cmds isearch-cmds)
1205 (isearch-success isearch-success)
1206 (isearch-wrapped isearch-wrapped)
1207 (isearch-barrier isearch-barrier)
1208 (isearch-adjusted isearch-adjusted)
1209 (isearch-yank-flag isearch-yank-flag)
ba653a53 1210 (isearch-error isearch-error)
e900ced4
RS
1211 ;;; Don't bind this. We want isearch-search, below, to set it.
1212 ;;; And the old value won't matter after that.
1213 ;;; (isearch-other-end isearch-other-end)
1214 ;;; Perhaps some of these other variables should be bound for a
1215 ;;; shorter period, ending before the next isearch-search.
1216 ;;; But there doesn't seem to be a real bug, so let's not risk it now.
1217 (isearch-opoint isearch-opoint)
1218 (isearch-slow-terminal-mode isearch-slow-terminal-mode)
1219 (isearch-small-window isearch-small-window)
1220 (isearch-recursive-edit isearch-recursive-edit)
1221 ;; Save current configuration so we can restore it here.
1222 (isearch-window-configuration (current-window-configuration))
ae1a21c6 1223
93eb7113
JL
1224 ;; This could protect the index of the search rings,
1225 ;; but we can't reliably count the number of typed M-p
1226 ;; in `read-from-minibuffer' to adjust the index accordingly.
1227 ;; So when the following is commented out, `isearch-mode'
1228 ;; below resets the index to the predictable value nil.
1229 ;; (search-ring-yank-pointer search-ring-yank-pointer)
1230 ;; (regexp-search-ring-yank-pointer regexp-search-ring-yank-pointer)
1231
ae1a21c6
MB
1232 ;; Temporarily restore `minibuffer-message-timeout'.
1233 (minibuffer-message-timeout
1234 isearch-original-minibuffer-message-timeout)
1235 (isearch-original-minibuffer-message-timeout
1236 isearch-original-minibuffer-message-timeout)
02b99a17 1237 old-point old-other-end)
e900ced4
RS
1238
1239 ;; Actually terminate isearching until editing is done.
b48ca14f 1240 ;; This is so that the user can do anything without failure,
e900ced4 1241 ;; like switch buffers and start another isearch, and return.
06b60517 1242 (condition-case nil
e900ced4
RS
1243 (isearch-done t t)
1244 (exit nil)) ; was recursive editing
1245
02b99a17
JL
1246 ;; Save old point and isearch-other-end before reading from minibuffer
1247 ;; that can change their values.
1248 (setq old-point (point) old-other-end isearch-other-end)
1249
e900ced4 1250 (unwind-protect
279f9b06 1251 (progn ,@body)
02b99a17
JL
1252
1253 ;; Set point at the start (end) of old match if forward (backward),
1254 ;; so after exiting minibuffer isearch resumes at the start (end)
1255 ;; of this match and can find it again.
1256 (if (and old-other-end (eq old-point (point))
1257 (eq isearch-forward isearch-new-forward))
1258 (goto-char old-other-end))
1259
e900ced4 1260 ;; Always resume isearching by restarting it.
b48ca14f
JB
1261 (isearch-mode isearch-forward
1262 isearch-regexp
1263 isearch-op-fun
e900ced4
RS
1264 nil
1265 isearch-word)
1266
1267 ;; Copy new local values to isearch globals
1268 (setq isearch-string isearch-new-string
1269 isearch-message isearch-new-message
1270 isearch-forward isearch-new-forward
80302a81
JL
1271 isearch-word isearch-new-word
1272 isearch-case-fold-search isearch-new-case-fold))
e900ced4
RS
1273
1274 ;; Empty isearch-string means use default.
11dcdbb2
JL
1275 (when (= 0 (length isearch-string))
1276 (setq isearch-string (or (car (if isearch-regexp
1277 regexp-search-ring
1278 search-ring))
1279 "")
1280
1281 isearch-message
1282 (mapconcat 'isearch-text-char-description
1283 isearch-string ""))
1284 ;; After taking the last element, adjust ring to previous one.
1285 (isearch-ring-adjust1 nil)))
e900ced4 1286
a71a98cf
JL
1287 ;; This used to push the state as of before this C-s, but it adds
1288 ;; an inconsistent state where part of variables are from the
1289 ;; previous search (e.g. `isearch-success'), and part of variables
1290 ;; are just entered from the minibuffer (e.g. `isearch-string').
1291 ;; (isearch-push-state)
08200510
RS
1292
1293 ;; Reinvoke the pending search.
08200510 1294 (isearch-search)
a71a98cf 1295 (isearch-push-state) ; this pushes the correct state
08200510 1296 (isearch-update)
b48ca14f 1297 (if isearch-nonincremental
08200510
RS
1298 (progn
1299 ;; (sit-for 1) ;; needed if isearch-done does: (message "")
40c7f256
RS
1300 (isearch-done)
1301 ;; The search done message is confusing when the string
1302 ;; is empty, so erase it.
1303 (if (equal isearch-string "")
1304 (message "")))))
8e1cae6d 1305
08200510
RS
1306 (quit ; handle abort-recursive-edit
1307 (isearch-abort) ;; outside of let to restore outside global values
1308 )))
1309
279f9b06
JL
1310(defun isearch-edit-string ()
1311 "Edit the search string in the minibuffer.
1312The following additional command keys are active while editing.
1313\\<minibuffer-local-isearch-map>
1314\\[exit-minibuffer] to resume incremental searching with the edited string.
279f9b06
JL
1315\\[isearch-forward-exit-minibuffer] to resume isearching forward.
1316\\[isearch-reverse-exit-minibuffer] to resume isearching backward.
1317\\[isearch-complete-edit] to complete the search string using the search ring."
1318 (interactive)
1319 (with-isearch-suspended
1320 (let* ((message-log-max nil)
1321 ;; Don't add a new search string to the search ring here
1322 ;; in `read-from-minibuffer'. It should be added only
1323 ;; by `isearch-update-ring' called from `isearch-done'.
1324 (history-add-new-input nil)
1325 ;; Binding minibuffer-history-symbol to nil is a work-around
1326 ;; for some incompatibility with gmhist.
1327 (minibuffer-history-symbol))
1328 (setq isearch-new-string
1329 (read-from-minibuffer
1330 (isearch-message-prefix nil isearch-nonincremental)
1331 (cons isearch-string (1+ (or (isearch-fail-pos)
1332 (length isearch-string))))
1333 minibuffer-local-isearch-map nil
1334 (if isearch-regexp
1335 (cons 'regexp-search-ring
1336 (1+ (or regexp-search-ring-yank-pointer -1)))
1337 (cons 'search-ring
1338 (1+ (or search-ring-yank-pointer -1))))
1339 nil t)
1340 isearch-new-message
1341 (mapconcat 'isearch-text-char-description
1342 isearch-new-string "")))))
1343
08200510
RS
1344(defun isearch-nonincremental-exit-minibuffer ()
1345 (interactive)
1346 (setq isearch-nonincremental t)
1347 (exit-minibuffer))
6d65486d
JL
1348;; Changing the value of `isearch-nonincremental' has no effect here,
1349;; because `isearch-edit-string' ignores this change. Thus marked as obsolete.
1350(make-obsolete 'isearch-nonincremental-exit-minibuffer 'exit-minibuffer "24.4")
08200510
RS
1351
1352(defun isearch-forward-exit-minibuffer ()
6d65486d 1353 "Resume isearching forward from the minibuffer that edits the search string."
08200510
RS
1354 (interactive)
1355 (setq isearch-new-forward t)
1356 (exit-minibuffer))
8e1cae6d 1357
08200510 1358(defun isearch-reverse-exit-minibuffer ()
6d65486d 1359 "Resume isearching backward from the minibuffer that edits the search string."
8e1cae6d 1360 (interactive)
08200510
RS
1361 (setq isearch-new-forward nil)
1362 (exit-minibuffer))
1363
19993c54
RS
1364(defun isearch-cancel ()
1365 "Terminate the search and go back to the starting point."
1366 (interactive)
40637410
JL
1367 (if (and isearch-push-state-function isearch-cmds)
1368 ;; For defined push-state function, restore the first state.
1369 ;; This calls pop-state function and restores original point.
1370 (let ((isearch-cmds (last isearch-cmds)))
7c2dc8bd 1371 (isearch--set-state (car isearch-cmds)))
40637410 1372 (goto-char isearch-opoint))
7c2dc8bd 1373 (isearch-done t) ; Exit isearch..
0352b205 1374 (isearch-clean-overlays)
7c2dc8bd 1375 (signal 'quit nil)) ; ..and pass on quit signal.
08200510
RS
1376
1377(defun isearch-abort ()
b7852303 1378 "Abort incremental search mode if searching is successful, signaling quit.
08200510 1379Otherwise, revert to previous successful search and continue searching.
b7852303 1380Use `isearch-exit' to quit without signaling."
08200510 1381 (interactive)
7c2dc8bd 1382 ;; (ding) signal instead below, if quitting
8e1cae6d 1383 (discard-input)
d4119912
JL
1384 (if (and isearch-success (not isearch-error))
1385 ;; If search is successful and has no incomplete regexp,
1386 ;; move back to starting point and really do quit.
6a18e4e7
JL
1387 (progn
1388 (setq isearch-success nil)
1389 (isearch-cancel))
925a67ca
RS
1390 ;; If search is failing, or has an incomplete regexp,
1391 ;; rub out until it is once more successful.
ba653a53 1392 (while (or (not isearch-success) isearch-error)
925a67ca 1393 (isearch-pop-state))
8e1cae6d
JB
1394 (isearch-update)))
1395
8e1cae6d
JB
1396(defun isearch-repeat (direction)
1397 ;; Utility for isearch-repeat-forward and -backward.
1398 (if (eq isearch-forward (eq direction 'forward))
1399 ;; C-s in forward or C-r in reverse.
1400 (if (equal isearch-string "")
1401 ;; If search string is empty, use last one.
ece75c05
JL
1402 (if (null (if isearch-regexp regexp-search-ring search-ring))
1403 (setq isearch-error "No previous search string")
1404 (setq isearch-string
7c2dc8bd 1405 (car (if isearch-regexp regexp-search-ring search-ring))
ece75c05
JL
1406 isearch-message
1407 (mapconcat 'isearch-text-char-description
1408 isearch-string "")
11dcdbb2
JL
1409 isearch-case-fold-search isearch-last-case-fold-search)
1410 ;; After taking the last element, adjust ring to previous one.
1411 (isearch-ring-adjust1 nil))
8e1cae6d
JB
1412 ;; If already have what to search for, repeat it.
1413 (or isearch-success
02b2f510 1414 (progn
ba653a53
JL
1415 ;; Set isearch-wrapped before calling isearch-wrap-function
1416 (setq isearch-wrapped t)
6a18e4e7
JL
1417 (if isearch-wrap-function
1418 (funcall isearch-wrap-function)
ba653a53 1419 (goto-char (if isearch-forward (point-min) (point-max)))))))
8e1cae6d 1420 ;; C-s in reverse or C-r in forward, change direction.
f4c49513
RS
1421 (setq isearch-forward (not isearch-forward)
1422 isearch-success t))
8e1cae6d
JB
1423
1424 (setq isearch-barrier (point)) ; For subsequent \| if regexp.
6fefdc4b
RS
1425
1426 (if (equal isearch-string "")
1427 (setq isearch-success t)
512bfd85
RS
1428 (if (and isearch-success
1429 (equal (point) isearch-other-end)
99ae9e9f 1430 (not isearch-just-started))
8e1cae6d
JB
1431 ;; If repeating a search that found
1432 ;; an empty string, ensure we advance.
6fefdc4b
RS
1433 (if (if isearch-forward (eobp) (bobp))
1434 ;; If there's nowhere to advance to, fail (and wrap next time).
1435 (progn
1436 (setq isearch-success nil)
1437 (ding))
1438 (forward-char (if isearch-forward 1 -1))
1439 (isearch-search))
1440 (isearch-search)))
1441
8e1cae6d
JB
1442 (isearch-push-state)
1443 (isearch-update))
1444
1445(defun isearch-repeat-forward ()
1446 "Repeat incremental search forwards."
1447 (interactive)
1448 (isearch-repeat 'forward))
1449
1450(defun isearch-repeat-backward ()
1451 "Repeat incremental search backwards."
1452 (interactive)
1453 (isearch-repeat 'backward))
1454
1455(defun isearch-toggle-regexp ()
1456 "Toggle regexp searching on or off."
1457 ;; The status stack is left unchanged.
1458 (interactive)
1459 (setq isearch-regexp (not isearch-regexp))
08200510 1460 (if isearch-regexp (setq isearch-word nil))
d85519bb 1461 (setq isearch-success t isearch-adjusted t)
8e1cae6d
JB
1462 (isearch-update))
1463
70bc5268
JL
1464(defun isearch-toggle-word ()
1465 "Toggle word searching on or off."
7c2dc8bd 1466 ;; The status stack is left unchanged.
70bc5268
JL
1467 (interactive)
1468 (setq isearch-word (not isearch-word))
7c2dc8bd 1469 (if isearch-word (setq isearch-regexp nil))
70bc5268
JL
1470 (setq isearch-success t isearch-adjusted t)
1471 (isearch-update))
1472
b9cb2387
JL
1473(defun isearch-toggle-symbol ()
1474 "Toggle symbol searching on or off."
1475 (interactive)
1476 (setq isearch-word (unless (eq isearch-word 'isearch-symbol-regexp)
1477 'isearch-symbol-regexp))
826b3235 1478 (if isearch-word (setq isearch-regexp nil))
b9cb2387
JL
1479 (setq isearch-success t isearch-adjusted t)
1480 (isearch-update))
1481
63dd1c6f
JL
1482(defun isearch-toggle-lax-whitespace ()
1483 "Toggle whitespace matching in searching on or off.
1484In ordinary search, toggles the value of the variable
1485`isearch-lax-whitespace'. In regexp search, toggles the
1486value of the variable `isearch-regexp-lax-whitespace'."
1487 (interactive)
1488 (if isearch-regexp
1489 (setq isearch-regexp-lax-whitespace (not isearch-regexp-lax-whitespace))
1490 (setq isearch-lax-whitespace (not isearch-lax-whitespace)))
1491 (let ((message-log-max nil))
1492 (message "%s%s [%s]"
1493 (isearch-message-prefix nil isearch-nonincremental)
1494 isearch-message
1495 (if (if isearch-regexp
1496 isearch-regexp-lax-whitespace
1497 isearch-lax-whitespace)
1498 "match spaces loosely"
1499 "match spaces literally")))
1500 (setq isearch-success t isearch-adjusted t)
1501 (sit-for 1)
1502 (isearch-update))
1503
4453091d 1504(defun isearch-toggle-case-fold ()
01dea85f
JL
1505 "Toggle case folding in searching on or off.
1506Toggles the value of the variable `isearch-case-fold-search'."
4453091d
RS
1507 (interactive)
1508 (setq isearch-case-fold-search
1509 (if isearch-case-fold-search nil 'yes))
a56687f1
KH
1510 (let ((message-log-max nil))
1511 (message "%s%s [case %ssensitive]"
7c2dc8bd 1512 (isearch-message-prefix nil isearch-nonincremental)
a56687f1
KH
1513 isearch-message
1514 (if isearch-case-fold-search "in" "")))
d85519bb 1515 (setq isearch-success t isearch-adjusted t)
4453091d
RS
1516 (sit-for 1)
1517 (isearch-update))
1518
01dea85f
JL
1519(defun isearch-toggle-invisible ()
1520 "Toggle searching in invisible text on or off.
1521Toggles the variable `isearch-invisible' between values
1522nil and a non-nil value of the option `search-invisible'
1523\(or `open' if `search-invisible' is nil)."
1524 (interactive)
1525 (setq isearch-invisible
1526 (if isearch-invisible nil (or search-invisible 'open)))
1527 (let ((message-log-max nil))
1528 (message "%s%s [match %svisible text]"
1529 (isearch-message-prefix nil isearch-nonincremental)
1530 isearch-message
1531 (if isearch-invisible "in" "")))
1532 (setq isearch-success t isearch-adjusted t)
1533 (sit-for 1)
1534 (isearch-update))
1535
a0a79cde
JL
1536\f
1537;; Word search
1538
1539(defun word-search-regexp (string &optional lax)
1540 "Return a regexp which matches words, ignoring punctuation.
1541Given STRING, a string of words separated by word delimiters,
1542compute a regexp that matches those exact words separated by
1543arbitrary punctuation. If LAX is non-nil, the end of the string
1544need not match a word boundary unless it ends in whitespace.
1545
1546Used in `word-search-forward', `word-search-backward',
1547`word-search-forward-lax', `word-search-backward-lax'."
1548 (if (string-match-p "^\\W*$" string)
1549 ""
1550 (concat
1551 "\\b"
1552 (mapconcat 'identity (split-string string "\\W+" t) "\\W+")
1553 (if (or (not lax) (string-match-p "\\W$" string)) "\\b"))))
1554
1555(defun word-search-backward (string &optional bound noerror count)
1556 "Search backward from point for STRING, ignoring differences in punctuation.
1557Set point to the beginning of the occurrence found, and return point.
1558An optional second argument bounds the search; it is a buffer position.
1559The match found must not extend before that position.
1560Optional third argument, if t, means if fail just return nil (no error).
1561 If not nil and not t, move to limit of search and return nil.
1562Optional fourth argument is repeat count--search for successive occurrences.
1563
1564Relies on the function `word-search-regexp' to convert a sequence
1565of words in STRING to a regexp used to search words without regard
1566to punctuation."
1567 (interactive "sWord search backward: ")
1568 (re-search-backward (word-search-regexp string nil) bound noerror count))
1569
1570(defun word-search-forward (string &optional bound noerror count)
1571 "Search forward from point for STRING, ignoring differences in punctuation.
1572Set point to the end of the occurrence found, and return point.
1573An optional second argument bounds the search; it is a buffer position.
1574The match found must not extend after that position.
1575Optional third argument, if t, means if fail just return nil (no error).
1576 If not nil and not t, move to limit of search and return nil.
1577Optional fourth argument is repeat count--search for successive occurrences.
1578
1579Relies on the function `word-search-regexp' to convert a sequence
1580of words in STRING to a regexp used to search words without regard
1581to punctuation."
1582 (interactive "sWord search: ")
1583 (re-search-forward (word-search-regexp string nil) bound noerror count))
1584
1585(defun word-search-backward-lax (string &optional bound noerror count)
1586 "Search backward from point for STRING, ignoring differences in punctuation.
1587Set point to the beginning of the occurrence found, and return point.
1588
1589Unlike `word-search-backward', the end of STRING need not match a word
1590boundary, unless STRING ends in whitespace.
1591
1592An optional second argument bounds the search; it is a buffer position.
1593The match found must not extend before that position.
1594Optional third argument, if t, means if fail just return nil (no error).
1595 If not nil and not t, move to limit of search and return nil.
1596Optional fourth argument is repeat count--search for successive occurrences.
1597
1598Relies on the function `word-search-regexp' to convert a sequence
1599of words in STRING to a regexp used to search words without regard
1600to punctuation."
1601 (interactive "sWord search backward: ")
1602 (re-search-backward (word-search-regexp string t) bound noerror count))
1603
1604(defun word-search-forward-lax (string &optional bound noerror count)
1605 "Search forward from point for STRING, ignoring differences in punctuation.
1606Set point to the end of the occurrence found, and return point.
1607
1608Unlike `word-search-forward', the end of STRING need not match a word
1609boundary, unless STRING ends in whitespace.
1610
1611An optional second argument bounds the search; it is a buffer position.
1612The match found must not extend after that position.
1613Optional third argument, if t, means if fail just return nil (no error).
1614 If not nil and not t, move to limit of search and return nil.
1615Optional fourth argument is repeat count--search for successive occurrences.
1616
1617Relies on the function `word-search-regexp' to convert a sequence
1618of words in STRING to a regexp used to search words without regard
1619to punctuation."
1620 (interactive "sWord search: ")
1621 (re-search-forward (word-search-regexp string t) bound noerror count))
1622
b9cb2387
JL
1623;; Symbol search
1624
1625(defun isearch-symbol-regexp (string &optional lax)
1626 "Return a regexp which matches STRING as a symbol.
1627Creates a regexp where STRING is surrounded by symbol delimiters \\_< and \\_>.
1628If LAX is non-nil, the end of the string need not match a symbol boundary."
1629 (concat "\\_<" (regexp-quote string) (unless lax "\\_>")))
1630
1631(put 'isearch-symbol-regexp 'isearch-message-prefix "symbol ")
1632
63dd1c6f
JL
1633;; Search with lax whitespace
1634
1635(defun search-forward-lax-whitespace (string &optional bound noerror count)
1636 "Search forward for STRING, matching a sequence of whitespace chars."
1637 (let ((search-spaces-regexp search-whitespace-regexp))
1638 (re-search-forward (regexp-quote string) bound noerror count)))
1639
1640(defun search-backward-lax-whitespace (string &optional bound noerror count)
1641 "Search backward for STRING, matching a sequence of whitespace chars."
1642 (let ((search-spaces-regexp search-whitespace-regexp))
1643 (re-search-backward (regexp-quote string) bound noerror count)))
1644
1645(defun re-search-forward-lax-whitespace (regexp &optional bound noerror count)
1646 "Search forward for REGEXP, matching a sequence of whitespace chars."
1647 (let ((search-spaces-regexp search-whitespace-regexp))
1648 (re-search-forward regexp bound noerror count)))
1649
1650(defun re-search-backward-lax-whitespace (regexp &optional bound noerror count)
1651 "Search backward for REGEXP, matching a sequence of whitespace chars."
1652 (let ((search-spaces-regexp search-whitespace-regexp))
1653 (re-search-backward regexp bound noerror count)))
1654
a0a79cde 1655\f
9d14752f
JL
1656(defun isearch-query-replace (&optional delimited regexp-flag)
1657 "Start `query-replace' with string to replace from last search string.
1658The arg DELIMITED (prefix arg if interactive), if non-nil, means replace
1659only matches surrounded by word boundaries. Note that using the prefix arg
25173000
JL
1660is possible only when `isearch-allow-scroll' is non-nil, and it doesn't
1661always provide the correct matches for `query-replace', so the preferred
9d14752f
JL
1662way to run word replacements from Isearch is `M-s w ... M-%'."
1663 (interactive
1664 (list current-prefix-arg))
81e898ea 1665 (barf-if-buffer-read-only)
d85519bb 1666 (if regexp-flag (setq isearch-regexp t))
6e3057bb
JL
1667 (let ((case-fold-search isearch-case-fold-search)
1668 ;; set `search-upper-case' to nil to not call
1669 ;; `isearch-no-upper-case-p' in `perform-replace'
8d32ed64 1670 (search-upper-case nil)
01dea85f 1671 (search-invisible isearch-invisible)
826b3235 1672 (replace-lax-whitespace
3231d532
JL
1673 isearch-lax-whitespace)
1674 (replace-regexp-lax-whitespace
1675 isearch-regexp-lax-whitespace)
8d32ed64
JL
1676 ;; Set `isearch-recursive-edit' to nil to prevent calling
1677 ;; `exit-recursive-edit' in `isearch-done' that terminates
1678 ;; the execution of this command when it is non-nil.
1679 ;; We call `exit-recursive-edit' explicitly at the end below.
1680 (isearch-recursive-edit nil))
1681 (isearch-done nil t)
74820eb5 1682 (isearch-clean-overlays)
e7e4ea21
JL
1683 (if (and isearch-other-end
1684 (< isearch-other-end (point))
d85519bb 1685 (not (and transient-mark-mode mark-active
fa81f010 1686 (< (mark) (point)))))
d85519bb
JL
1687 (goto-char isearch-other-end))
1688 (set query-replace-from-history-variable
1689 (cons isearch-string
1690 (symbol-value query-replace-from-history-variable)))
81e898ea
SM
1691 (perform-replace
1692 isearch-string
d85519bb
JL
1693 (query-replace-read-to
1694 isearch-string
9d14752f
JL
1695 (concat "Query replace"
1696 (if (or delimited isearch-word) " word" "")
1697 (if isearch-regexp " regexp" "")
1698 (if (and transient-mark-mode mark-active) " in region" ""))
d85519bb 1699 isearch-regexp)
9d14752f 1700 t isearch-regexp (or delimited isearch-word) nil nil
d85519bb 1701 (if (and transient-mark-mode mark-active) (region-beginning))
8d32ed64
JL
1702 (if (and transient-mark-mode mark-active) (region-end))))
1703 (and isearch-recursive-edit (exit-recursive-edit)))
74820eb5 1704
9d14752f
JL
1705(defun isearch-query-replace-regexp (&optional delimited)
1706 "Start `query-replace-regexp' with string to replace from last search string.
1707See `isearch-query-replace' for more information."
1708 (interactive
1709 (list current-prefix-arg))
1710 (isearch-query-replace delimited t))
74820eb5 1711
6e3057bb 1712(defun isearch-occur (regexp &optional nlines)
0bd1e074
JL
1713 "Run `occur' using the last search string as the regexp.
1714Interactively, REGEXP is constructed using the search string from the
1715last search command. NLINES has the same meaning as in `occur'.
1716
1717If the last search command was a word search, REGEXP is computed from
1718the search words, ignoring punctuation. If the last search
1719command was a regular expression search, REGEXP is the regular
1720expression used in that search. If the last search command searched
1721for a literal string, REGEXP is constructed by quoting all the special
1722characters in that string."
6e3057bb 1723 (interactive
0bd1e074
JL
1724 (let* ((perform-collect (consp current-prefix-arg))
1725 (regexp (cond
d5e61c1c
JL
1726 ((functionp isearch-word)
1727 (funcall isearch-word isearch-string))
0bd1e074
JL
1728 (isearch-word (word-search-regexp isearch-string))
1729 (isearch-regexp isearch-string)
1730 (t (regexp-quote isearch-string)))))
1731 (list regexp
1732 (if perform-collect
1733 ;; Perform collect operation
1734 (if (zerop (regexp-opt-depth regexp))
1735 ;; No subexpression so collect the entire match.
1736 "\\&"
1737 ;; Get the regexp for collection pattern.
1738 (isearch-done nil t)
1739 (isearch-clean-overlays)
1740 (let ((default (car occur-collect-regexp-history)))
eb2deaff 1741 (read-regexp
0bd1e074 1742 (format "Regexp to collect (default %s): " default)
eb2deaff 1743 default 'occur-collect-regexp-history)))
0bd1e074
JL
1744 ;; Otherwise normal occur takes numerical prefix argument.
1745 (when current-prefix-arg
1746 (prefix-numeric-value current-prefix-arg))))))
6e3057bb 1747 (let ((case-fold-search isearch-case-fold-search)
3e8cd5ce
JL
1748 ;; Set `search-upper-case' to nil to not call
1749 ;; `isearch-no-upper-case-p' in `occur-1'.
1750 (search-upper-case nil)
63dd1c6f
JL
1751 (search-spaces-regexp
1752 (if (if isearch-regexp
1753 isearch-regexp-lax-whitespace
1754 isearch-lax-whitespace)
1755 search-whitespace-regexp)))
6e3057bb
JL
1756 (occur regexp nlines)))
1757
11c9f489
JL
1758(declare-function hi-lock-read-face-name "hi-lock" ())
1759
8938c0bc 1760(defun isearch-highlight-regexp ()
11c9f489 1761 "Run `highlight-regexp' with regexp from the current search string.
8938c0bc
JL
1762It exits Isearch mode and calls `hi-lock-face-buffer' with its regexp
1763argument from the last search regexp or a quoted search string,
1764and reads its face argument using `hi-lock-read-face-name'."
1765 (interactive)
8d32ed64
JL
1766 (let (
1767 ;; Set `isearch-recursive-edit' to nil to prevent calling
1768 ;; `exit-recursive-edit' in `isearch-done' that terminates
1769 ;; the execution of this command when it is non-nil.
1770 ;; We call `exit-recursive-edit' explicitly at the end below.
1771 (isearch-recursive-edit nil))
1772 (isearch-done nil t)
1773 (isearch-clean-overlays))
8938c0bc 1774 (require 'hi-lock nil t)
e5e4a942
JL
1775 (let ((regexp (cond ((functionp isearch-word)
1776 (funcall isearch-word isearch-string))
1777 (isearch-word (word-search-regexp isearch-string))
1778 (isearch-regexp isearch-string)
36bdf1ff
CY
1779 ((if (and (eq isearch-case-fold-search t)
1780 search-upper-case)
1781 (isearch-no-upper-case-p
1782 isearch-string isearch-regexp)
1783 isearch-case-fold-search)
1784 ;; Turn isearch-string into a case-insensitive
1785 ;; regexp.
9c7a6b15
CY
1786 (mapconcat
1787 (lambda (c)
1788 (let ((s (string c)))
1789 (if (string-match "[[:alpha:]]" s)
1790 (format "[%s%s]" (upcase s) (downcase s))
1791 (regexp-quote s))))
1792 isearch-string ""))
36bdf1ff 1793 (t (regexp-quote isearch-string)))))
e5e4a942 1794 (hi-lock-face-buffer regexp (hi-lock-read-face-name)))
8d32ed64 1795 (and isearch-recursive-edit (exit-recursive-edit)))
11c9f489 1796
74820eb5 1797\f
8e1cae6d 1798(defun isearch-delete-char ()
d6b8a1c0 1799 "Discard last input item and move point back.
8370def5
JL
1800Last input means the last character or the last isearch command
1801that added or deleted characters from the search string,
1802moved point, toggled regexp mode or case-sensitivity, etc.
8e1cae6d
JB
1803If no previous match was done, just beep."
1804 (interactive)
1805 (if (null (cdr isearch-cmds))
1806 (ding)
1807 (isearch-pop-state))
1808 (isearch-update))
1809
74820eb5
JL
1810(defun isearch-del-char (&optional arg)
1811 "Delete character from end of search string and search again.
8370def5
JL
1812Unlike `isearch-delete-char', it only deletes the last character,
1813but doesn't cancel the effect of other isearch command.
74820eb5
JL
1814If search string is empty, just beep."
1815 (interactive "p")
1816 (if (= 0 (length isearch-string))
35904fd3 1817 (ding)
74820eb5 1818 (setq isearch-string (substring isearch-string 0 (- (or arg 1)))
35904fd3 1819 isearch-message (mapconcat 'isearch-text-char-description
02b99a17
JL
1820 isearch-string "")))
1821 ;; Use the isearch-other-end as new starting point to be able
1822 ;; to find the remaining part of the search string again.
1823 (if isearch-other-end (goto-char isearch-other-end))
1824 (isearch-search)
1825 (isearch-push-state)
1826 (isearch-update))
8e1cae6d 1827
9cf081fa
KH
1828(defun isearch-yank-string (string)
1829 "Pull STRING into search string."
1830 ;; Downcase the string if not supposed to case-fold yanked strings.
1831 (if (and isearch-case-fold-search
1832 (eq 'not-yanks search-upper-case))
1833 (setq string (downcase string)))
1834 (if isearch-regexp (setq string (regexp-quote string)))
5d944a8f
JL
1835 ;; Don't move cursor in reverse search.
1836 (setq isearch-yank-flag t)
1837 (isearch-process-search-string
1838 string (mapconcat 'isearch-text-char-description string "")))
8e1cae6d 1839
30d47262
RS
1840(defun isearch-yank-kill ()
1841 "Pull string from kill ring into search string."
1842 (interactive)
9cf081fa
KH
1843 (isearch-yank-string (current-kill 0)))
1844
25666126
LL
1845(defun isearch-yank-pop ()
1846 "Replace just-yanked search string with previously killed string."
1847 (interactive)
1848 (if (not (memq last-command '(isearch-yank-kill isearch-yank-pop)))
1849 ;; Fall back on `isearch-yank-kill' for the benefits of people
1850 ;; who are used to the old behavior of `M-y' in isearch mode. In
1851 ;; future, this fallback may be changed if we ever change
1852 ;; `yank-pop' to do something like the kill-ring-browser.
1853 (isearch-yank-kill)
1854 (isearch-pop-state)
1855 (isearch-yank-string (current-kill 1))))
1856
9cf081fa 1857(defun isearch-yank-x-selection ()
117132a6 1858 "Pull current X selection into search string."
9cf081fa 1859 (interactive)
c935221f
SM
1860 (isearch-yank-string (x-get-selection))
1861 ;; If `x-get-selection' returned the text from the active region,
1862 ;; then it "used" the mark which we should hence deactivate.
1863 (when select-active-regions (deactivate-mark)))
8e1cae6d 1864
e4af1426 1865
191f025a 1866(defun isearch-mouse-2 (click)
e4af1426 1867 "Handle mouse-2 in Isearch mode.
117132a6 1868For a click in the echo area, invoke `isearch-yank-x-selection'.
f3b5dd74
DK
1869Otherwise invoke whatever the calling mouse-2 command sequence
1870is bound to outside of Isearch."
191f025a 1871 (interactive "e")
e4af1426
GM
1872 (let* ((w (posn-window (event-start click)))
1873 (overriding-terminal-local-map nil)
f3b5dd74 1874 (binding (key-binding (this-command-keys-vector) t)))
117132a6
DL
1875 (if (and (window-minibuffer-p w)
1876 (not (minibuffer-window-active-p w))) ; in echo area
1877 (isearch-yank-x-selection)
cd59ea72 1878 (when (functionp binding)
191f025a 1879 (call-interactively binding)))))
e4af1426 1880
29e53a0a
KF
1881(defun isearch-yank-internal (jumpform)
1882 "Pull the text from point to the point reached by JUMPFORM.
4b65254d
JB
1883JUMPFORM is a lambda expression that takes no arguments and returns
1884a buffer position, possibly having moved point to that position.
1885For example, it might move point forward by a word and return point,
1886or it might return the position of the end of the line."
9cf081fa
KH
1887 (isearch-yank-string
1888 (save-excursion
1889 (and (not isearch-forward) isearch-other-end
1890 (goto-char isearch-other-end))
29e53a0a
KF
1891 (buffer-substring-no-properties (point) (funcall jumpform)))))
1892
74820eb5
JL
1893(defun isearch-yank-char-in-minibuffer (&optional arg)
1894 "Pull next character from buffer into end of search string in minibuffer."
1895 (interactive "p")
1896 (if (eobp)
1897 (insert
00c42405 1898 (with-current-buffer (cadr (buffer-list))
74820eb5
JL
1899 (buffer-substring-no-properties
1900 (point) (progn (forward-char arg) (point)))))
1901 (forward-char arg)))
1902
1903(defun isearch-yank-char (&optional arg)
35904fd3 1904 "Pull next character from buffer into search string."
74820eb5
JL
1905 (interactive "p")
1906 (isearch-yank-internal (lambda () (forward-char arg) (point))))
29e53a0a 1907
1ddb2ea0 1908(declare-function subword-forward "subword" (&optional arg))
868bf43a 1909(defun isearch-yank-word-or-char ()
1ddb2ea0
MY
1910 "Pull next character, subword or word from buffer into search string.
1911Subword is used when `subword-mode' is activated. "
868bf43a 1912 (interactive)
21d90805 1913 (isearch-yank-internal
b48ca14f 1914 (lambda ()
21d90805
KF
1915 (if (or (= (char-syntax (or (char-after) 0)) ?w)
1916 (= (char-syntax (or (char-after (1+ (point))) 0)) ?w))
1ddb2ea0
MY
1917 (if (and (boundp 'subword-mode) subword-mode)
1918 (subword-forward 1)
1919 (forward-word 1))
21d90805 1920 (forward-char 1)) (point))))
868bf43a 1921
29e53a0a
KF
1922(defun isearch-yank-word ()
1923 "Pull next word from buffer into search string."
1924 (interactive)
1925 (isearch-yank-internal (lambda () (forward-word 1) (point))))
8e1cae6d
JB
1926
1927(defun isearch-yank-line ()
1928 "Pull rest of line from buffer into search string."
1929 (interactive)
a1883913 1930 (isearch-yank-internal
933f8467
RF
1931 (lambda () (let ((inhibit-field-text-motion t))
1932 (line-end-position (if (eolp) 2 1))))))
8e1cae6d 1933
da547b32
JL
1934(defun isearch-char-by-name ()
1935 "Read a character by its Unicode name and add it to the search string.
1936Completion is available like in `read-char-by-name' used by `insert-char'."
279f9b06
JL
1937 (interactive)
1938 (with-isearch-suspended
da547b32 1939 (let ((char (read-char-by-name "Add character to search (Unicode name or hex): ")))
279f9b06
JL
1940 (when char
1941 (setq isearch-new-string (concat isearch-string (string char))
1942 isearch-new-message (concat isearch-message
1943 (mapconcat 'isearch-text-char-description
1944 (string char) "")))))))
1945
8e1cae6d
JB
1946(defun isearch-search-and-update ()
1947 ;; Do the search and update the display.
191f025a 1948 (when (or isearch-success
35904fd3
JL
1949 ;; Unsuccessful regexp search may become successful by
1950 ;; addition of characters which make isearch-string valid
cd59ea72
SM
1951 isearch-regexp
1952 ;; If the string was found but was completely invisible,
1953 ;; it might now be partly visible, so try again.
1954 (prog1 isearch-hidden (setq isearch-hidden nil)))
8e1cae6d
JB
1955 ;; In reverse search, adding stuff at
1956 ;; the end may cause zero or many more chars to be
1957 ;; matched, in the string following point.
1958 ;; Allow all those possibilities without moving point as
1959 ;; long as the match does not extend past search origin.
1960 (if (and (not isearch-forward) (not isearch-adjusted)
1961 (condition-case ()
99ae9e9f 1962 (let ((case-fold-search isearch-case-fold-search))
ec0a2f45
KH
1963 (if (and (eq case-fold-search t) search-upper-case)
1964 (setq case-fold-search
1965 (isearch-no-upper-case-p isearch-string isearch-regexp)))
02b16839 1966 (looking-at (cond
d5e61c1c
JL
1967 ((functionp isearch-word)
1968 (funcall isearch-word isearch-string t))
02b16839
JL
1969 (isearch-word (word-search-regexp isearch-string t))
1970 (isearch-regexp isearch-string)
1971 (t (regexp-quote isearch-string)))))
8e1cae6d 1972 (error nil))
bd6a8414 1973 (or isearch-yank-flag
191f025a 1974 (<= (match-end 0)
bd6a8414 1975 (min isearch-opoint isearch-barrier))))
d64b1d96 1976 (progn
191f025a 1977 (setq isearch-success t
ba653a53 1978 isearch-error nil
d64b1d96
RS
1979 isearch-other-end (match-end 0))
1980 (if (and (eq isearch-case-fold-search t) search-upper-case)
1981 (setq isearch-case-fold-search
1982 (isearch-no-upper-case-p isearch-string isearch-regexp))))
8e1cae6d
JB
1983 ;; Not regexp, not reverse, or no match at point.
1984 (if (and isearch-other-end (not isearch-adjusted))
1985 (goto-char (if isearch-forward isearch-other-end
191f025a
SM
1986 (min isearch-opoint
1987 isearch-barrier
8e1cae6d
JB
1988 (1+ isearch-other-end)))))
1989 (isearch-search)
1990 ))
1991 (isearch-push-state)
1992 (if isearch-op-fun (funcall isearch-op-fun))
1993 (isearch-update))
1994
1995
08e3de69 1996;; *, ?, }, and | chars can make a regexp more liberal.
08200510 1997;; They can make a regexp match sooner or make it succeed instead of failing.
8e1cae6d
JB
1998;; So go back to place last successful search started
1999;; or to the last ^S/^R (barrier), whichever is nearer.
08200510 2000;; + needs no special handling because the string must match at least once.
8e1cae6d 2001
08e3de69
EZ
2002(defun isearch-backslash (str)
2003 "Return t if STR ends in an odd number of backslashes."
2004 (= (mod (- (length str) (string-match "\\\\*\\'" str)) 2) 1))
2005
2006(defun isearch-fallback (want-backslash &optional allow-invalid to-barrier)
2007 "Return point to previous successful match to allow regexp liberalization.
2008\\<isearch-mode-map>
4b65254d 2009Respects \\[isearch-repeat-forward] and \\[isearch-repeat-backward] by \
08e3de69
EZ
2010stopping at `isearch-barrier' as needed.
2011
6f2df0f4
JL
2012Do nothing if a backslash is escaping the liberalizing character.
2013If WANT-BACKSLASH is non-nil, invert this behavior (for \\} and \\|).
08e3de69 2014
6f2df0f4
JL
2015Do nothing if regexp has recently been invalid unless optional
2016ALLOW-INVALID non-nil.
08e3de69 2017
6f2df0f4
JL
2018If optional TO-BARRIER non-nil, ignore previous matches and go exactly
2019to the barrier."
08e3de69
EZ
2020 ;; (eq (not a) (not b)) makes all non-nil values equivalent
2021 (when (and isearch-regexp (eq (not (isearch-backslash isearch-string))
2022 (not want-backslash))
2023 ;; We have to check 2 stack frames because the last might be
2024 ;; invalid just because of a backslash.
ba653a53 2025 (or (not isearch-error)
7c2dc8bd 2026 (not (isearch--state-error (cadr isearch-cmds)))
08e3de69
EZ
2027 allow-invalid))
2028 (if to-barrier
2029 (progn (goto-char isearch-barrier)
2030 (setq isearch-adjusted t))
2031 (let* ((stack isearch-cmds)
2032 (previous (cdr stack)) ; lookbelow in the stack
2033 (frame (car stack)))
2034 ;; Walk down the stack looking for a valid regexp (as of course only
2035 ;; they can be the previous successful match); this conveniently
2036 ;; removes all bracket-sets and groups that might be in the way, as
2037 ;; well as partial \{\} constructs that the code below leaves behind.
2038 ;; Also skip over postfix operators -- though horrid,
9b9a4122 2039 ;; 'ab?\{5,6\}+\{1,2\}*' is perfectly valid.
08e3de69 2040 (while (and previous
7c2dc8bd
SM
2041 (or (isearch--state-error frame)
2042 (let* ((string (isearch--state-string frame))
08e3de69
EZ
2043 (lchar (aref string (1- (length string)))))
2044 ;; The operators aren't always operators; check
2045 ;; backslashes. This doesn't handle the case of
2046 ;; operators at the beginning of the regexp not
2047 ;; being special, but then we should fall back to
2048 ;; the barrier anyway because it's all optional.
2049 (if (isearch-backslash
7c2dc8bd 2050 (isearch--state-string (car previous)))
08e3de69
EZ
2051 (eq lchar ?\})
2052 (memq lchar '(?* ?? ?+))))))
2053 (setq stack previous previous (cdr previous) frame (car stack)))
2054 (when stack
2055 ;; `stack' now refers the most recent valid regexp that is not at
2056 ;; all optional in its last term. Now dig one level deeper and find
2057 ;; what matched before that.
5a1f9fcf
JL
2058 (let ((last-other-end
2059 (or (and (car previous)
7c2dc8bd 2060 (isearch--state-other-end (car previous)))
5a1f9fcf 2061 isearch-barrier)))
08e3de69
EZ
2062 (goto-char (if isearch-forward
2063 (max last-other-end isearch-barrier)
2064 (min last-other-end isearch-barrier)))
6f2df0f4 2065 (setq isearch-adjusted t)))))))
8e1cae6d 2066
6b61353c
KH
2067(defun isearch-unread-key-sequence (keylist)
2068 "Unread the given key-sequence KEYLIST.
2069Scroll-bar or mode-line events are processed appropriately."
2070 (cancel-kbd-macro-events)
2071 (apply 'isearch-unread keylist)
2072 ;; If the event was a scroll-bar or mode-line click, the event will have
2073 ;; been prefixed by a symbol such as vertical-scroll-bar. We must remove
2074 ;; it here, because this symbol will be attached to the event again next
2075 ;; time it gets read by read-key-sequence.
2076 ;;
2077 ;; (Old comment from isearch-other-meta-char: "Note that we don't have to
2078 ;; modify the event anymore in 21 because read_key_sequence no longer
2079 ;; modifies events to produce fake prefix keys.")
2080 (if (and (> (length keylist) 1)
2081 (symbolp (car keylist))
2082 (listp (cadr keylist))
2083 (not (numberp (posn-point
2084 (event-start (cadr keylist) )))))
2085 (pop unread-command-events)))
2086
2087;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2088;; scrolling within Isearch mode. Alan Mackenzie (acm@muc.de), 2003/2/24
2089;;
2090;; The idea here is that certain vertical scrolling commands (like C-l
2091;; `recenter') should be usable WITHIN Isearch mode. For a command to be
2092;; suitable, it must NOT alter the buffer, swap to another buffer or frame,
2093;; tamper with isearch's state, or move point. It is unacceptable for the
2094;; search string to be scrolled out of the current window. If a command
2095;; attempts this, we scroll the text back again.
2096;;
2097;; We implement this feature with a property called `isearch-scroll'.
ad40eec5
JL
2098;; If a command's symbol has the value t for this property or for the
2099;; `scroll-command' property, it is a scrolling command. The feature
2100;; needs to be enabled by setting the customizable variable
2101;; `isearch-allow-scroll' to a non-nil value.
6b61353c
KH
2102;;
2103;; The universal argument commands (e.g. C-u) in simple.el are marked
2104;; as scrolling commands, and isearch.el has been amended to allow
2105;; prefix arguments to be passed through to scrolling commands. Thus
2106;; M-0 C-l will scroll point to the top of the window.
2107;;
2108;; Horizontal scrolling commands are currently not catered for.
2109;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2110
2111;; Set the isearch-scroll property on some standard functions:
2112;; Scroll-bar functions:
2113(if (fboundp 'scroll-bar-toolkit-scroll)
2114 (put 'scroll-bar-toolkit-scroll 'isearch-scroll t))
6b61353c
KH
2115(if (fboundp 'w32-handle-scroll-bar-event)
2116 (put 'w32-handle-scroll-bar-event 'isearch-scroll t))
2117
ad40eec5
JL
2118;; Commands which scroll the window (some scroll commands
2119;; already have the `scroll-command' property on them):
6b61353c 2120(put 'recenter 'isearch-scroll t)
21d9ed8b 2121(put 'recenter-top-bottom 'isearch-scroll t)
6b61353c 2122(put 'reposition-window 'isearch-scroll t)
6b61353c
KH
2123
2124;; Commands which act on the other window
2125(put 'list-buffers 'isearch-scroll t)
2126(put 'scroll-other-window 'isearch-scroll t)
2127(put 'scroll-other-window-down 'isearch-scroll t)
2128(put 'beginning-of-buffer-other-window 'isearch-scroll t)
2129(put 'end-of-buffer-other-window 'isearch-scroll t)
2130
2131;; Commands which change the window layout
2132(put 'delete-other-windows 'isearch-scroll t)
2133(put 'balance-windows 'isearch-scroll t)
2d197ffb
CY
2134(put 'split-window-right 'isearch-scroll t)
2135(put 'split-window-below 'isearch-scroll t)
2136(put 'enlarge-window 'isearch-scroll t)
2137
2138;; Aliases for split-window-*
6b61353c 2139(put 'split-window-vertically 'isearch-scroll t)
7f55d3b7 2140(put 'split-window-horizontally 'isearch-scroll t)
6b61353c
KH
2141
2142;; Universal argument commands
2143(put 'universal-argument 'isearch-scroll t)
2144(put 'negative-argument 'isearch-scroll t)
2145(put 'digit-argument 'isearch-scroll t)
2146
2147(defcustom isearch-allow-scroll nil
9425f8e1
CY
2148 "Whether scrolling is allowed during incremental search.
2149If non-nil, scrolling commands can be used in Isearch mode.
2150However, the current match will never scroll offscreen.
a4b000fb 2151If nil, scrolling commands will first cancel Isearch mode."
6b61353c
KH
2152 :type 'boolean
2153 :group 'isearch)
2154
2155(defun isearch-string-out-of-window (isearch-point)
2156 "Test whether the search string is currently outside of the window.
2157Return nil if it's completely visible, or if point is visible,
2158together with as much of the search string as will fit; the symbol
2159`above' if we need to scroll the text downwards; the symbol `below',
2160if upwards."
2161 (let ((w-start (window-start))
2162 (w-end (window-end nil t))
2163 (w-L1 (save-excursion (move-to-window-line 1) (point)))
2164 (w-L-1 (save-excursion (move-to-window-line -1) (point)))
2165 start end) ; start and end of search string in buffer
2166 (if isearch-forward
2167 (setq end isearch-point start (or isearch-other-end isearch-point))
2168 (setq start isearch-point end (or isearch-other-end isearch-point)))
2169 (cond ((or (and (>= start w-start) (<= end w-end))
2170 (if isearch-forward
2171 (and (>= isearch-point w-L-1) (< isearch-point w-end)) ; point on Line -1
2172 (and (>= isearch-point w-start) (< isearch-point w-L1)))) ; point on Line 0
2173 nil)
2174 ((and (< start w-start)
2175 (< isearch-point w-L-1))
2176 'above)
2177 (t 'below))))
2178
2179(defun isearch-back-into-window (above isearch-point)
2180 "Scroll the window to bring the search string back into view.
2181Restore point to ISEARCH-POINT in the process. ABOVE is t when the
2182search string is above the top of the window, nil when it is beneath
2183the bottom."
2184 (let (start end)
2185 (if isearch-forward
2186 (setq end isearch-point start (or isearch-other-end isearch-point))
2187 (setq start isearch-point end (or isearch-other-end isearch-point)))
2188 (if above
2189 (progn
2190 (goto-char start)
2191 (recenter 0)
2192 (when (>= isearch-point (window-end nil t))
2193 (goto-char isearch-point)
2194 (recenter -1)))
2195 (goto-char end)
2196 (recenter -1)
2197 (when (< isearch-point (window-start))
2198 (goto-char isearch-point)
2199 (recenter 0))))
2200 (goto-char isearch-point))
2201
2202(defun isearch-reread-key-sequence-naturally (keylist)
e9a452d9 2203 "Reread key sequence KEYLIST with an inactive Isearch-mode keymap.
6b61353c
KH
2204Return the key sequence as a string/vector."
2205 (isearch-unread-key-sequence keylist)
2206 (let (overriding-terminal-local-map)
2207 (read-key-sequence nil))) ; This will go through function-key-map, if nec.
2208
2209(defun isearch-lookup-scroll-key (key-seq)
2210 "If KEY-SEQ is bound to a scrolling command, return it as a symbol.
2211Otherwise return nil."
2212 (let* ((overriding-terminal-local-map nil)
2213 (binding (key-binding key-seq)))
2214 (and binding (symbolp binding) (commandp binding)
ad40eec5
JL
2215 (or (eq (get binding 'isearch-scroll) t)
2216 (eq (get binding 'scroll-command) t))
6b61353c 2217 binding)))
8e1cae6d 2218
b457cc3b 2219(defalias 'isearch-other-control-char 'isearch-other-meta-char)
8e1cae6d 2220
6b61353c
KH
2221(defun isearch-other-meta-char (&optional arg)
2222 "Process a miscellaneous key sequence in Isearch mode.
2223
2224Try to convert the current key-sequence to something usable in Isearch
2225mode, either by converting it with `function-key-map', downcasing a
2226key with C-<upper case>, or finding a \"scrolling command\" bound to
2227it. \(In the last case, we may have to read more events.) If so,
2228either unread the converted sequence or execute the command.
2229
2230Otherwise, if `search-exit-option' is non-nil (the default) unread the
2231key-sequence and exit the search normally. If it is the symbol
2232`edit', the search string is edited in the minibuffer and the meta
2233character is unread so that it applies to editing the string.
2234
2235ARG is the prefix argument. It will be transmitted through to the
2236scrolling command or to the command whose key-sequence exits
2237Isearch mode."
2238 (interactive "P")
2239 (let* ((key (if current-prefix-arg ; not nec the same as ARG
2240 (substring (this-command-keys) universal-argument-num-events)
2241 (this-command-keys)))
c6431f12 2242 (main-event (aref key 0))
6b61353c
KH
2243 (keylist (listify-key-sequence key))
2244 scroll-command isearch-point)
24b5caec 2245 (cond ((and (= (length key) 1)
c40bb1ba 2246 (let ((lookup (lookup-key local-function-key-map key)))
3f866b53
KH
2247 (not (or (null lookup) (integerp lookup)
2248 (keymapp lookup)))))
24b5caec
RS
2249 ;; Handle a function key that translates into something else.
2250 ;; If the key has a global definition too,
2251 ;; exit and unread the key itself, so its global definition runs.
2252 ;; Otherwise, unread the translation,
2253 ;; so that the translated key takes effect within isearch.
d94eb6eb 2254 (cancel-kbd-macro-events)
24b5caec 2255 (if (lookup-key global-map key)
02b2f510 2256 (progn
24b5caec 2257 (isearch-done)
d809b8eb 2258 (setq prefix-arg arg)
24b5caec 2259 (apply 'isearch-unread keylist))
a5cc922e 2260 (setq keylist
b5dd9a77
CY
2261 (listify-key-sequence
2262 (lookup-key local-function-key-map key)))
a5cc922e
KH
2263 (while keylist
2264 (setq key (car keylist))
2265 ;; If KEY is a printing char, we handle it here
2266 ;; directly to avoid the input method and keyboard
2267 ;; coding system translating it.
2268 (if (and (integerp key)
6480c508 2269 (>= key ?\s) (/= key 127) (< key 256))
a5cc922e 2270 (progn
ce69a844
CY
2271 ;; Ensure that the processed char is recorded in
2272 ;; the keyboard macro, if any (Bug#4894)
b5dd9a77 2273 (store-kbd-macro-event key)
a5cc922e
KH
2274 (isearch-process-search-char key)
2275 (setq keylist (cdr keylist)))
2276 ;; As the remaining keys in KEYLIST can't be handled
2277 ;; here, we must reread them.
d809b8eb 2278 (setq prefix-arg arg)
a5cc922e
KH
2279 (apply 'isearch-unread keylist)
2280 (setq keylist nil)))))
24b5caec 2281 (
c6431f12
KH
2282 ;; Handle an undefined shifted control character
2283 ;; by downshifting it if that makes it defined.
2284 ;; (As read-key-sequence would normally do,
2285 ;; if we didn't have a default definition.)
2286 (let ((mods (event-modifiers main-event)))
2287 (and (integerp main-event)
2288 (memq 'shift mods)
2289 (memq 'control mods)
f438bf5c
EZ
2290 (not (memq (lookup-key isearch-mode-map
2291 (let ((copy (copy-sequence key)))
2292 (aset copy 0
2293 (- main-event
2294 (- ?\C-\S-a ?\C-a)))
2295 copy)
2296 nil)
2297 '(nil
2298 isearch-other-control-char)))))
c6431f12 2299 (setcar keylist (- main-event (- ?\C-\S-a ?\C-a)))
d94eb6eb 2300 (cancel-kbd-macro-events)
d809b8eb 2301 (setq prefix-arg arg)
c6431f12
KH
2302 (apply 'isearch-unread keylist))
2303 ((eq search-exit-option 'edit)
d809b8eb 2304 (setq prefix-arg arg)
c6431f12
KH
2305 (apply 'isearch-unread keylist)
2306 (isearch-edit-string))
6b61353c
KH
2307 ;; Handle a scrolling function.
2308 ((and isearch-allow-scroll
2309 (progn (setq key (isearch-reread-key-sequence-naturally keylist))
2310 (setq keylist (listify-key-sequence key))
2311 (setq main-event (aref key 0))
2312 (setq scroll-command (isearch-lookup-scroll-key key))))
2313 ;; From this point onwards, KEY, KEYLIST and MAIN-EVENT hold a
2314 ;; complete key sequence, possibly as modified by function-key-map,
2315 ;; not merely the one or two event fragment which invoked
2316 ;; isearch-other-meta-char in the first place.
2317 (setq isearch-point (point))
2318 (setq prefix-arg arg)
2319 (command-execute scroll-command)
2320 (let ((ab-bel (isearch-string-out-of-window isearch-point)))
2321 (if ab-bel
35904fd3 2322 (isearch-back-into-window (eq ab-bel 'above) isearch-point)
d85519bb 2323 (goto-char isearch-point)))
6b61353c 2324 (isearch-update))
72a69d7f
JL
2325 ;; A mouse click on the isearch message starts editing the search string
2326 ((and (eq (car-safe main-event) 'down-mouse-1)
2327 (window-minibuffer-p (posn-window (event-start main-event))))
2328 ;; Swallow the up-event.
2329 (read-event)
2330 (isearch-edit-string))
c6431f12
KH
2331 (search-exit-option
2332 (let (window)
d809b8eb 2333 (setq prefix-arg arg)
6b61353c
KH
2334 (isearch-unread-key-sequence keylist)
2335 (setq main-event (car unread-command-events))
3fb01f36 2336
346f18dc
GM
2337 ;; If we got a mouse click event, that event contains the
2338 ;; window clicked on. maybe it was read with the buffer
c6431f12
KH
2339 ;; it was clicked on. If so, that buffer, not the current one,
2340 ;; is in isearch mode. So end the search in that buffer.
346f18dc
GM
2341
2342 ;; ??? I have no idea what this if checks for, but it's
2343 ;; obviously wrong for the case that a down-mouse event
2344 ;; on another window invokes this function. The event
2345 ;; will contain the window clicked on and that window's
b48ca14f 2346 ;; buffer is certainly not always in Isearch mode.
346f18dc
GM
2347 ;;
2348 ;; Leave the code in, but check for current buffer not
2349 ;; being in Isearch mode for now, until someone tells
2350 ;; what it's really supposed to do.
2351 ;;
2352 ;; --gerd 2001-08-10.
2353
2354 (if (and (not isearch-mode)
2355 (listp main-event)
c6431f12 2356 (setq window (posn-window (event-start main-event)))
4fd017e7
RS
2357 (windowp window)
2358 (or (> (minibuffer-depth) 0)
2359 (not (window-minibuffer-p window))))
00c42405 2360 (with-current-buffer (window-buffer window)
0352b205
RS
2361 (isearch-done)
2362 (isearch-clean-overlays))
2363 (isearch-done)
6b61353c
KH
2364 (isearch-clean-overlays)
2365 (setq prefix-arg arg))))
2366 (t;; otherwise nil
c6431f12 2367 (isearch-process-search-string key key)))))
8e1cae6d 2368
8e1cae6d
JB
2369(defun isearch-quote-char ()
2370 "Quote special characters for incremental search."
2371 (interactive)
8bc15fa8 2372 (let ((char (read-quoted-char (isearch-message t))))
3c75023f 2373 ;; Assume character codes 0200 - 0377 stand for characters in some
c20d35d5
KH
2374 ;; single-byte character set, and convert them to Emacs
2375 ;; characters.
63dd1c6f 2376 (if (and isearch-regexp isearch-regexp-lax-whitespace (= char ?\s))
be02a7ed 2377 (if (subregexp-context-p isearch-string (length isearch-string))
30bb1443
SM
2378 (isearch-process-search-string "[ ]" " ")
2379 (isearch-process-search-char char))
ab67e8b6
RS
2380 (and enable-multibyte-characters
2381 (>= char ?\200)
2382 (<= char ?\377)
2383 (setq char (unibyte-char-to-multibyte char)))
2384 (isearch-process-search-char char))))
8e1cae6d 2385
8e1cae6d 2386(defun isearch-printing-char ()
b68c164d 2387 "Add this ordinary printing character to the search string and search."
8e1cae6d 2388 (interactive)
8989a920 2389 (let ((char last-command-event))
45b94eb2 2390 (if (= char ?\S-\ )
6480c508 2391 (setq char ?\s))
758710cb
KH
2392 (if current-input-method
2393 (isearch-process-search-multibyte-characters char)
2394 (isearch-process-search-char char))))
8e1cae6d 2395
8e1cae6d 2396(defun isearch-process-search-char (char)
6f2df0f4
JL
2397 ;; * and ? are special in regexps when not preceded by \.
2398 ;; } and | are special in regexps when preceded by \.
2399 ;; Nothing special for + because it matches at least once.
2400 (cond
2401 ((memq char '(?* ??)) (isearch-fallback nil))
2402 ((eq char ?\}) (isearch-fallback t t))
2403 ((eq char ?|) (isearch-fallback t nil t)))
2404
8e1cae6d 2405 ;; Append the char to the search string, update the message and re-search.
191f025a
SM
2406 (isearch-process-search-string
2407 (char-to-string char)
8f3c1d76 2408 (if (>= char ?\200)
01a6ef79
RS
2409 (char-to-string char)
2410 (isearch-text-char-description char))))
8e1cae6d
JB
2411
2412(defun isearch-process-search-string (string message)
2413 (setq isearch-string (concat isearch-string string)
2414 isearch-message (concat isearch-message message))
2415 (isearch-search-and-update))
2416
2417\f
8e1cae6d
JB
2418;; Search Ring
2419
08200510
RS
2420(defun isearch-ring-adjust1 (advance)
2421 ;; Helper for isearch-ring-adjust
2422 (let* ((ring (if isearch-regexp regexp-search-ring search-ring))
8e1cae6d
JB
2423 (length (length ring))
2424 (yank-pointer-name (if isearch-regexp
08200510 2425 'regexp-search-ring-yank-pointer
8e1cae6d
JB
2426 'search-ring-yank-pointer))
2427 (yank-pointer (eval yank-pointer-name)))
2428 (if (zerop length)
2429 ()
2430 (set yank-pointer-name
2431 (setq yank-pointer
11dcdbb2 2432 (mod (+ (or yank-pointer (if advance 0 -1))
ffbc30b2
PE
2433 (if advance -1 1))
2434 length)))
a5dcf63f 2435 (setq isearch-string (nth yank-pointer ring)
08200510
RS
2436 isearch-message (mapconcat 'isearch-text-char-description
2437 isearch-string "")))))
2438
2439(defun isearch-ring-adjust (advance)
2440 ;; Helper for isearch-ring-advance and isearch-ring-retreat
08200510 2441 (isearch-ring-adjust1 advance)
08200510
RS
2442 (if search-ring-update
2443 (progn
2444 (isearch-search)
a71a98cf 2445 (isearch-push-state)
08200510 2446 (isearch-update))
a71a98cf
JL
2447 ;; Otherwise, edit the search string instead. Note that there is
2448 ;; no need to push the search state after isearch-edit-string here
2449 ;; since isearch-edit-string already pushes its state
2450 (isearch-edit-string)))
8e1cae6d
JB
2451
2452(defun isearch-ring-advance ()
2453 "Advance to the next search string in the ring."
08200510 2454 ;; This could be more general to handle a prefix arg, but who would use it.
8e1cae6d
JB
2455 (interactive)
2456 (isearch-ring-adjust 'advance))
2457
2458(defun isearch-ring-retreat ()
2459 "Retreat to the previous search string in the ring."
2460 (interactive)
2461 (isearch-ring-adjust nil))
2462
08200510
RS
2463(defun isearch-complete1 ()
2464 ;; Helper for isearch-complete and isearch-complete-edit
2465 ;; Return t if completion OK, nil if no completion exists.
2466 (let* ((ring (if isearch-regexp regexp-search-ring search-ring))
08200510 2467 (completion-ignore-case case-fold-search)
c789e608 2468 (completion (try-completion isearch-string ring)))
08200510
RS
2469 (cond
2470 ((eq completion t)
2471 ;; isearch-string stays the same
2472 t)
2473 ((or completion ; not nil, must be a string
b7852303 2474 (= 0 (length isearch-string))) ; shouldn't have to say this
08200510 2475 (if (equal completion isearch-string) ;; no extension?
36bcac3f
RS
2476 (progn
2477 (if completion-auto-help
2478 (with-output-to-temp-buffer "*Isearch completions*"
b48ca14f 2479 (display-completion-list
c789e608 2480 (all-completions isearch-string ring))))
36bcac3f
RS
2481 t)
2482 (and completion
2483 (setq isearch-string completion))))
08200510
RS
2484 (t
2485 (message "No completion") ; waits a second if in minibuffer
2486 nil))))
2487
2488(defun isearch-complete ()
2489 "Complete the search string from the strings on the search ring.
2490The completed string is then editable in the minibuffer.
2491If there is no completion possible, say so and continue searching."
2492 (interactive)
2493 (if (isearch-complete1)
3ae4c509
RS
2494 (progn (setq isearch-message
2495 (mapconcat 'isearch-text-char-description
2496 isearch-string ""))
2497 (isearch-edit-string))
08200510
RS
2498 ;; else
2499 (sit-for 1)
2500 (isearch-update)))
8e1cae6d 2501
08200510
RS
2502(defun isearch-complete-edit ()
2503 "Same as `isearch-complete' except in the minibuffer."
2504 (interactive)
c789e608 2505 (setq isearch-string (field-string))
08200510 2506 (if (isearch-complete1)
8e1cae6d 2507 (progn
b7b66466 2508 (delete-field)
08200510 2509 (insert isearch-string))))
8e1cae6d
JB
2510
2511\f
8e1cae6d
JB
2512;; Message string
2513
2514(defun isearch-message (&optional c-q-hack ellipsis)
2515 ;; Generate and print the message string.
2516 (let ((cursor-in-echo-area ellipsis)
d8891294 2517 (m isearch-message)
72779976
JL
2518 (fail-pos (isearch-fail-pos t)))
2519 ;; Highlight failed part
2520 (when fail-pos
2521 (setq m (copy-sequence m))
2522 (add-text-properties fail-pos (length m) '(face isearch-fail) m)
d8891294
JL
2523 ;; Highlight failed trailing whitespace
2524 (when (string-match " +$" m)
2525 (add-text-properties (match-beginning 0) (match-end 0)
2526 '(face trailing-whitespace) m)))
2527 (setq m (concat
7c2dc8bd 2528 (isearch-message-prefix ellipsis isearch-nonincremental)
d8891294 2529 m
7c2dc8bd 2530 (isearch-message-suffix c-q-hack)))
d8891294 2531 (if c-q-hack m (let ((message-log-max nil)) (message "%s" m)))))
8e1cae6d 2532
7c2dc8bd 2533(defun isearch-message-prefix (&optional ellipsis nonincremental)
8e1cae6d
JB
2534 ;; If about to search, and previous search regexp was invalid,
2535 ;; check that it still is. If it is valid now,
2536 ;; let the message we display while searching say that it is valid.
ba653a53 2537 (and isearch-error ellipsis
8e1cae6d
JB
2538 (condition-case ()
2539 (progn (re-search-forward isearch-string (point) t)
ba653a53 2540 (setq isearch-error nil))
8e1cae6d
JB
2541 (error nil)))
2542 ;; If currently failing, display no ellipsis.
2543 (or isearch-success (setq ellipsis nil))
2544 (let ((m (concat (if isearch-success "" "failing ")
d85519bb 2545 (if isearch-adjusted "pending " "")
7badea30 2546 (if (and isearch-wrapped
6a18e4e7 2547 (not isearch-wrap-function)
7badea30
RS
2548 (if isearch-forward
2549 (> (point) isearch-opoint)
2550 (< (point) isearch-opoint)))
2551 "over")
8e1cae6d 2552 (if isearch-wrapped "wrapped ")
66fc57e3
JL
2553 (mapconcat (lambda (s)
2554 (and (symbolp s)
2555 (get s 'isearch-message-prefix)))
2556 (if (consp isearch-filter-predicates)
2557 isearch-filter-predicates
2558 (list isearch-filter-predicates))
2559 "")
d5e61c1c
JL
2560 (if isearch-word
2561 (or (and (symbolp isearch-word)
2562 (get isearch-word 'isearch-message-prefix))
2563 "word ")
2564 "")
8e1cae6d 2565 (if isearch-regexp "regexp " "")
3c0aa5e6 2566 (if multi-isearch-next-buffer-current-function "multi " "")
ee9b85a8 2567 (or isearch-message-prefix-add "")
08200510 2568 (if nonincremental "search" "I-search")
f46d5091 2569 (if isearch-forward "" " backward")
5b56d4e4 2570 (if current-input-method
66e0570c
EZ
2571 ;; Input methods for RTL languages use RTL
2572 ;; characters for their title, and that messes
dd782f24 2573 ;; up the display of search text after the prompt.
66e0570c
EZ
2574 (bidi-string-mark-left-to-right
2575 (concat " [" current-input-method-title "]: "))
f46d5091 2576 ": ")
8e1cae6d 2577 )))
aec11aff
KS
2578 (propertize (concat (upcase (substring m 0 1)) (substring m 1))
2579 'face 'minibuffer-prompt)))
8e1cae6d 2580
7c2dc8bd 2581(defun isearch-message-suffix (&optional c-q-hack)
8e1cae6d 2582 (concat (if c-q-hack "^Q" "")
ba653a53
JL
2583 (if isearch-error
2584 (concat " [" isearch-error "]")
ee9b85a8
JL
2585 "")
2586 (or isearch-message-suffix-add "")))
8e1cae6d
JB
2587
2588\f
191f025a
SM
2589;; Searching
2590
8cbd80f7
JL
2591(defvar isearch-search-fun-function 'isearch-search-fun-default
2592 "Non-default value overrides the behavior of `isearch-search-fun-default'.
f158badc
LMI
2593This variable's value should be a function, which will be called
2594with no arguments, and should return a function that takes three
2595arguments: STRING, BOUND, and NOERROR.
2596
2597This returned function will be used by `isearch-search-string' to
2598search for the first occurrence of STRING or its translation.")
191f025a
SM
2599
2600(defun isearch-search-fun ()
2601 "Return the function to use for the search.
2602Can be changed via `isearch-search-fun-function' for special needs."
8cbd80f7
JL
2603 (funcall (or isearch-search-fun-function 'isearch-search-fun-default)))
2604
2605(defun isearch-search-fun-default ()
2606 "Return default functions to use for the search."
2607 (cond
2608 (isearch-word
d5e61c1c
JL
2609 (lambda (string &optional bound noerror count)
2610 ;; Use lax versions to not fail at the end of the word while
2611 ;; the user adds and removes characters in the search string
2612 ;; (or when using nonincremental word isearch)
2613 (let ((lax (not (or isearch-nonincremental
dc2bc295 2614 (null (car isearch-cmds))
d5e61c1c 2615 (eq (length isearch-string)
7c2dc8bd
SM
2616 (length (isearch--state-string
2617 (car isearch-cmds))))))))
d5e61c1c
JL
2618 (funcall
2619 (if isearch-forward #'re-search-forward #'re-search-backward)
2620 (if (functionp isearch-word)
2621 (funcall isearch-word string lax)
2622 (word-search-regexp string lax))
2623 bound noerror count))))
63dd1c6f
JL
2624 ((and isearch-regexp isearch-regexp-lax-whitespace
2625 search-whitespace-regexp)
2626 (if isearch-forward
2627 're-search-forward-lax-whitespace
2628 're-search-backward-lax-whitespace))
8cbd80f7
JL
2629 (isearch-regexp
2630 (if isearch-forward 're-search-forward 're-search-backward))
63dd1c6f
JL
2631 ((and isearch-lax-whitespace search-whitespace-regexp)
2632 (if isearch-forward
2633 'search-forward-lax-whitespace
2634 'search-backward-lax-whitespace))
8cbd80f7 2635 (t
63dd1c6f 2636 (if isearch-forward 'search-forward 'search-backward))))
8e1cae6d 2637
6cf157df 2638(defun isearch-search-string (string bound noerror)
4b65254d
JB
2639 "Search for the first occurrence of STRING or its translation.
2640If found, move point to the end of the occurrence,
2641update the match data, and return point."
3be5da9e
SM
2642 (let* ((func (isearch-search-fun))
2643 (pos1 (save-excursion (funcall func string bound noerror)))
2644 pos2)
0d58bedd
EZ
2645 (when (and
2646 ;; Avoid "obsolete" warnings for translation-table-for-input.
2647 (with-no-warnings
2648 (char-table-p translation-table-for-input))
2649 (multibyte-string-p string)
2650 ;; Minor optimization.
2651 (string-match-p "[^[:ascii:]]" string))
3be5da9e
SM
2652 (let ((translated
2653 (apply 'string
2654 (mapcar (lambda (c)
0d58bedd
EZ
2655 (or
2656 ;; Avoid "obsolete" warnings for
2657 ;; translation-table-for-input.
2658 (with-no-warnings
2659 (aref translation-table-for-input c))
2660 c))
3be5da9e
SM
2661 string)))
2662 match-data)
2663 (when translated
2664 (save-match-data
2665 (save-excursion
2666 (if (setq pos2 (funcall func translated bound noerror))
2667 (setq match-data (match-data t)))))
2668 (when (and pos2
2669 (or (not pos1)
2670 (if isearch-forward (< pos2 pos1) (> pos2 pos1))))
2671 (setq pos1 pos2)
2672 (set-match-data match-data)))))
d240f431
JL
2673 (when pos1
2674 ;; When using multiple buffers isearch, switch to the new buffer here,
2675 ;; because `save-excursion' above doesn't allow doing it inside funcall.
3c0aa5e6
JL
2676 (if (and multi-isearch-next-buffer-current-function
2677 (buffer-live-p multi-isearch-current-buffer))
2678 (switch-to-buffer multi-isearch-current-buffer))
3be5da9e
SM
2679 (goto-char pos1)
2680 pos1)))
6cf157df 2681
8e1cae6d
JB
2682(defun isearch-search ()
2683 ;; Do the search with the current search string.
ac475d3a
JL
2684 (if isearch-message-function
2685 (funcall isearch-message-function nil t)
2686 (isearch-message nil t))
4453091d 2687 (if (and (eq isearch-case-fold-search t) search-upper-case)
b78559b0
RS
2688 (setq isearch-case-fold-search
2689 (isearch-no-upper-case-p isearch-string isearch-regexp)))
8e1cae6d 2690 (condition-case lossage
01dea85f 2691 (let ((inhibit-point-motion-hooks isearch-invisible)
79c7a4fa 2692 (inhibit-quit nil)
86bfaffe 2693 (case-fold-search isearch-case-fold-search)
01dea85f 2694 (search-invisible isearch-invisible)
86bfaffe 2695 (retry t))
ba653a53 2696 (setq isearch-error nil)
86bfaffe
RS
2697 (while retry
2698 (setq isearch-success
6cf157df 2699 (isearch-search-string isearch-string nil t))
2f669fac
JL
2700 ;; Clear RETRY unless the search predicate says
2701 ;; to skip this search hit.
5e189f39 2702 (if (or (not isearch-success)
86bfaffe
RS
2703 (bobp) (eobp)
2704 (= (match-beginning 0) (match-end 0))
66fc57e3
JL
2705 ;; When one of filter predicates returns nil,
2706 ;; retry the search. Otherwise, act according
2707 ;; to search-invisible (open overlays, etc.)
2708 (and (run-hook-with-args-until-failure
2709 'isearch-filter-predicates
2710 (match-beginning 0) (match-end 0))
2711 (or (eq search-invisible t)
2712 (not (isearch-range-invisible
2713 (match-beginning 0) (match-end 0))))))
86bfaffe 2714 (setq retry nil)))
99ae9e9f 2715 (setq isearch-just-started nil)
8e1cae6d
JB
2716 (if isearch-success
2717 (setq isearch-other-end
2718 (if isearch-forward (match-beginning 0) (match-end 0)))))
2719
d32696d4 2720 (quit (isearch-unread ?\C-g)
8e1cae6d
JB
2721 (setq isearch-success nil))
2722
191f025a 2723 (invalid-regexp
ba653a53 2724 (setq isearch-error (car (cdr lossage)))
8e1cae6d
JB
2725 (if (string-match
2726 "\\`Premature \\|\\`Unmatched \\|\\`Invalid "
ba653a53
JL
2727 isearch-error)
2728 (setq isearch-error "incomplete input")))
2729
2730 (search-failed
2731 (setq isearch-success nil)
2732 (setq isearch-error (nth 2 lossage)))
2733
f1c03125 2734 (error
c982ab21 2735 ;; stack overflow in regexp search.
ba653a53 2736 (setq isearch-error (format "%s" lossage))))
8e1cae6d
JB
2737
2738 (if isearch-success
2739 nil
2740 ;; Ding if failed this time after succeeding last time.
7c2dc8bd 2741 (and (isearch--state-success (car isearch-cmds))
8e1cae6d 2742 (ding))
7c2dc8bd
SM
2743 (if (functionp (isearch--state-pop-fun (car isearch-cmds)))
2744 (funcall (isearch--state-pop-fun (car isearch-cmds))
2745 (car isearch-cmds)))
2746 (goto-char (isearch--state-point (car isearch-cmds)))))
8e1cae6d 2747
0352b205 2748
191f025a 2749;; Called when opening an overlay, and we are still in isearch.
0352b205 2750(defun isearch-open-overlay-temporary (ov)
b48ca14f 2751 (if (not (null (overlay-get ov 'isearch-open-invisible-temporary)))
0352b205
RS
2752 ;; Some modes would want to open the overlays temporary during
2753 ;; isearch in their own way, they should set the
2754 ;; `isearch-open-invisible-temporary' to a function doing this.
2755 (funcall (overlay-get ov 'isearch-open-invisible-temporary) ov nil)
2756 ;; Store the values for the `invisible' and `intangible'
2757 ;; properties, and then set them to nil. This way the text hidden
2758 ;; by this overlay becomes visible.
2759
380866a2 2760 ;; Do we really need to set the `intangible' property to t? Can we
0352b205
RS
2761 ;; have the point inside an overlay with an `intangible' property?
2762 ;; In 19.34 this does not exist so I cannot test it.
2763 (overlay-put ov 'isearch-invisible (overlay-get ov 'invisible))
2764 (overlay-put ov 'isearch-intangible (overlay-get ov 'intangible))
2765 (overlay-put ov 'invisible nil)
2766 (overlay-put ov 'intangible nil)))
2767
2768
191f025a
SM
2769;; This is called at the end of isearch. It will open the overlays
2770;; that contain the latest match. Obviously in case of a C-g the
2771;; point returns to the original location which surely is not contain
2772;; in any of these overlays, se we are safe in this case too.
0352b205 2773(defun isearch-open-necessary-overlays (ov)
191f025a 2774 (let ((inside-overlay (and (> (point) (overlay-start ov))
0352b205
RS
2775 (< (point) (overlay-end ov))))
2776 ;; If this exists it means that the overlay was opened using
2777 ;; this function, not by us tweaking the overlay properties.
2778 (fct-temp (overlay-get ov 'isearch-open-invisible-temporary)))
2779 (when (or inside-overlay (not fct-temp))
2780 ;; restore the values for the `invisible' and `intangible'
2781 ;; properties
2782 (overlay-put ov 'invisible (overlay-get ov 'isearch-invisible))
2783 (overlay-put ov 'intangible (overlay-get ov 'isearch-intangible))
2784 (overlay-put ov 'isearch-invisible nil)
2785 (overlay-put ov 'isearch-intangible nil))
2786 (if inside-overlay
2787 (funcall (overlay-get ov 'isearch-open-invisible) ov)
2788 (if fct-temp
2789 (funcall fct-temp ov t)))))
2790
191f025a
SM
2791;; This is called when exiting isearch. It closes the temporary
2792;; opened overlays, except the ones that contain the latest match.
0352b205
RS
2793(defun isearch-clean-overlays ()
2794 (when isearch-opened-overlays
02b2f510 2795 (mapc 'isearch-open-necessary-overlays isearch-opened-overlays)
0352b205
RS
2796 (setq isearch-opened-overlays nil)))
2797
fc0eccfc
GM
2798
2799(defun isearch-intersects-p (start0 end0 start1 end1)
2800 "Return t if regions START0..END0 and START1..END1 intersect."
eaa493df
GM
2801 (or (and (>= start0 start1) (< start0 end1))
2802 (and (> end0 start1) (<= end0 end1))
2803 (and (>= start1 start0) (< start1 end0))
2804 (and (> end1 start0) (<= end1 end0))))
fc0eccfc
GM
2805
2806
191f025a
SM
2807;; Verify if the current match is outside of each element of
2808;; `isearch-opened-overlays', if so close that overlay.
fc0eccfc
GM
2809
2810(defun isearch-close-unnecessary-overlays (begin end)
2811 (let ((overlays isearch-opened-overlays))
0352b205 2812 (setq isearch-opened-overlays nil)
fc0eccfc
GM
2813 (dolist (ov overlays)
2814 (if (isearch-intersects-p begin end (overlay-start ov) (overlay-end ov))
2815 (push ov isearch-opened-overlays)
2816 (let ((fct-temp (overlay-get ov 'isearch-open-invisible-temporary)))
2817 (if fct-temp
2818 ;; If this exists it means that the overlay was opened
2819 ;; using this function, not by us tweaking the overlay
2820 ;; properties.
2821 (funcall fct-temp ov t)
2822 (overlay-put ov 'invisible (overlay-get ov 'isearch-invisible))
2823 (overlay-put ov 'intangible (overlay-get ov 'isearch-intangible))
2824 (overlay-put ov 'isearch-invisible nil)
2825 (overlay-put ov 'isearch-intangible nil)))))))
2826
0352b205 2827
86bfaffe 2828(defun isearch-range-invisible (beg end)
79c7a4fa 2829 "Return t if all the text from BEG to END is invisible."
cd59ea72
SM
2830 (when (/= beg end)
2831 ;; Check that invisibility runs up to END.
2832 (save-excursion
2833 (goto-char beg)
2834 (let (;; can-be-opened keeps track if we can open some overlays.
2835 (can-be-opened (eq search-invisible 'open))
2836 ;; the list of overlays that could be opened
2837 (crt-overlays nil))
2838 (when (and can-be-opened isearch-hide-immediately)
2839 (isearch-close-unnecessary-overlays beg end))
2840 ;; If the following character is currently invisible,
2841 ;; skip all characters with that same `invisible' property value.
2842 ;; Do that over and over.
4d90d6d0 2843 (while (and (< (point) end) (invisible-p (point)))
66e2e71d 2844 (if (invisible-p (get-text-property (point) 'invisible))
cd59ea72
SM
2845 (progn
2846 (goto-char (next-single-property-change (point) 'invisible
2847 nil end))
2848 ;; if text is hidden by an `invisible' text property
2849 ;; we cannot open it at all.
2850 (setq can-be-opened nil))
2851 (when can-be-opened
2852 (let ((overlays (overlays-at (point)))
2853 ov-list
2854 o
2855 invis-prop)
2856 (while overlays
2857 (setq o (car overlays)
2858 invis-prop (overlay-get o 'invisible))
4d90d6d0 2859 (if (invisible-p invis-prop)
cd59ea72
SM
2860 (if (overlay-get o 'isearch-open-invisible)
2861 (setq ov-list (cons o ov-list))
2862 ;; We found one overlay that cannot be
2863 ;; opened, that means the whole chunk
2864 ;; cannot be opened.
2865 (setq can-be-opened nil)))
2866 (setq overlays (cdr overlays)))
2867 (if can-be-opened
2868 ;; It makes sense to append to the open
2869 ;; overlays list only if we know that this is
2870 ;; t.
2871 (setq crt-overlays (append ov-list crt-overlays)))))
2872 (goto-char (next-overlay-change (point)))))
2873 ;; See if invisibility reaches up thru END.
2874 (if (>= (point) end)
2875 (if (and can-be-opened (consp crt-overlays))
2876 (progn
2877 (setq isearch-opened-overlays
2878 (append isearch-opened-overlays crt-overlays))
2879 (mapc 'isearch-open-overlay-temporary crt-overlays)
2880 nil)
2881 (setq isearch-hidden t)))))))
08200510 2882
f2fd3261
JL
2883(defun isearch-filter-visible (beg end)
2884 "Test whether the current search hit is visible at least partially.
2885Return non-nil if the text from BEG to END is visible to Isearch as
2886determined by `isearch-range-invisible' unless invisible text can be
2887searched too when `search-invisible' is t."
5e189f39
JL
2888 (or (eq search-invisible t)
2889 (not (isearch-range-invisible beg end))))
01dea85f 2890(make-obsolete 'isearch-filter-visible 'isearch-invisible "24.4")
5e189f39 2891
08200510 2892\f
191f025a 2893;; General utilities
08200510 2894
b78559b0
RS
2895(defun isearch-no-upper-case-p (string regexp-flag)
2896 "Return t if there are no upper case chars in STRING.
b7852303 2897If REGEXP-FLAG is non-nil, disregard letters preceded by `\\' (but not `\\\\')
b78559b0 2898since they have special meaning in a regexp."
b48ca14f 2899 (let (quote-flag (i 0) (len (length string)) found)
59057198
RS
2900 (while (and (not found) (< i len))
2901 (let ((char (aref string i)))
2902 (if (and regexp-flag (eq char ?\\))
2903 (setq quote-flag (not quote-flag))
2904 (if (and (not quote-flag) (not (eq char (downcase char))))
f4be3f89
RS
2905 (setq found t))
2906 (setq quote-flag nil)))
59057198 2907 (setq i (1+ i)))
c5f847b6
SM
2908 (not (or found
2909 ;; Even if there's no uppercase char, we want to detect the use
2910 ;; of [:upper:] or [:lower:] char-class, which indicates
2911 ;; clearly that the user cares about case distinction.
2912 (and regexp-flag (string-match "\\[:\\(upp\\|low\\)er:]" string)
2913 (condition-case err
2914 (progn
2915 (string-match (substring string 0 (match-beginning 0))
2916 "")
2917 nil)
2918 (invalid-regexp
2919 (equal "Unmatched [ or [^" (cadr err)))))))))
08200510 2920
b78559b0 2921;; Portability functions to support various Emacs versions.
8e1cae6d 2922
08200510 2923(defun isearch-text-char-description (c)
14373677 2924 (cond
cf5e4199
JL
2925 ((< c ?\s) (propertize (format "^%c" (+ c 64)) 'face 'escape-glyph))
2926 ((= c ?\^?) (propertize "^?" 'face 'escape-glyph))
14373677 2927 (t (char-to-string c))))
08200510 2928
bd1bd125 2929;; General function to unread characters or events.
99ae9e9f 2930;; Also insert them in a keyboard macro being defined.
8f90f594 2931(defun isearch-unread (&rest char-or-events)
02b2f510 2932 (mapc 'store-kbd-macro-event char-or-events)
bd1bd125
RS
2933 (setq unread-command-events
2934 (append char-or-events unread-command-events)))
08200510 2935
44336afb 2936\f
bca92193
JL
2937;; Highlighting
2938
2939(defvar isearch-overlay nil)
2940
2941(defun isearch-highlight (beg end)
704d3ae7
JL
2942 (if search-highlight
2943 (if isearch-overlay
2944 ;; Overlay already exists, just move it.
2945 (move-overlay isearch-overlay beg end (current-buffer))
2946 ;; Overlay doesn't exist, create it.
2947 (setq isearch-overlay (make-overlay beg end))
2948 ;; 1001 is higher than lazy's 1000 and ediff's 100+
2949 (overlay-put isearch-overlay 'priority 1001)
4be520fb 2950 (overlay-put isearch-overlay 'face isearch-face))))
bca92193
JL
2951
2952(defun isearch-dehighlight ()
2953 (when isearch-overlay
2954 (delete-overlay isearch-overlay)))
2955\f
191f025a
SM
2956;; isearch-lazy-highlight feature
2957;; by Bob Glickstein <http://www.zanshin.com/~bobg/>
2958
2959;; When active, *every* match for the current search string is
2960;; highlighted: the current one using the normal isearch match color
e3cde0c7 2961;; and all the others using `isearch-lazy-highlight'. The extra
191f025a
SM
2962;; highlighting makes it easier to anticipate where the cursor will
2963;; land each time you press C-s or C-r to repeat a pending search.
2964;; Highlighting of these additional matches happens in a deferred
2965;; fashion using "idle timers," so the cycles needed do not rob
2966;; isearch of its usual snappy response.
2967
2968;; IMPLEMENTATION NOTE: This depends on some isearch internals.
2969;; Specifically:
2970;; - `isearch-update' is expected to be called (at least) every time
2971;; the search string or window-start changes;
2972;; - `isearch-string' is expected to contain the current search
2973;; string as entered by the user;
2974;; - the type of the current search is expected to be given by
2975;; `isearch-word' and `isearch-regexp';
2976;; - the direction of the current search is expected to be given by
2977;; `isearch-forward';
ba653a53 2978;; - the variable `isearch-error' is expected to be true
4837b516 2979;; only if `isearch-string' is an invalid regexp.
44336afb 2980
44336afb 2981(defvar isearch-lazy-highlight-overlays nil)
c982ab21 2982(defvar isearch-lazy-highlight-wrapped nil)
f9114cec
RS
2983(defvar isearch-lazy-highlight-start-limit nil)
2984(defvar isearch-lazy-highlight-end-limit nil)
44336afb
GM
2985(defvar isearch-lazy-highlight-start nil)
2986(defvar isearch-lazy-highlight-end nil)
2987(defvar isearch-lazy-highlight-timer nil)
2988(defvar isearch-lazy-highlight-last-string nil)
c982ab21
GM
2989(defvar isearch-lazy-highlight-window nil)
2990(defvar isearch-lazy-highlight-window-start nil)
6b61353c 2991(defvar isearch-lazy-highlight-window-end nil)
46daf6c7
GM
2992(defvar isearch-lazy-highlight-case-fold-search nil)
2993(defvar isearch-lazy-highlight-regexp nil)
63dd1c6f
JL
2994(defvar isearch-lazy-highlight-lax-whitespace nil)
2995(defvar isearch-lazy-highlight-regexp-lax-whitespace nil)
7ce7717b 2996(defvar isearch-lazy-highlight-word nil)
e54a1075 2997(defvar isearch-lazy-highlight-forward nil)
957e5dd1 2998(defvar isearch-lazy-highlight-error nil)
44336afb 2999
46fe9018 3000(defun lazy-highlight-cleanup (&optional force)
c982ab21 3001 "Stop lazy highlighting and remove extra highlighting from current buffer.
c1bc6bb6 3002FORCE non-nil means do it whether or not `lazy-highlight-cleanup'
c982ab21 3003is nil. This function is called when exiting an incremental search if
c1bc6bb6 3004`lazy-highlight-cleanup' is non-nil."
44336afb 3005 (interactive '(t))
c1bc6bb6 3006 (if (or force lazy-highlight-cleanup)
c982ab21
GM
3007 (while isearch-lazy-highlight-overlays
3008 (delete-overlay (car isearch-lazy-highlight-overlays))
3009 (setq isearch-lazy-highlight-overlays
3010 (cdr isearch-lazy-highlight-overlays))))
3011 (when isearch-lazy-highlight-timer
3012 (cancel-timer isearch-lazy-highlight-timer)
3013 (setq isearch-lazy-highlight-timer nil)))
44336afb 3014
6480c508
JB
3015(define-obsolete-function-alias 'isearch-lazy-highlight-cleanup
3016 'lazy-highlight-cleanup
3017 "22.1")
46fe9018 3018
d9dd1f33 3019(defun isearch-lazy-highlight-new-loop (&optional beg end)
c1bc6bb6 3020 "Cleanup any previous `lazy-highlight' loop and begin a new one.
f9114cec
RS
3021BEG and END specify the bounds within which highlighting should occur.
3022This is called when `isearch-update' is invoked (which can cause the
3023search string to change or the window to scroll). It is also used
3024by other Emacs features."
da79720c 3025 (when (and (null executing-kbd-macro)
c982ab21
GM
3026 (sit-for 0) ;make sure (window-start) is credible
3027 (or (not (equal isearch-string
3028 isearch-lazy-highlight-last-string))
3029 (not (eq (selected-window)
3030 isearch-lazy-highlight-window))
46daf6c7
GM
3031 (not (eq isearch-lazy-highlight-case-fold-search
3032 isearch-case-fold-search))
3033 (not (eq isearch-lazy-highlight-regexp
3034 isearch-regexp))
7ce7717b
JL
3035 (not (eq isearch-lazy-highlight-word
3036 isearch-word))
63dd1c6f
JL
3037 (not (eq isearch-lazy-highlight-lax-whitespace
3038 isearch-lax-whitespace))
3039 (not (eq isearch-lazy-highlight-regexp-lax-whitespace
3040 isearch-regexp-lax-whitespace))
c982ab21 3041 (not (= (window-start)
6b61353c
KH
3042 isearch-lazy-highlight-window-start))
3043 (not (= (window-end) ; Window may have been split/joined.
e54a1075
JB
3044 isearch-lazy-highlight-window-end))
3045 (not (eq isearch-forward
957e5dd1
JL
3046 isearch-lazy-highlight-forward))
3047 ;; In case we are recovering from an error.
3048 (not (equal isearch-error
3049 isearch-lazy-highlight-error))))
c982ab21 3050 ;; something important did indeed change
46fe9018 3051 (lazy-highlight-cleanup t) ;kill old loop & remove overlays
957e5dd1 3052 (setq isearch-lazy-highlight-error isearch-error)
30c62133
JL
3053 ;; It used to check for `(not isearch-error)' here, but actually
3054 ;; lazy-highlighting might find matches to highlight even when
3055 ;; `isearch-error' is non-nil. (Bug#9918)
3056 (setq isearch-lazy-highlight-start-limit beg
3057 isearch-lazy-highlight-end-limit end)
3058 (setq isearch-lazy-highlight-window (selected-window)
3059 isearch-lazy-highlight-window-start (window-start)
3060 isearch-lazy-highlight-window-end (window-end)
54d9de11
JL
3061 ;; Start lazy-highlighting at the beginning of the found
3062 ;; match (`isearch-other-end'). If no match, use point.
3063 ;; One of the next two variables (depending on search direction)
3064 ;; is used to define the starting position of lazy-highlighting
3065 ;; and also to remember the current position of point between
3066 ;; calls of `isearch-lazy-highlight-update', and another variable
3067 ;; is used to define where the wrapped search must stop.
3068 isearch-lazy-highlight-start (or isearch-other-end (point))
3069 isearch-lazy-highlight-end (or isearch-other-end (point))
30c62133
JL
3070 isearch-lazy-highlight-wrapped nil
3071 isearch-lazy-highlight-last-string isearch-string
3072 isearch-lazy-highlight-case-fold-search isearch-case-fold-search
3073 isearch-lazy-highlight-regexp isearch-regexp
63dd1c6f
JL
3074 isearch-lazy-highlight-lax-whitespace isearch-lax-whitespace
3075 isearch-lazy-highlight-regexp-lax-whitespace isearch-regexp-lax-whitespace
30c62133
JL
3076 isearch-lazy-highlight-word isearch-word
3077 isearch-lazy-highlight-forward isearch-forward)
5f3a57c9
RS
3078 (unless (equal isearch-string "")
3079 (setq isearch-lazy-highlight-timer
c1bc6bb6 3080 (run-with-idle-timer lazy-highlight-initial-delay nil
30c62133 3081 'isearch-lazy-highlight-update)))))
c982ab21
GM
3082
3083(defun isearch-lazy-highlight-search ()
3084 "Search ahead for the next or previous match, for lazy highlighting.
4b65254d 3085Attempt to do the search exactly the way the pending Isearch would."
83659220
JL
3086 (condition-case nil
3087 (let ((case-fold-search isearch-lazy-highlight-case-fold-search)
3088 (isearch-regexp isearch-lazy-highlight-regexp)
7ce7717b 3089 (isearch-word isearch-lazy-highlight-word)
826b3235
JL
3090 (isearch-lax-whitespace
3091 isearch-lazy-highlight-lax-whitespace)
3092 (isearch-regexp-lax-whitespace
3093 isearch-lazy-highlight-regexp-lax-whitespace)
3094 (isearch-forward isearch-lazy-highlight-forward)
83659220
JL
3095 (search-invisible nil) ; don't match invisible text
3096 (retry t)
3097 (success nil)
e54a1075 3098 (bound (if isearch-lazy-highlight-forward
83659220
JL
3099 (min (or isearch-lazy-highlight-end-limit (point-max))
3100 (if isearch-lazy-highlight-wrapped
3101 isearch-lazy-highlight-start
3102 (window-end)))
3103 (max (or isearch-lazy-highlight-start-limit (point-min))
ba653a53 3104 (if isearch-lazy-highlight-wrapped
83659220
JL
3105 isearch-lazy-highlight-end
3106 (window-start))))))
2f669fac 3107 ;; Use a loop like in `isearch-search'.
83659220
JL
3108 (while retry
3109 (setq success (isearch-search-string
3110 isearch-lazy-highlight-last-string bound t))
2f669fac
JL
3111 ;; Clear RETRY unless the search predicate says
3112 ;; to skip this search hit.
83659220 3113 (if (or (not success)
27dd3c11
JL
3114 (= (point) bound) ; like (bobp) (eobp) in `isearch-search'.
3115 (= (match-beginning 0) (match-end 0))
66fc57e3
JL
3116 (and (run-hook-with-args-until-failure
3117 'isearch-filter-predicates
3118 (match-beginning 0) (match-end 0))
3119 (not (isearch-range-invisible
3120 (match-beginning 0) (match-end 0)))))
83659220
JL
3121 (setq retry nil)))
3122 success)
3123 (error nil)))
44336afb
GM
3124
3125(defun isearch-lazy-highlight-update ()
c982ab21 3126 "Update highlighting of other matches for current search."
c1bc6bb6 3127 (let ((max lazy-highlight-max-at-a-time)
c982ab21
GM
3128 (looping t)
3129 nomore)
6ee8e621
JL
3130 (with-local-quit
3131 (save-selected-window
3132 (if (and (window-live-p isearch-lazy-highlight-window)
3133 (not (eq (selected-window) isearch-lazy-highlight-window)))
3134 (select-window isearch-lazy-highlight-window))
3135 (save-excursion
3136 (save-match-data
e54a1075 3137 (goto-char (if isearch-lazy-highlight-forward
6ee8e621
JL
3138 isearch-lazy-highlight-end
3139 isearch-lazy-highlight-start))
3140 (while looping
3141 (let ((found (isearch-lazy-highlight-search)))
3142 (when max
3143 (setq max (1- max))
3144 (if (<= max 0)
3145 (setq looping nil)))
3146 (if found
3147 (let ((mb (match-beginning 0))
3148 (me (match-end 0)))
3149 (if (= mb me) ;zero-length match
e54a1075 3150 (if isearch-lazy-highlight-forward
6ee8e621
JL
3151 (if (= mb (if isearch-lazy-highlight-wrapped
3152 isearch-lazy-highlight-start
3153 (window-end)))
3154 (setq found nil)
3155 (forward-char 1))
3156 (if (= mb (if isearch-lazy-highlight-wrapped
3157 isearch-lazy-highlight-end
3158 (window-start)))
3159 (setq found nil)
3160 (forward-char -1)))
3161
3162 ;; non-zero-length match
3163 (let ((ov (make-overlay mb me)))
3164 (push ov isearch-lazy-highlight-overlays)
704d3ae7
JL
3165 ;; 1000 is higher than ediff's 100+,
3166 ;; but lower than isearch main overlay's 1001
3167 (overlay-put ov 'priority 1000)
46fe9018 3168 (overlay-put ov 'face lazy-highlight-face)
6ee8e621 3169 (overlay-put ov 'window (selected-window))))
54d9de11
JL
3170 ;; Remember the current position of point for
3171 ;; the next call of `isearch-lazy-highlight-update'
3172 ;; when `lazy-highlight-max-at-a-time' is too small.
e54a1075 3173 (if isearch-lazy-highlight-forward
6ee8e621
JL
3174 (setq isearch-lazy-highlight-end (point))
3175 (setq isearch-lazy-highlight-start (point)))))
3176
3177 ;; not found or zero-length match at the search bound
3178 (if (not found)
3179 (if isearch-lazy-highlight-wrapped
3180 (setq looping nil
3181 nomore t)
3182 (setq isearch-lazy-highlight-wrapped t)
e54a1075 3183 (if isearch-lazy-highlight-forward
6ee8e621
JL
3184 (progn
3185 (setq isearch-lazy-highlight-end (window-start))
f9114cec
RS
3186 (goto-char (max (or isearch-lazy-highlight-start-limit (point-min))
3187 (window-start))))
6ee8e621 3188 (setq isearch-lazy-highlight-start (window-end))
f9114cec
RS
3189 (goto-char (min (or isearch-lazy-highlight-end-limit (point-max))
3190 (window-end))))))))
6ee8e621
JL
3191 (unless nomore
3192 (setq isearch-lazy-highlight-timer
2c987fc2 3193 (run-at-time lazy-highlight-interval nil
6ee8e621 3194 'isearch-lazy-highlight-update)))))))))
44336afb 3195
72a69d7f 3196(defun isearch-resume (string regexp word forward message case-fold)
c40a4de1 3197 "Resume an incremental search.
72a69d7f 3198STRING is the string or regexp searched for.
c40a4de1
GM
3199REGEXP non-nil means the resumed search was a regexp search.
3200WORD non-nil means resume a word search.
3201FORWARD non-nil means resume a forward search.
3202MESSAGE is the echo-area message recorded for the search resumed.
3203CASE-FOLD non-nil means the search was case-insensitive."
3204 (isearch-mode forward regexp nil nil word)
72a69d7f 3205 (setq isearch-string string
c40a4de1
GM
3206 isearch-message message
3207 isearch-case-fold-search case-fold)
72a69d7f
JL
3208 (isearch-search)
3209 (isearch-update))
191f025a 3210
3b1e4dd1 3211;;; isearch.el ends here