entered into RCS
[emacs.git] / lisp / isearch.el
blob0a0fc946be1e9d6d9053d08f55588eab97e2d3dd
1 ;; isearch-mode.el --- incremental search minor mode.
3 ;; Copyright (C) 1992 Free Software Foundation, Inc.
5 ;; Author: Daniel LaLiberte <liberte@cs.uiuc.edu>
6 ;; Version: 1.2
8 ;; This file is part of GNU Emacs.
10 ;; GNU Emacs is distributed in the hope that it will be useful,
11 ;; but WITHOUT ANY WARRANTY. No author or distributor
12 ;; accepts responsibility to anyone for the consequences of using it
13 ;; or for whether it serves any particular purpose or works at all,
14 ;; unless he says so in writing. Refer to the GNU Emacs General Public
15 ;; License for full details.
17 ;; Everyone is granted permission to copy, modify and redistribute
18 ;; GNU Emacs, but only under the conditions described in the
19 ;; GNU Emacs General Public License. A copy of this license is
20 ;; supposed to have been given to you along with GNU Emacs so you
21 ;; can know your rights and responsibilities. It should be in a
22 ;; file named COPYING. Among other things, the copyright notice
23 ;; and this notice must be preserved on all copies.
25 ;;; Commentary:
27 ;;;====================================================================
28 ;; Instructions
30 ;; Searching with isearch-mode.el should work just like isearch.el,
31 ;; except it is done in a temporary minor mode that terminates when
32 ;; you finish searching.
34 ;; To use isearch-mode instead of the standard isearch.el, add the
35 ;; following to your .emacs file. The standard key bindings to
36 ;; isearch-forward, etc, will then use isearch-mode instead of
37 ;; isearch.
39 ;; (fset 'isearch 'isearch-mode)
40 ;; (autoload 'isearch-mode "isearch-mode")
42 ;; The key bindings active within isearch-mode are defined below in
43 ;; `isearch-mode-map' and `isearch-mode-meta-map' which are given
44 ;; bindings close to the default characters of isearch.el for
45 ;; version 19. With `isearch-mode', however, you can bind
46 ;; multi-character keys and it should be easier to add new commands.
48 ;; Note to epoch and emacs version 19 users: isearch-mode should
49 ;; work even if you switch windows with the mouse. However, if
50 ;; you isearch in a buffer that is also displayed in another window,
51 ;; when you switch to that other window you will still be in
52 ;; isearch mode but not necessarily in the right state for it to work.
53 ;; So ... don't do it unless you are in an experimental mood.
54 ;; You can also experiment with the window-local-variable routines
55 ;; contained in this package but not yet used.
56 ;; Also, I am not sure what happens when you return to an isearching
57 ;; buffer; ideally, the echo area should redisplay the searching status.
58 ;; A select-window-hook might be useful.
60 ;;; Change Log:
62 ;;;====================================================================
64 ;;; Revision 1.2 92/05/27 11:33:57 liberte
65 ;;; Several new commands and features have been added. Emacs version
66 ;;; 19 has a search ring, which is supported here. Other fixes found
67 ;;; in the version 19 isearch are included here. Also see variables
68 ;;; search-caps-disable-folding, search-nonincremental-instead,
69 ;;; search-whitespace-regexp, and commands isearch-toggle-regexp,
70 ;;; isearch-edit-string,
71 ;;;
72 ;;; Semi-modal searching is supported, using a recursive edit. If
73 ;;; isearching is started non-interactively by calling one of the
74 ;;; isearch commands (e.g. isearch-forward), it does not return
75 ;;; until the search is completed. You should still be able switch
76 ;;; buffers, so be careful not to get things confused.
77 ;;;
79 ;;; Changes for 1.1
80 ;;; 3/18/92 Fixed invalid-regexp.
81 ;;; 3/18/92 Fixed yanking in regexps.
83 ;;; Code:
85 ;;;=========================================================================
86 ;;; The following, defined in loaddefs.el, are still used with isearch-mode.
88 ;(defvar search-last-string ""
89 ; "Last string search for by a search command.
90 ;This does not include direct calls to the primitive search functions,
91 ;and does not include searches that are aborted.")
93 ;(defvar search-last-regexp ""
94 ; "Last string searched for by a regexp search command.
95 ;This does not include direct calls to the primitive search functions,
96 ;and does not include searches that are aborted.")
98 ;(defconst search-exit-option t
99 ; "Non-nil means random control characters terminate incremental search.")
101 ;(defvar search-slow-window-lines 1
102 ; "*Number of lines in slow search display windows.")
104 ;(defconst search-slow-speed 1200
105 ; "*Highest terminal speed at which to use \"slow\" style incremental search.
106 ;This is the style where a one-line window is created to show the line
107 ;that the search has reached.")
109 ;;;========================================================================
110 ;;; Some additional options and constants.
112 (defvar search-caps-disable-folding t
113 "*If non-nil, upper case chars disable case fold searching.
114 This does not yet apply to yanked strings, however.")
116 (defvar search-nonincremental-instead t
117 "*If non-nil, do a nonincremental search instead if exiting immediately.
118 The default value of t reflects the default behavior of old
119 isearch.")
121 (defconst search-whitespace-regexp "\\s-+"
122 "*If non-nil, regular expression to match a sequence of whitespace chars.
123 You might want to use something like \"[ \\t\\r\\n]+\" instead.")
125 ;;;==================================================================
126 ;;; Search ring.
127 ;;; "regex" == "regexp". One should become the standard term.
129 (defvar search-ring nil
130 "List of search string sequences.")
131 (defvar regex-search-ring nil ;; Is `regex' the new spelling?
132 "List of regular expression search string sequences.")
134 (defconst search-ring-max 16
135 "*Maximum length of search ring before oldest elements are thrown away.")
136 (defconst regex-search-ring-max 16
137 "*Maximum length of regex search ring before oldest elements are thrown away.")
139 (defvar search-ring-yank-pointer nil
140 "The tail of the search ring whose car is the last thing searched for.")
141 (defvar regex-search-ring-yank-pointer nil
142 "The tail of the regular expression search ring whose car is the last
143 thing searched for.")
145 ;;;====================================================
146 ;;; Define isearch-mode keymap.
148 (defvar isearch-mode-map nil
149 "Keymap for isearch-mode.")
151 (defvar isearch-mode-meta-map nil
152 "Keymap for isearch-mode for keys with meta char prefix.")
155 ;; To handle meta char prefix keys, define another full keymap.
156 ;; The same must be done for any other prefix keys.
157 ;; It would be simpler to disable to global keymap, and/or
158 ;; have a default local key binding for any key not otherwise bound.
159 (if isearch-mode-meta-map
161 (setq isearch-mode-meta-map
162 (list 'keymap (make-vector 128 'isearch-other-meta-char)))
163 (define-key isearch-mode-meta-map "n" 'isearch-ring-advance)
164 (define-key isearch-mode-meta-map "p" 'isearch-ring-retreat)
165 (define-key isearch-mode-meta-map " " 'isearch-whitespace-chars)
166 ;for regexps
168 ;; (define-key isearch-mode-meta-map "?" nil) ; my help key is M-?
171 (if isearch-mode-map
173 (let ((i 0)
175 ;; Printing chars extend the selection by default.
176 (array (make-vector 128 'isearch-printing-char)))
178 ;; Non-printing chars by default suspend isearch mode transparently
179 (while (< i ?\ )
180 (aset array i 'isearch-other-control-char)
181 (setq i (1+ i)))
183 (setq i ?A)
184 (while (<= i ?Z)
185 (aset array i 'isearch-upper-case-char)
186 (setq i (1+ i)))
188 (setq isearch-mode-map (list 'keymap array))
190 ;; You can reenable global keys by unbinding them locally.
192 ;; For the help char this doesnt work quite as expected because
193 ;; isearch-mode is not a major mode, and the echo area is not
194 ;; restored after the help command.
195 ;; Also, we should not assume that the help-command is on C-h.
196 ;; If it is not, and the prefix is not the meta-char, we would
197 ;; have to create another map for its prefix.
198 ; (define-key isearch-mode-map "\C-h" nil)
200 ;; Several non-printing chars change the searching behavior.
201 (define-key isearch-mode-map "\C-s" 'isearch-repeat-forward)
202 (define-key isearch-mode-map "\C-r" 'isearch-repeat-backward)
203 (define-key isearch-mode-map "\177" 'isearch-delete-char)
204 (define-key isearch-mode-map "\C-g" 'isearch-quit)
207 (define-key isearch-mode-map "\C-q" 'isearch-quote-char)
209 ;; (define-key isearch-mode-map "\r" 'isearch-return-char)
210 ;; For version 19, CR (C-m) terminates search and LFD (C-j) matches eol.
211 (define-key isearch-mode-map "\r" 'isearch-exit)
212 (define-key isearch-mode-map "\C-j" 'isearch-printing-char)
215 (define-key isearch-mode-map "\C-w" 'isearch-yank-word)
216 (define-key isearch-mode-map "\C-y" 'isearch-yank-line)
218 (define-key isearch-mode-map "\C-t" 'isearch-toggle-regexp)
219 (define-key isearch-mode-map "\C-^" 'isearch-edit-string)
221 ;; define keys for regexp chars * ? |
222 (define-key isearch-mode-map "*" 'isearch-*-char)
223 (define-key isearch-mode-map "?" 'isearch-*-char)
224 (define-key isearch-mode-map "|" 'isearch-|-char)
226 ;; Assumes meta-prefix-char is \e.
227 ;; isearch-mode-meta-map must be a keymap before this.
228 (define-key isearch-mode-map "\e" isearch-mode-meta-map)
231 ;;;========================================================
232 ;; Internal variables declared globally for byte-compiler.
233 ;; These are all made buffer-local during searching.
235 (defvar isearch-cmds nil
236 "Stack of search status sets.")
237 (defvar isearch-string ""
238 "The current search string.")
239 (defvar isearch-message ""
240 "The text-char-description version of isearch-string")
241 (defvar isearch-success t)
242 (defvar isearch-forward nil)
243 (defvar isearch-other-end nil
244 "Start of last match if forward, end if backward.")
245 (defvar isearch-invalid-regexp nil)
246 (defvar isearch-wrapped nil)
247 (defvar isearch-barrier 0)
249 (defvar isearch-regexp nil)
250 (defvar isearch-case-fold-search nil
251 "Value of case-fold-search while actually searching.")
253 (defvar isearch-adjusted nil)
254 (defvar isearch-slow-terminal-mode nil)
255 (defvar isearch-small-window nil
256 "If t, using a small window.")
257 (defvar isearch-found-point nil
258 "to restore point from a small window.")
260 (defvar isearch-found-start nil
261 "This is the window-start value found by the search.")
262 (defvar isearch-opoint 0)
263 (defvar isearch-window-configuration nil
264 "The window configuration active at the beginning of the search.")
265 (defvar isearch-old-local-map [])
267 (defvar isearch-yank-flag nil
268 "Flag to indicate a yank occurred, so don't move the cursor.")
270 (defvar isearch-op-fun nil
271 "A function to be called after each input character is processed.
272 (It is not called after characters that exit the search.)
273 It is only set from an optional argument to `isearch-mode'.")
275 ;; This is a global variable to avoid byte-compile warnings.
276 (defvar isearch-last-command-char -1
277 "Last command char.")
279 (defvar isearch-mode-hook nil
280 "List of functions to call after starting up an incremental search.
281 See `isearch-modal' for an example.
282 Set with `(setq isearch-mode-hook (cons 'myhook isearch-mode-hook))
283 where myhook can be a function name or lambda expression.")
285 (defvar isearch-mode-end-hook nil
286 "List of functions to call after terminating an incremental search.
287 See `isearch-mode-hook' for more details.")
289 ;;;==============================================================
290 ;; Minor-mode-alist changes - kind of redundant with the
291 ;; echo area, but if isearching in multiple windows, it can be useful.
293 (or (assq 'isearch-mode minor-mode-alist)
294 (nconc minor-mode-alist
295 (list '(isearch-mode isearch-mode))))
297 (defvar isearch-mode nil)
298 (make-variable-buffer-local 'isearch-mode)
300 ;;;===============================================================
301 ;;; Entry points to isearch-mode.
302 ;;; These four functions should be moved to loaddefs.el
304 (defun isearch-forward ()
306 Do incremental search forward.
307 As you type characters, they add to the search string and are found.
309 \\<isearch-mode-map>
310 Type \\[isearch-delete-char] to cancel characters from end of search
311 string.
312 Type \\[isearch-exit] to exit, leaving point at location found.
313 Type \\[isearch-repeat-forward] to search again forward,
314 \\[isearch-repeat-backward] to search again backward.
315 Type \\[isearch-toggle-regexp] to toggle regular expression with normal searching.
316 Type \\[isearch-yank-word] to yank word from buffer onto end of
317 search string and search for it.
318 Type \\[isearch-yank-line] to yank rest of line onto end of search string, etc.
319 Type \\[isearch-quote-char] to quote control character to search for it.
320 Type C-j to match end of line.
322 Also supported is a search ring of the previous 16 search strings.
323 Type \\[isearch-ring-advance] to search for the next item in the search ring.
324 Type \\[isearch-ring-retreat] to search for the previous item in the search ring.
326 Other control and meta characters terminate the search
327 and are then executed normally.
329 \\[isearch-quit] while searching or when search has failed
330 cancels input back to what has been found successfully.
331 \\[isearch-quit] when search is successful aborts and moves point to starting point.
333 All of these keys are bound in `isearch-mode-map' and
334 `isearch-mode-meta-map'. If `isearch-forward' is called
335 non-interactively, it does not return to the calling function until
336 the search is done."
337 (interactive)
338 (if (interactive-p)
339 (isearch-mode t)
340 (isearch-modal t)))
342 (defun isearch-forward-regexp ()
344 Do incremental search forward for regular expression.
345 Like ordinary incremental search except that your input
346 is treated as a regexp. See \\[isearch-forward] for more info."
347 (interactive)
348 (if (interactive-p)
349 (isearch-mode t t)
350 (isearch-modal t t)))
352 (defun isearch-backward ()
354 Do incremental search backward.
355 See \\[isearch-forward] for more information."
356 (interactive)
357 (if (interactive-p)
358 (isearch-mode nil)
359 (isearch-modal nil)))
361 (defun isearch-backward-regexp ()
363 Do incremental search backward for regular expression.
364 Like ordinary incremental search except that your input
365 is treated as a regexp. See \\[isearch-forward] for more info."
366 (interactive)
367 (if (interactive-p)
368 (isearch-mode nil t)
369 (isearch-modal nil t)))
372 (defun isearch-modal (forward &optional regexp op-fun)
373 ;; As an example of using the hooks, isearch-mode can be made
374 ;; modal (in the sense of not returning to the calling function
375 ;; until searching is completed) by entering a recursive-edit.
376 ;; This is used if the above functions are called non-interactively.
377 (let ((isearch-mode-hook
378 (cons (function (lambda () (recursive-edit)))
379 isearch-mode-hook))
380 (isearch-mode-end-hook
381 (cons (function (lambda () (exit-recursive-edit)))
382 isearch-mode-end-hook)))
383 (isearch-mode forward regexp op-fun)))
386 ;;;==================================================================
387 ;; isearch-mode only sets up incremental search for the minor mode.
388 ;; All the work is done by the isearch-mode commands.
390 (defun isearch-mode (forward &optional regexp op-fun)
391 "Start isearch minor mode. Called by isearch-forward, etc."
392 ;; Make buffer-local variables for isearching.
393 ;; We really need window-local variables.
394 (mapcar
395 'make-local-variable
396 '(isearch-forward
397 isearch-regexp isearch-string isearch-message
398 isearch-case-fold-search
399 isearch-cmds isearch-success isearch-wrapped
400 isearch-barrier isearch-adjusted isearch-invalid-regexp
401 isearch-slow-terminal-mode isearch-other-end isearch-small-window
402 isearch-found-point isearch-found-start isearch-opoint
403 isearch-window-configuration isearch-old-local-map))
405 ;; Initialize global vars.
406 (setq isearch-forward forward
407 isearch-regexp regexp
408 isearch-op-fun op-fun
409 isearch-case-fold-search case-fold-search
410 isearch-string ""
411 isearch-message ""
412 isearch-cmds nil
413 isearch-success t
414 isearch-wrapped nil
415 isearch-barrier (point)
416 isearch-adjusted nil
417 isearch-yank-flag nil
418 isearch-invalid-regexp nil
419 isearch-slow-terminal-mode (and (<= (baud-rate) search-slow-speed)
420 (> (window-height)
421 (* 4 search-slow-window-lines)))
422 isearch-other-end nil
423 isearch-small-window nil
424 isearch-found-point nil
426 isearch-found-start nil
427 isearch-opoint (point)
428 isearch-window-configuration (current-window-configuration)
429 isearch-old-local-map (current-local-map)
431 ;; inhibit-quit t
433 (setq isearch-mode " Isearch") ;; forward? regexp?
434 (set-buffer-modified-p (buffer-modified-p)) ; update modeline
436 (isearch-push-state)
438 (use-local-map isearch-mode-map)
439 (isearch-update)
440 (run-hooks 'isearch-mode-hook)
444 ;;;====================================================
445 ;; Some high level utilities. Others below.
447 (defun isearch-update ()
448 ;; Called after each command to update the display.
449 (or unread-command-char
450 (progn
451 (if (not (input-pending-p))
452 (isearch-message))
453 (if (and isearch-slow-terminal-mode
454 (not (or isearch-small-window
455 (pos-visible-in-window-p))))
456 (progn
457 (setq isearch-small-window t)
458 (setq isearch-found-point (point))
459 (move-to-window-line 0)
460 (let ((window-min-height 1))
461 (split-window nil (if (< search-slow-window-lines 0)
462 (1+ (- search-slow-window-lines))
463 (- (window-height)
464 (1+ search-slow-window-lines)))))
465 (if (< search-slow-window-lines 0)
466 (progn (vertical-motion (- 1 search-slow-window-lines))
467 (set-window-start (next-window) (point))
468 (set-window-hscroll (next-window)
469 (window-hscroll))
470 (set-window-hscroll (selected-window) 0))
471 (other-window 1))
472 (goto-char isearch-found-point)))))
473 (setq ;; quit-flag nil not for isearch-mode
474 isearch-adjusted nil
475 isearch-yank-flag nil)
479 (defun isearch-done ()
480 ;; Called by all commands that terminate isearch-mode.
481 (use-local-map isearch-old-local-map)
482 (setq isearch-found-start (window-start (selected-window)))
483 (setq isearch-found-point (point))
484 (set-window-configuration isearch-window-configuration)
486 (if (> (length isearch-string) 0)
487 ;; Update the ring data.
488 (if isearch-regexp
489 (if (not (setq regex-search-ring-yank-pointer
490 (memq isearch-string regex-search-ring)))
491 (progn
492 (setq regex-search-ring (cons isearch-string regex-search-ring)
493 regex-search-ring-yank-pointer regex-search-ring)
494 (if (> (length regex-search-ring) regex-search-ring-max)
495 (setcdr (nthcdr (1- search-ring-max) regex-search-ring)
496 nil))))
497 (if (not (setq search-ring-yank-pointer
498 (memq isearch-string search-ring)))
499 (progn
500 (setq search-ring (cons isearch-string search-ring)
501 search-ring-yank-pointer search-ring)
502 (if (> (length search-ring) search-ring-max)
503 (setcdr (nthcdr (1- search-ring-max) search-ring) nil))))))
505 ;; If there was movement, mark the starting position.
506 ;; Maybe should test difference between and set mark iff > threshold.
507 (if (/= (point) isearch-opoint)
508 (push-mark isearch-opoint)
509 (message ""))
510 (if isearch-small-window
511 (goto-char isearch-found-point)
512 ;; Exiting the save-window-excursion clobbers window-start; restore it.
513 (set-window-start (selected-window) isearch-found-start t))
515 ;; Kill buffer-local variables for isearching
516 (mapcar
517 'kill-local-variable
518 '(isearch-forward
519 isearch-regexp isearch-string isearch-message
520 isearch-case-fold-search
521 isearch-cmds isearch-success isearch-wrapped
522 isearch-barrier isearch-adjusted isearch-invalid-regexp
523 isearch-slow-terminal-mode isearch-other-end isearch-small-window
524 isearch-found-point isearch-found-start isearch-opoint
525 isearch-window-configuration isearch-old-local-map))
527 (setq isearch-mode nil)
528 (set-buffer-modified-p (buffer-modified-p))
529 (run-hooks 'isearch-mode-end-hook)
533 ;;;====================================================
534 ;; Commands active while inside of the isearch minor mode.
536 (defun isearch-exit ()
537 "Exit search normally.
538 However, if this is the first command after starting incremental
539 search and `search-nonincremental-instead' is non-nil, do a
540 nonincremental search instead."
542 (interactive)
543 (if (and search-nonincremental-instead
544 (= 0 (length isearch-string)))
545 (nonincremental-search isearch-forward isearch-regexp))
546 (isearch-done))
549 (defun isearch-edit-string ()
550 "Edit the search string in the minibuffer and return to incremental search."
551 ;; This doesnt back up the search point.
552 (interactive)
553 (setq isearch-string (read-string (isearch-message-prefix) isearch-string)
554 isearch-message (mapconcat 'text-char-description
555 isearch-string ""))
556 (isearch-push-state)
557 (isearch-search)
558 (isearch-update))
561 (defun isearch-quit ()
562 "Quit incremental search mode if searching is successful.
563 Otherwise, revert to previous successful search and continue searching."
564 (interactive)
565 (ding)
566 (discard-input)
567 (if isearch-success
568 ;; If search is successful, move back to starting point
569 ;; and really do quit.
570 (progn (goto-char isearch-opoint)
571 (isearch-done)) ; exit and quit
572 ;; If search is failing, rub out until it is once more
573 ;; successful.
574 (while (not isearch-success) (isearch-pop-state))
575 (isearch-update)))
578 (defun isearch-repeat (direction)
579 ;; Utility for isearch-repeat-forward and -backward.
580 (if (eq isearch-forward (eq direction 'forward))
581 ;; C-s in forward or C-r in reverse.
582 (if (equal isearch-string "")
583 ;; If search string is empty, use last one.
584 (setq isearch-string
585 ;; (if isearch-regexp
586 ;; search-last-regexp search-last-string)
587 (or (if isearch-regexp
588 (if regex-search-ring-yank-pointer
589 (car regex-search-ring-yank-pointer)
590 (car regex-search-ring))
591 (if search-ring-yank-pointer
592 (car search-ring-yank-pointer)
593 (car search-ring)))
595 isearch-message
596 (mapconcat 'text-char-description
597 isearch-string ""))
598 ;; If already have what to search for, repeat it.
599 (or isearch-success
600 (progn
602 (goto-char (if isearch-forward (point-min) (point-max)))
603 (setq isearch-wrapped t))))
604 ;; C-s in reverse or C-r in forward, change direction.
605 (setq isearch-forward (not isearch-forward)))
607 (setq isearch-barrier (point)) ; For subsequent \| if regexp.
608 (setq isearch-success t)
609 (or (equal isearch-string "")
610 (progn
611 ;; If repeating a search that found
612 ;; an empty string, ensure we advance.
613 (if (equal (match-end 0) (match-beginning 0))
614 (forward-char (if isearch-forward 1 -1)))
615 (isearch-search)))
616 (isearch-push-state)
617 (isearch-update))
619 (defun isearch-repeat-forward ()
620 "Repeat incremental search forwards."
621 (interactive)
622 (isearch-repeat 'forward))
624 (defun isearch-repeat-backward ()
625 "Repeat incremental search backwards."
626 (interactive)
627 (isearch-repeat 'backward))
629 (defun isearch-toggle-regexp ()
630 "Toggle regexp searching on or off."
631 ;; The status stack is left unchanged.
632 (interactive)
633 (setq isearch-regexp (not isearch-regexp))
634 (isearch-update))
636 (defun isearch-delete-char ()
637 "Discard last input item and move point back.
638 If no previous match was done, just beep."
639 (interactive)
640 (if (null (cdr isearch-cmds))
641 (ding)
642 (isearch-pop-state))
643 (isearch-update))
646 (defun isearch-yank (chunk)
647 ;; Helper for isearch-yank-word and isearch-yank-line
648 (let ((word (save-excursion
649 (and (not isearch-forward) isearch-other-end
650 (goto-char isearch-other-end))
651 (buffer-substring
652 (point)
653 (save-excursion
654 (cond
655 ((eq chunk 'word)
656 (forward-word 1))
657 ((eq chunk 'line)
658 (end-of-line)))
659 (point))))))
660 (if isearch-regexp (setq word (regexp-quote word)))
661 (setq isearch-string (concat isearch-string word)
662 isearch-message
663 (concat isearch-message
664 (mapconcat 'text-char-description
665 word ""))
666 ;; Don't move cursor in reverse search.
667 isearch-yank-flag t))
668 (isearch-search-and-update))
671 (defun isearch-yank-word ()
672 "Pull next word from buffer into search string."
673 (interactive)
674 (isearch-yank 'word))
676 (defun isearch-yank-line ()
677 "Pull rest of line from buffer into search string."
678 (interactive)
679 (isearch-yank 'line))
682 (defun isearch-search-and-update ()
683 ;; Do the search and update the display.
684 (if (and (not isearch-success)
685 ;; unsuccessful regexp search may become
686 ;; successful by addition of characters which
687 ;; make isearch-string valid
688 (not isearch-regexp))
690 ;; In reverse search, adding stuff at
691 ;; the end may cause zero or many more chars to be
692 ;; matched, in the string following point.
693 ;; Allow all those possibilities without moving point as
694 ;; long as the match does not extend past search origin.
695 (if (and (not isearch-forward) (not isearch-adjusted)
696 (condition-case ()
697 (looking-at (if isearch-regexp isearch-string
698 (regexp-quote isearch-string)))
699 (error nil))
700 (or isearch-yank-flag
701 (<= (match-end 0)
702 (min isearch-opoint isearch-barrier))))
703 (setq isearch-success t
704 isearch-invalid-regexp nil
705 isearch-other-end (match-end 0))
706 ;; Not regexp, not reverse, or no match at point.
707 (if (and isearch-other-end (not isearch-adjusted))
708 (goto-char (if isearch-forward isearch-other-end
709 (min isearch-opoint
710 isearch-barrier
711 (1+ isearch-other-end)))))
712 (isearch-search)
714 (isearch-push-state)
715 (if isearch-op-fun (funcall isearch-op-fun))
716 (isearch-update))
719 ;; *, ?, and | chars can make a regexp more liberal.
720 ;; They can make a regexp match sooner
721 ;; or make it succeed instead of failing.
722 ;; So go back to place last successful search started
723 ;; or to the last ^S/^R (barrier), whichever is nearer.
725 (defun isearch-*-char ()
726 "Handle * and ? specially in regexps."
727 (interactive)
728 (if isearch-regexp
730 (progn
731 (setq isearch-adjusted t)
732 (let ((cs (nth (if isearch-forward
733 5 ; isearch-other-end
734 2) ; saved (point)
735 (car (cdr isearch-cmds)))))
736 ;; (car isearch-cmds) is after last search;
737 ;; (car (cdr isearch-cmds)) is from before it.
738 (setq cs (or cs isearch-barrier))
739 (goto-char
740 (if isearch-forward
741 (max cs isearch-barrier)
742 (min cs isearch-barrier))))))
743 (isearch-process-search-char last-command-char))
747 (defun isearch-|-char ()
748 "If in regexp search, jump to the barrier."
749 (interactive)
750 (if isearch-regexp
751 (progn
752 (setq isearch-adjusted t)
753 (goto-char isearch-barrier)))
754 (isearch-process-search-char last-command-char))
758 (defun isearch-other-control-char ()
759 "Any other control char => unread it and exit the search normally.
760 But only if `search-exit-option' is non-nil."
761 (interactive)
762 (if search-exit-option
763 (progn
764 (setq unread-command-char last-command-char)
765 (isearch-done))
766 ;; otherwise
767 (isearch-search-and-update)))
770 (defun isearch-other-meta-char ()
771 "Any other meta char => exit the search normally and reexecute the whole key.
772 But only if `search-exit-option' is non-nil."
773 ;; This will probably work in place of isearch-other-control-char too,
774 ;; but here we use unwind-protect and command-execute since it is
775 ;; a multi-char key we would want to unread.
776 (interactive)
777 (if search-exit-option
778 (unwind-protect
779 (isearch-done) ;; this exits recursive edit
780 ;; Reexecute the key.
781 (command-execute (this-command-keys)))
782 ;; otherwise
783 (isearch-search-and-update)))
786 (defun isearch-quote-char ()
787 "Quote special characters for incremental search."
788 (interactive)
789 (isearch-process-search-char (read-quoted-char (isearch-message t))))
792 (defun isearch-return-char ()
793 "Convert return into newline for incremental search.
794 Obsolete."
795 (interactive)
796 (isearch-process-search-char ?\n))
799 (defun isearch-printing-char ()
800 "Any other printing character => add it to the search string and search."
801 (interactive)
802 (isearch-process-search-char last-command-char))
805 (defun isearch-upper-case-char ()
806 "Any upper case char => turn off case fold search for remainder of search."
807 ;; This feature only applies to interactively entered chars,
808 ;; but not yanked chars, repeat default searches, or search ring searches.
809 ;; Support for these should be easy to add.
810 (interactive)
811 (if search-caps-disable-folding
812 (setq isearch-case-fold-search nil))
813 (isearch-printing-char))
815 (defun isearch-whitespace-chars ()
816 "Match all whitespace chars, if in regexp mode."
817 (interactive)
818 (if (and isearch-regexp search-whitespace-regexp)
819 (isearch-process-search-string search-whitespace-regexp " ")
820 (isearch-other-meta-char)))
822 (defun isearch-process-search-char (char)
823 ;; Append the char to the search string, update the message and re-search.
824 (isearch-process-search-string (char-to-string char)
826 (text-char-description char)))
828 (defun isearch-process-search-string (string message)
829 (setq isearch-string (concat isearch-string string)
830 isearch-message (concat isearch-message message))
831 (isearch-search-and-update))
834 ;;===========================================================
835 ;; Search Ring
837 (defun isearch-ring-adjust (advance)
838 ;; helper for isearch-ring-advance and isearch-ring-retreat
839 (if (cdr isearch-cmds)
840 (isearch-pop-state))
841 (let* ((ring (if isearch-regexp regex-search-ring search-ring))
842 (length (length ring))
843 (yank-pointer-name (if isearch-regexp
844 'regex-search-ring-yank-pointer
845 'search-ring-yank-pointer))
846 (yank-pointer (eval yank-pointer-name)))
847 (if (zerop length)
849 (set yank-pointer-name
850 (setq yank-pointer
851 (nthcdr (% (+ (- length (length yank-pointer))
852 (if advance (1- length) 1))
853 length) ring)))
854 (setq isearch-string (car yank-pointer)
855 isearch-message (mapconcat 'text-char-description
856 isearch-string ""))))
857 (isearch-push-state)
858 (isearch-search)
859 (isearch-update))
861 (defun isearch-ring-advance ()
862 "Advance to the next search string in the ring."
863 (interactive)
864 (isearch-ring-adjust 'advance))
866 (defun isearch-ring-retreat ()
867 "Retreat to the previous search string in the ring."
868 (interactive)
869 (isearch-ring-adjust nil))
872 ;;;=============================================================
873 ;; Window-local variables
874 ;; (not used yet - and maybe never)
876 (defvar window-local-variable-alist nil
877 "An alist of windows associated with window local variables and values.
878 The cdr of each item is another alist of variables and values.")
880 (defvar last-local-window nil)
881 (defvar last-window-local-vars nil)
883 (defun kill-window-local-variables ()
884 "Remove the old variable list, if any."
885 (setq window-local-variable-alist
886 (delq window-local-variable-alist
887 (assq (selected-window)
888 window-local-variable-alist))))
890 ;; Assume that window-local variables are not buffer-local
891 ;; so we can delay storing until absolutely necessary.
893 (defun store-window-local-variables (&rest vars-and-vals)
894 "Store the window local variables for selected window."
895 (setq last-local-window (selected-window))
896 (setq last-window-local-vars vars-and-vals))
899 (defun fetch-window-local-variables ()
900 "Fetch the window local variables for selected window.
901 Does nothing if the last store was for the same window."
902 (if (not (eq (selected-window) last-local-window))
903 (progn
904 ;; First store the previous values.
905 (setq window-local-variable-alist
906 (cons (cons last-local-window
907 last-window-local-vars)
908 (delq window-local-variable-alist
909 (assq last-local-window
910 window-local-variable-alist))))
911 ;; Now fetch the values for the selected-window.
912 (setq last-local-window (selected-window))
913 (setq last-window-local-vars
914 (cdr (assq last-local-window window-local-variable-alist)))
915 (let ((vars-and-vals last-window-local-vars))
916 (while vars-and-vals
917 (set (car vars-and-vals) (car (cdr vars-and-vals)))
918 (setq vars-and-vals (cdr (cdr vars-and-vals))))))))
922 ;;;==============================================================
923 ;; The search status stack (and isearch window-local variables, not used).
925 (defun isearch-top-state ()
926 ;; (fetch-window-local-variables)
927 (let ((cmd (car isearch-cmds)))
928 (setq isearch-string (car cmd)
929 isearch-message (car (cdr cmd))
930 isearch-success (nth 3 cmd)
931 isearch-forward (nth 4 cmd)
932 isearch-other-end (nth 5 cmd)
933 isearch-invalid-regexp (nth 6 cmd)
934 isearch-wrapped (nth 7 cmd)
935 isearch-barrier (nth 8 cmd))
936 (goto-char (car (cdr (cdr cmd))))))
938 (defun isearch-pop-state ()
939 ;; (fetch-window-local-variables)
940 (setq isearch-cmds (cdr isearch-cmds))
941 (isearch-top-state)
944 (defun isearch-push-state ()
945 (setq isearch-cmds
946 (cons (list isearch-string isearch-message (point)
947 isearch-success isearch-forward isearch-other-end
948 isearch-invalid-regexp isearch-wrapped isearch-barrier)
949 isearch-cmds)))
951 (defun isearch-store-variables ()
952 (store-window-local-variables
953 'isearch-cmds isearch-cmds
954 'isearch-regexp isearch-regexp
955 'isearch-adjusted isearch-adjusted
956 'isearch-slow-terminal-mode isearch-slow-terminal-mode
957 'isearch-small-window isearch-small-window
958 'isearch-found-point isearch-found-point
959 'isearch-found-start isearch-found-start
960 'isearch-opoint isearch-opoint
961 'isearch-window-configuration isearch-window-configuration
962 'isearch-old-local-map isearch-old-local-map
966 ;;;==================================================================
967 ;; Message string
969 (defun isearch-message (&optional c-q-hack ellipsis)
970 ;; Generate and print the message string.
971 (let ((cursor-in-echo-area ellipsis)
972 (m (concat
973 (isearch-message-prefix c-q-hack ellipsis)
974 isearch-message
975 (isearch-message-suffix c-q-hack ellipsis)
977 (if c-q-hack m (message "%s" m))))
979 (defun isearch-message-prefix (&optional c-q-hack ellipsis)
980 ;; If about to search, and previous search regexp was invalid,
981 ;; check that it still is. If it is valid now,
982 ;; let the message we display while searching say that it is valid.
983 (and isearch-invalid-regexp ellipsis
984 (condition-case ()
985 (progn (re-search-forward isearch-string (point) t)
986 (setq isearch-invalid-regexp nil))
987 (error nil)))
988 ;; If currently failing, display no ellipsis.
989 (or isearch-success (setq ellipsis nil))
990 (let ((m (concat (if isearch-success "" "failing ")
991 (if isearch-wrapped "wrapped ")
992 (if isearch-regexp "regexp " "")
993 "I-search"
994 (if isearch-forward ": " " backward: ")
996 (aset m 0 (upcase (aref m 0)))
1000 (defun isearch-message-suffix (&optional c-q-hack ellipsis)
1001 (concat (if c-q-hack "^Q" "")
1002 (if isearch-invalid-regexp
1003 (concat " [" isearch-invalid-regexp "]")
1004 "")))
1007 ;;;========================================================
1008 ;;; Searching
1010 (defun isearch-search ()
1011 ;; Do the search with the current search string.
1012 (isearch-message nil t)
1013 (condition-case lossage
1014 (let ((inhibit-quit nil)
1015 (case-fold-search isearch-case-fold-search))
1016 (if isearch-regexp (setq isearch-invalid-regexp nil))
1017 (setq isearch-success
1018 (funcall
1019 (if isearch-regexp
1020 (if isearch-forward 're-search-forward 're-search-backward)
1021 (if isearch-forward 'search-forward 'search-backward))
1022 isearch-string nil t))
1023 (if isearch-success
1024 (setq isearch-other-end
1025 (if isearch-forward (match-beginning 0) (match-end 0)))))
1027 (quit (setq unread-command-char ?\C-g)
1028 (setq isearch-success nil))
1030 (invalid-regexp
1031 (setq isearch-invalid-regexp (car (cdr lossage)))
1032 (if (string-match
1033 "\\`Premature \\|\\`Unmatched \\|\\`Invalid "
1034 isearch-invalid-regexp)
1035 (setq isearch-invalid-regexp "incomplete input"))))
1037 (if isearch-success
1039 ;; Ding if failed this time after succeeding last time.
1040 (and (nth 3 (car isearch-cmds))
1041 (ding))
1042 (goto-char (nth 2 (car isearch-cmds)))))
1044 ;;;=================================================
1045 ;; This is called from incremental-search
1046 ;; if the first input character is the exit character.
1048 ;; We store the search string in `isearch-string'
1049 ;; which has been bound already by `isearch-search'
1050 ;; so that, when we exit, it is copied into `search-last-string'.
1052 (defun nonincremental-search (forward regexp)
1053 ;; This may be broken. Anyway, it could be replaced by the
1054 ;; isearch-edit-string command instead.
1055 (setq isearch-forward forward
1056 isearch-regexp regexp)
1057 (let (char function
1058 inhibit-quit
1059 (cursor-in-echo-area t))
1060 ;; Prompt assuming not word search,
1061 (setq isearch-message
1063 (if isearch-regexp
1065 (if isearch-forward "Regexp search: "
1066 "Regexp search backward: ")
1067 (if isearch-forward "Search: " "Search backward: ")))
1068 (message "%s" isearch-message)
1069 ;; Read 1 char and switch to word search if it is ^W.
1070 (setq char (read-char))
1071 (if (eq char search-yank-word-char)
1072 (setq isearch-message (if isearch-forward "Word search: "
1074 "Word search backward: "))
1075 ;; Otherwise let that 1 char be part of the search string.
1076 (setq unread-command-char char))
1077 (setq function
1078 (if (eq char search-yank-word-char)
1079 (if isearch-forward 'word-search-forward 'word-search-backward)
1080 (if isearch-regexp
1081 (if isearch-forward 're-search-forward 're-search-backward)
1082 (if isearch-forward 'search-forward 'search-backward))))
1083 ;; Read the search string with corrected prompt.
1084 (setq isearch-string (read-string isearch-message isearch-string))
1085 ;; Empty means use default.
1086 (if (= 0 (length isearch-string))
1087 (setq isearch-string search-last-string)
1088 ;; Set last search string now so it is set even if we fail.
1089 (setq search-last-string isearch-string))
1090 ;; Since we used the minibuffer, we should be available for redo.
1091 (setq command-history
1093 (cons (list function isearch-string) command-history))
1094 ;; Go ahead and search.
1095 (let ((case-fold-search isearch-case-fold-search))
1096 (funcall function isearch-string))))