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