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