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