Fix minor bugs in previous change.
[bpt/emacs.git] / lisp / isearch.el
CommitLineData
08200510 1;; Incremental search minor mode.
3a801d0c
ER
2;; Copyright (C) 1992 Free Software Foundation, Inc.
3
08200510
RS
4;; LCD Archive Entry:
5;; isearch-mode|Daniel LaLiberte|liberte@cs.uiuc.edu
6;; |A minor mode replacement for isearch.el.
4ae7d00a 7;; |$Date: 1993/02/17 20:34:20 $|$Revision: 1.18 $|~/modes/isearch-mode.el
8e1cae6d 8
08200510
RS
9;; This file is not yet part of GNU Emacs, but it is based almost
10;; entirely on isearch.el which is part of GNU Emacs.
8e1cae6d
JB
11
12;; GNU Emacs is distributed in the hope that it will be useful,
13;; but WITHOUT ANY WARRANTY. No author or distributor
14;; accepts responsibility to anyone for the consequences of using it
15;; or for whether it serves any particular purpose or works at all,
16;; unless he says so in writing. Refer to the GNU Emacs General Public
17;; License for full details.
18
19;; Everyone is granted permission to copy, modify and redistribute
20;; GNU Emacs, but only under the conditions described in the
21;; GNU Emacs General Public License. A copy of this license is
22;; supposed to have been given to you along with GNU Emacs so you
23;; can know your rights and responsibilities. It should be in a
24;; file named COPYING. Among other things, the copyright notice
25;; and this notice must be preserved on all copies.
26
8e1cae6d
JB
27;;;====================================================================
28;; Instructions
29
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.
33
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.
38
39;; (fset 'isearch 'isearch-mode)
40;; (autoload 'isearch-mode "isearch-mode")
41
08200510
RS
42;; For programmed use of isearch-mode, e.g. calling (isearch-forward),
43;; isearch-mode behaves modally and does not return until the search
44;; is completed. It uses a recursive-edit to behave this way. Note:
45;; gnus does it wrong: (call-interactively 'isearch-forward).
46
47;; If any package you use invokes isearching non-interactively to get
48;; the modal behavior described above, you must use the redefinitions
49;; of isearch-forward, etc. found in this file instead of those in
50;; loaddefs.el. The simplest way to ensure this is to just load
51;; isearch-mode explicitly in your .emacs instead of using the above
52;; fset and autoload.
53
54;; (load "isearch-mode")
55
8e1cae6d 56;; The key bindings active within isearch-mode are defined below in
08200510
RS
57;; `isearch-mode-map' which is given bindings close to the default
58;; characters of the original isearch.el. With `isearch-mode',
59;; however, you can bind multi-character keys and it should be easier
60;; to add new commands. One bug though: keys with meta-prefix cannot
61;; be longer than two chars. Also see minibuffer-local-isearch-map
62;; for bindings active during `isearch-edit-string'.
63
64;; Note to emacs version 19 users: isearch-mode should work even if
65;; you switch windows with the mouse, in which case isearch-mode is
66;; terminated automatically before the switch. This is true of lemacs
67;; too, with a few more cleanups I've neglected in this release.
68;; No one has supplied patches for epoch yet.
69
70;; The search ring and completion commands automatically put you in
71;; the minibuffer to edit the string. This gives you a chance to
72;; modify the search string before executing the search. There are
73;; three commands to terminate the editing: C-s and C-r exit the
74;; minibuffer and search forward and reverse respectively, while C-m
75;; exits and does a nonincremental search.
76
77;; Exiting immediately from isearch uses isearch-edit-string instead
78;; of nonincremental-search, if search-nonincremental-instead is non-nil.
79;; The name of this option should probably be changed if we decide to
80;; keep the behavior. No point in forcing nonincremental search until
81;; the last possible moment.
82
83;; TODO
84;; - Integrate the emacs 19 generalized commmand history.
85;; - Think about incorporating query-replace.
86;; - Hooks and options for failed search.
282d89c0
ER
87
88;;;====================================================================
08200510
RS
89;;; Change History
90
4ae7d00a 91;;; $Header: /gd/gnu/emacs/19.0/lisp/RCS/isearch-mode.el,v 1.18 1993/02/17 20:34:20 rms Exp rms $
49c13105 92;;; $Log: isearch-mode.el,v $
4ae7d00a
RS
93; Revision 1.18 1993/02/17 20:34:20 rms
94; (isearch-backward-regexp):
95; New arg no-recursive-edit, always non-nil for interactive call.
96; Rename first arg, and set it right in interactive call.
97; (isearch-forward-regexp): Likewise.
98; (isearch-forward, isearch-backward): Likewise no-recursive-edit.
99;
eceee2c0
RS
100; Revision 1.17 1993/01/26 01:48:27 jimb
101; JimB's changes since January 18th
102;
dbc4e1c1
JB
103; Revision 1.16 1992/11/16 01:37:06 jimb
104; * bytecomp.el: Declare unread-command-char an obsolete variable.
105; * vip.el (vip-escape-to-emacs, vip-prefix-arg-value,
106; vip-prefix-arg-com): Use unread-command-event instead of
107; unread-command-char; respect its new semantics.
108; * isearch-mode.el (isearch-update, isearch-unread): Same.
109;
797a37c7
JB
110; Revision 1.15 1992/11/07 06:17:04 jimb
111; * isearch.el (isearch-frames-exist): This isn't what we want -
112; replaced by...
113; (isearch-gnu-emacs-events): non-nil if should expect events in the
114; style generated by GNU Emacs 19. Set if set-frame-height is
115; fboundp; this is true on any GNU Emacs 19, whether or not it was
116; compiled with multiple frame support.
117; (isearch-mode-map): Test isearch-gnu-emacs-events instead of
118; isearch-frames-exist to see if we should bind switch-frame events.
119; (isearch-update): Test isearch-gnu-emacs-events instead of
120; isearch-frames-exist to see if unread-command-char's quiescent
121; value is nil or -1.
122;
1a3a6707
JB
123; Revision 1.14 1992/11/01 22:10:59 rms
124; (isearch-search): Handle all sorts of errors from regexp search.
125;
f1c03125
RS
126; Revision 1.13 1992/10/27 04:11:46 rms
127; (isearch-edit-string):
128; Bind cursor-in-echo-area only around read-char/allocate-event.
129;
d94307ee
RS
130; Revision 1.12 1992/10/20 21:21:47 rms
131; (isearch-mode-map): Make the top-level keymap dense.
132; Explicitly bind control characters at that level.
133;
ac689515
RS
134; Revision 1.11 1992/10/11 05:25:11 rms
135; (isearch-ring-advance-edit): Delete spurious `)'.
136;
49c13105
RS
137; Revision 1.10 1992/09/21 08:28:43 rms
138; entered into RCS
139;
08200510
RS
140;;; Revision 1.4 92/09/14 16:26:02 liberte
141;;; Added prefix args to isearch-forward, etc. to switch between
142;;; string and regular expression searching.
143;;; Added some support for lemacs.
144;;; Added general isearch-highlight option - but only for lemacs so far.
145;;; Added support for frame switching in emacs 19.
146;;; Added word search option to isearch-edit-string.
147;;; Renamed isearch-quit to isearch-abort.
148;;; Numerous changes to comments and doc strings.
282d89c0 149;;;
08200510
RS
150;;; Revision 1.3 92/06/29 13:10:08 liberte
151;;; Moved modal isearch-mode handling into isearch-mode.
152;;; Got rid of buffer-local isearch variables.
153;;; isearch-edit-string used by ring adjustments, completion, and
154;;; nonincremental searching. C-s and C-r are additional exit commands.
155;;; Renamed all regex to regexp.
156;;; Got rid of found-start and found-point globals.
157;;; Generalized handling of upper-case chars.
158
159;;; Revision 1.2 92/05/27 11:33:57 liberte
160;;; Emacs version 19 has a search ring, which is supported here.
161;;; Other fixes found in the version 19 isearch are included here.
162;;;
163;;; Also see variables search-caps-disable-folding,
164;;; search-nonincremental-instead, search-whitespace-regexp, and
165;;; commands isearch-toggle-regexp, isearch-edit-string.
166;;;
167;;; semi-modal isearching is supported.
282d89c0
ER
168
169;;; Changes for 1.1
170;;; 3/18/92 Fixed invalid-regexp.
171;;; 3/18/92 Fixed yanking in regexps.
172
08200510
RS
173
174\f
175;;;=========================================================================
176;;; Emacs features
177
178;; isearch-mode takes advantage of the features provided by several
179;; different versions of emacs. Rather than testing the version of
180;; emacs, several constants are defined, one for each of the features.
181;; Each of the tests below must work on any version of emacs.
182;; (Perhaps provide and featurep could be used for this purpose.)
183
1a3a6707 184(defconst isearch-gnu-emacs-events (fboundp 'set-frame-height)) ;; emacs 19
08200510 185(defconst isearch-pre-command-hook-exists (boundp 'pre-command-hook)) ;; lemacs
a5dcf63f 186(defconst isearch-event-data-type nil) ;; lemacs
08200510 187
fdf5afa4 188(defconst search-exit-option t
a8f783b2 189 "*Non-nil means random control characters terminate incremental search.")
fdf5afa4
RS
190
191(defvar search-slow-window-lines 1
192 "*Number of lines in slow search display windows.
193These are the short windows used during incremental search on slow terminals.
194Negative means put the slow search window at the top (normally it's at bottom)
195and the value is minus the number of lines.")
196
197(defvar search-slow-speed 1200
198 "*Highest terminal speed at which to use \"slow\" style incremental search.
199This is the style where a one-line window is created to show the line
200that the search has reached.")
8e1cae6d
JB
201
202;;;========================================================================
203;;; Some additional options and constants.
204
fdf5afa4 205(defvar search-upper-case t
8e1cae6d 206 "*If non-nil, upper case chars disable case fold searching.
08200510
RS
207That is, upper and lower case chars must match exactly.
208This applies no matter where the chars come from, but does not
fdf5afa4
RS
209apply to chars in regexps that are prefixed with `\\'.
210If this value is `not-yanks', yanked text is always downcased.")
8e1cae6d
JB
211
212(defvar search-nonincremental-instead t
213 "*If non-nil, do a nonincremental search instead if exiting immediately.
08200510
RS
214Actually, `isearch-edit-string' is called to let you enter the search
215string, and RET terminates editing and does a nonincremental search.")
8e1cae6d
JB
216
217(defconst search-whitespace-regexp "\\s-+"
218 "*If non-nil, regular expression to match a sequence of whitespace chars.
219You might want to use something like \"[ \\t\\r\\n]+\" instead.")
220
a8f783b2
RS
221;; I removed the * from the doc string because highlighting is not
222;; currently a clean thing to do. Once highlighting is made clean,
223;; this feature can be re-enabled and advertised.
08200510 224(defvar search-highlight nil
a8f783b2 225 "Whether isearch and query-replace should highlight the text which
08200510
RS
226currently matches the search-string.")
227
228
229(defvar isearch-mode-hook nil
230 "Function(s) to call after starting up an incremental search.")
231
232(defvar isearch-mode-end-hook nil
233 "Function(s) to call after terminating an incremental search.")
234
8e1cae6d
JB
235;;;==================================================================
236;;; Search ring.
8e1cae6d
JB
237
238(defvar search-ring nil
239 "List of search string sequences.")
08200510 240(defvar regexp-search-ring nil
8e1cae6d
JB
241 "List of regular expression search string sequences.")
242
243(defconst search-ring-max 16
244 "*Maximum length of search ring before oldest elements are thrown away.")
08200510
RS
245(defconst regexp-search-ring-max 16
246 "*Maximum length of regexp search ring before oldest elements are thrown away.")
8e1cae6d
JB
247
248(defvar search-ring-yank-pointer nil
a5dcf63f
RS
249 "Index in `search-ring' of last string reused.
250nil if none yet.")
08200510 251(defvar regexp-search-ring-yank-pointer nil
a5dcf63f
RS
252 "Index in `regexp-search-ring' of last string reused.
253nil if none yet.")
8e1cae6d 254
08200510
RS
255(defvar search-ring-update nil
256 "*Non-nil if advancing or retreating in the search ring should cause search.
257Default value, nil, means edit the string instead.")
258
8e1cae6d
JB
259;;;====================================================
260;;; Define isearch-mode keymap.
261
262(defvar isearch-mode-map nil
263 "Keymap for isearch-mode.")
264
08200510
RS
265(or isearch-mode-map
266 (let* ((i 0)
fdf5afa4 267 (map (make-keymap)))
08200510 268
ac689515
RS
269 ;; Make function keys, etc, exit the search.
270 (define-key map [t] 'isearch-other-control-char)
08200510 271 ;; Control chars, by default, end isearch mode transparently.
ac689515
RS
272 ;; We need these explicit definitions because, in a dense keymap,
273 ;; the binding for t does not affect characters.
274 ;; We use a dense keymap to save space.
08200510
RS
275 (while (< i ?\ )
276 (define-key map (make-string 1 i) 'isearch-other-control-char)
277 (setq i (1+ i)))
278
279 ;; Printing chars extend the selection by default.
ac689515 280 (setq i ?\ )
fdf5afa4 281 (while (< i 128)
08200510
RS
282 (define-key map (make-string 1 i) 'isearch-printing-char)
283 (setq i (1+ i)))
284
285 ;; Several non-printing chars change the searching behavior.
286 (define-key map "\C-s" 'isearch-repeat-forward)
287 (define-key map "\C-r" 'isearch-repeat-backward)
288 (define-key map "\177" 'isearch-delete-char)
289 (define-key map "\C-g" 'isearch-abort)
8e1cae6d 290
08200510
RS
291 (define-key map "\C-q" 'isearch-quote-char)
292
293 ;; (define-key map "\r" 'isearch-return-char)
294 ;; For version 19, RET (C-m) terminates search and LFD (C-j) matches eol.
295 ;; We could make this conditional.
296 (define-key map "\r" 'isearch-exit)
297 (define-key map "\C-j" 'isearch-printing-char)
298 (define-key map "\t" 'isearch-printing-char)
299 (define-key map " " 'isearch-whitespace-chars)
8e1cae6d 300
08200510
RS
301 (define-key map "\C-w" 'isearch-yank-word)
302 (define-key map "\C-y" 'isearch-yank-line)
303
304 ;; Define keys for regexp chars * ? |.
305 ;; Nothing special for + because it matches at least once.
306 (define-key map "*" 'isearch-*-char)
307 (define-key map "?" 'isearch-*-char)
308 (define-key map "|" 'isearch-|-char)
309
310 ;; You can reenable global keys by binding them locally to nil.
311 ;; For the help char this doesnt work quite as expected because
312 ;; isearch-mode is not a major mode. Also the echo area is not
313 ;; restored after the help command while isearch-mode is
314 ;; still active. Furthermore, we should not assume that the
315 ;; help-command is on C-h. But here is how it would be done:
316 ;; (define-key map "\C-h" nil)
317
318 ;; Instead bind C-h to special help command for isearch-mode.
319 (define-key map "\C-h" 'isearch-mode-help)
320
321 ;; To handle local bindings with meta char prefix keys, define
322 ;; another full keymap. This must be done for any other prefix
323 ;; keys as well, one full keymap per char of the prefix key. It
324 ;; would be simpler to disable the global keymap, and/or have a
325 ;; default local key binding for any key not otherwise bound.
ac689515
RS
326 (define-key map (char-to-string meta-prefix-char) (make-sparse-keymap))
327 (define-key map (vector meta-prefix-char t) 'isearch-other-meta-char)
328;;; (setq i 0)
329;;; (while (< i 128)
330;;; (define-key map (char-to-string (+ 128 i));; Needs to be generalized.
331;;; 'isearch-other-meta-char)
332;;; (setq i (1+ i)))
08200510
RS
333
334 (define-key map "\M-n" 'isearch-ring-advance)
335 (define-key map "\M-p" 'isearch-ring-retreat)
1a3a6707 336
08200510
RS
337 (define-key map "\M-\t" 'isearch-complete)
338
339 ;; For emacs 19, switching frames should terminate isearch-mode
1a3a6707 340 (if isearch-gnu-emacs-events
08200510
RS
341 (define-key map [switch-frame] 'isearch-switch-frame-handler))
342
343 (setq isearch-mode-map map)
344 ))
8e1cae6d 345
08200510
RS
346;; Some bindings you may want to put in your isearch-mode-hook.
347;; Suggest some alternates...
348;; (define-key isearch-mode-map "\C-t" 'isearch-toggle-regexp)
349;; (define-key isearch-mode-map "\C-^" 'isearch-edit-string)
8e1cae6d 350
8e1cae6d 351
08200510
RS
352(defvar minibuffer-local-isearch-map nil
353 "Keymap for editing isearch strings in the minibuffer.")
8e1cae6d 354
08200510
RS
355(or minibuffer-local-isearch-map
356 (let ((map (copy-keymap minibuffer-local-map)))
357 (define-key map "\r" 'isearch-nonincremental-exit-minibuffer)
35a4d143
RS
358 (define-key map "\M-n" 'isearch-ring-advance-edit)
359 (define-key map "\M-p" 'isearch-ring-retreat-edit)
08200510
RS
360 (define-key map "\M-\t" 'isearch-complete-edit)
361 (define-key map "\C-s" 'isearch-forward-exit-minibuffer)
362 (define-key map "\C-r" 'isearch-reverse-exit-minibuffer)
363 (setq minibuffer-local-isearch-map map)
364 ))
8e1cae6d
JB
365
366;;;========================================================
367;; Internal variables declared globally for byte-compiler.
08200510
RS
368;; These are all set with setq while isearching
369;; and bound locally while editing the search string.
370
371(defvar isearch-forward nil) ; Searching in the forward direction.
372(defvar isearch-regexp nil) ; Searching for a regexp.
373(defvar isearch-word nil) ; Searching for words.
374
375(defvar isearch-cmds nil) ; Stack of search status sets.
376(defvar isearch-string "") ; The current search string.
377(defvar isearch-message "") ; text-char-description version of isearch-string
378
379(defvar isearch-success t) ; Searching is currently successful.
380(defvar isearch-invalid-regexp nil) ; Regexp not well formed.
381(defvar isearch-other-end nil) ; Start (end) of match if forward (backward).
382(defvar isearch-wrapped nil) ; Searching restarted from the top (bottom).
8e1cae6d
JB
383(defvar isearch-barrier 0)
384
08200510 385(defvar isearch-case-fold-search nil) ; case-fold-search while searching.
8e1cae6d
JB
386
387(defvar isearch-adjusted nil)
388(defvar isearch-slow-terminal-mode nil)
08200510
RS
389;;; If t, using a small window.
390(defvar isearch-small-window nil)
8e1cae6d 391(defvar isearch-opoint 0)
08200510
RS
392;;; The window configuration active at the beginning of the search.
393(defvar isearch-window-configuration nil)
394(defvar isearch-old-local-map nil)
8e1cae6d 395
08200510
RS
396;; Flag to indicate a yank occurred, so don't move the cursor.
397(defvar isearch-yank-flag nil)
8e1cae6d 398
08200510
RS
399;;; A function to be called after each input character is processed.
400;;; (It is not called after characters that exit the search.)
401;;; It is only set from an optional argument to `isearch-mode'.
402(defvar isearch-op-fun nil)
8e1cae6d 403
08200510
RS
404;;; Is isearch-mode in a recursive edit for modal searching.
405(defvar isearch-recursive-edit nil)
8e1cae6d 406
08200510
RS
407;;; Should isearch be terminated after doing one search?
408(defvar isearch-nonincremental nil)
409
410;; New value of isearch-forward after isearch-edit-string.
411(defvar isearch-new-forward nil)
8e1cae6d 412
8e1cae6d
JB
413
414;;;==============================================================
415;; Minor-mode-alist changes - kind of redundant with the
416;; echo area, but if isearching in multiple windows, it can be useful.
417
418(or (assq 'isearch-mode minor-mode-alist)
419 (nconc minor-mode-alist
420 (list '(isearch-mode isearch-mode))))
421
08200510 422(defvar isearch-mode nil) ;; Name of the minor mode, if non-nil.
8e1cae6d
JB
423(make-variable-buffer-local 'isearch-mode)
424
fdf5afa4
RS
425(define-key global-map "\C-s" 'isearch-forward)
426(define-key esc-map "\C-s" 'isearch-forward-regexp)
427(define-key global-map "\C-r" 'isearch-backward)
428(define-key esc-map "\C-r" 'isearch-backward-regexp)
429
8e1cae6d
JB
430;;;===============================================================
431;;; Entry points to isearch-mode.
08200510
RS
432;;; These four functions should replace those in loaddefs.el
433;;; An alternative is to fset isearch-forward etc to isearch-mode,
434;;; and look at this-command to set the options accordingly.
8e1cae6d 435
eceee2c0 436(defun isearch-forward (&optional regexp-p no-recursive-edit)
8e1cae6d
JB
437 "\
438Do incremental search forward.
08200510
RS
439With a prefix argument, do an incremental regular expression search instead.
440\\<isearch-mode-map>
8e1cae6d 441As you type characters, they add to the search string and are found.
08200510 442The following non-printing keys are bound in `isearch-mode-map'.
8e1cae6d 443
08200510 444Type \\[isearch-delete-char] to cancel characters from end of search string.
8e1cae6d 445Type \\[isearch-exit] to exit, leaving point at location found.
08200510
RS
446Type LFD (C-j) to match end of line.
447Type \\[isearch-repeat-forward] to search again forward,\
448 \\[isearch-repeat-backward] to search again backward.
449Type \\[isearch-yank-word] to yank word from buffer onto end of search\
450 string and search for it.
451Type \\[isearch-yank-line] to yank rest of line onto end of search string\
452 and search for it.
8e1cae6d 453Type \\[isearch-quote-char] to quote control character to search for it.
08200510
RS
454Type \\[isearch-whitespace-chars] to match all whitespace chars in regexp.
455\\[isearch-abort] while searching or when search has failed cancels input\
456 back to what has
457 been found successfully.
458\\[isearch-abort] when search is successful aborts and moves point to\
459 starting point.
8e1cae6d
JB
460
461Also supported is a search ring of the previous 16 search strings.
462Type \\[isearch-ring-advance] to search for the next item in the search ring.
08200510
RS
463Type \\[isearch-ring-retreat] to search for the previous item in the search\
464 ring.
465Type \\[isearch-complete] to complete the search string using the search ring.
8e1cae6d 466
08200510
RS
467The above keys, bound in `isearch-mode-map', are often controlled by
468 options; do M-x apropos on search-.* to find them.
8e1cae6d 469Other control and meta characters terminate the search
08200510 470 and are then executed normally (depending on `search-exit-option').
8e1cae6d 471
08200510
RS
472If this function is called non-interactively, it does not return to
473the calling function until the search is done."
8e1cae6d 474
eceee2c0 475 (interactive "P\np")
4ae7d00a 476 (isearch-mode t (not (null regexp-p)) nil (not no-recursive-edit)))
8e1cae6d 477
eceee2c0 478(defun isearch-forward-regexp (&optional not-regexp no-recursive-edit)
8e1cae6d
JB
479 "\
480Do incremental search forward for regular expression.
08200510 481With a prefix argument, do a regular string search instead.
8e1cae6d
JB
482Like ordinary incremental search except that your input
483is treated as a regexp. See \\[isearch-forward] for more info."
eceee2c0 484 (interactive "P\np")
4ae7d00a 485 (isearch-mode t (null not-regexp) nil (not no-recursive-edit)))
8e1cae6d 486
eceee2c0 487(defun isearch-backward (&optional regexp-p no-recursive-edit)
8e1cae6d
JB
488 "\
489Do incremental search backward.
08200510 490With a prefix argument, do a regular expression search instead.
8e1cae6d 491See \\[isearch-forward] for more information."
eceee2c0 492 (interactive "P\np")
4ae7d00a 493 (isearch-mode nil (not (null regexp-p)) nil (not no-recursive-edit)))
8e1cae6d 494
eceee2c0 495(defun isearch-backward-regexp (&optional not-regexp no-recursive-edit)
8e1cae6d
JB
496 "\
497Do incremental search backward for regular expression.
08200510 498With a prefix argument, do a regular string search instead.
8e1cae6d
JB
499Like ordinary incremental search except that your input
500is treated as a regexp. See \\[isearch-forward] for more info."
eceee2c0 501 (interactive "P\np")
4ae7d00a 502 (isearch-mode nil (null not-regexp) nil (not no-recursive-edit)))
08200510
RS
503
504
505(defun isearch-mode-help ()
506 (interactive)
507 (describe-function 'isearch-forward)
508 (isearch-update))
8e1cae6d
JB
509
510\f
511;;;==================================================================
512;; isearch-mode only sets up incremental search for the minor mode.
513;; All the work is done by the isearch-mode commands.
514
08200510
RS
515;; Not used yet:
516;;(defconst isearch-commands '(isearch-forward isearch-backward
517;; isearch-forward-regexp isearch-backward-regexp)
518;; "List of commands for which isearch-mode does not recursive-edit.")
519
520
521(defun isearch-mode (forward &optional regexp op-fun recursive-edit word-p)
8e1cae6d 522 "Start isearch minor mode. Called by isearch-forward, etc."
8e1cae6d
JB
523
524 ;; Initialize global vars.
525 (setq isearch-forward forward
526 isearch-regexp regexp
08200510 527 isearch-word word-p
8e1cae6d
JB
528 isearch-op-fun op-fun
529 isearch-case-fold-search case-fold-search
530 isearch-string ""
531 isearch-message ""
532 isearch-cmds nil
533 isearch-success t
534 isearch-wrapped nil
535 isearch-barrier (point)
536 isearch-adjusted nil
537 isearch-yank-flag nil
538 isearch-invalid-regexp nil
dd492f04
JA
539 ;; Use (baud-rate) for now, for sake of other versions.
540 isearch-slow-terminal-mode (and (<= (baud-rate) search-slow-speed)
8e1cae6d
JB
541 (> (window-height)
542 (* 4 search-slow-window-lines)))
543 isearch-other-end nil
544 isearch-small-window nil
8e1cae6d 545
8e1cae6d
JB
546 isearch-opoint (point)
547 isearch-window-configuration (current-window-configuration)
a5dcf63f
RS
548 isearch-old-local-map (current-local-map)
549 search-ring-yank-pointer nil
550 regexp-search-ring-yank-pointer nil)
08200510
RS
551 (if isearch-pre-command-hook-exists
552 (add-hook 'pre-command-hook 'isearch-pre-command-hook))
8e1cae6d
JB
553 (setq isearch-mode " Isearch") ;; forward? regexp?
554 (set-buffer-modified-p (buffer-modified-p)) ; update modeline
555
556 (isearch-push-state)
557
558 (use-local-map isearch-mode-map)
559 (isearch-update)
560 (run-hooks 'isearch-mode-hook)
08200510
RS
561
562 ;; isearch-mode can be made modal (in the sense of not returning to
563 ;; the calling function until searching is completed) by entering
564 ;; a recursive-edit and exiting it when done isearching.
565 (if recursive-edit (recursive-edit))
8e1cae6d
JB
566 )
567
568
569;;;====================================================
570;; Some high level utilities. Others below.
571
572(defun isearch-update ()
573 ;; Called after each command to update the display.
a5dcf63f 574 (if (if isearch-event-data-type
08200510 575 (null unread-command-event)
1a3a6707 576 (if isearch-gnu-emacs-events
dbc4e1c1 577 (null unread-command-events)
08200510 578 (< unread-command-char 0)))
8e1cae6d
JB
579 (progn
580 (if (not (input-pending-p))
581 (isearch-message))
582 (if (and isearch-slow-terminal-mode
583 (not (or isearch-small-window
584 (pos-visible-in-window-p))))
08200510 585 (let ((found-point (point)))
8e1cae6d 586 (setq isearch-small-window t)
8e1cae6d
JB
587 (move-to-window-line 0)
588 (let ((window-min-height 1))
589 (split-window nil (if (< search-slow-window-lines 0)
590 (1+ (- search-slow-window-lines))
591 (- (window-height)
592 (1+ search-slow-window-lines)))))
593 (if (< search-slow-window-lines 0)
594 (progn (vertical-motion (- 1 search-slow-window-lines))
595 (set-window-start (next-window) (point))
596 (set-window-hscroll (next-window)
597 (window-hscroll))
598 (set-window-hscroll (selected-window) 0))
599 (other-window 1))
08200510
RS
600 (goto-char found-point)))
601 (if isearch-other-end
602 (if (< isearch-other-end (point)) ; isearch-forward?
603 (isearch-highlight isearch-other-end (point))
604 (isearch-highlight (point) isearch-other-end)))
605 ))
8e1cae6d
JB
606 (setq ;; quit-flag nil not for isearch-mode
607 isearch-adjusted nil
608 isearch-yank-flag nil)
609 )
610
611
35a4d143 612(defun isearch-done (&optional nopush)
8e1cae6d 613 ;; Called by all commands that terminate isearch-mode.
35a4d143 614 ;; If NOPUSH is non-nil, we don't push the string on the search ring.
8e1cae6d 615 (use-local-map isearch-old-local-map)
08200510
RS
616 ;; (setq pre-command-hook isearch-old-pre-command-hook) ; for lemacs
617 (isearch-dehighlight t)
618 (let ((found-start (window-start (selected-window)))
619 (found-point (point)))
620 (set-window-configuration isearch-window-configuration)
621
622 ;; If there was movement, mark the starting position.
623 ;; Maybe should test difference between and set mark iff > threshold.
624 (if (/= (point) isearch-opoint)
625 (push-mark isearch-opoint)
626 ;; (message "") why is this needed?
627 )
628 (if isearch-small-window
629 (goto-char found-point)
630 ;; Exiting the save-window-excursion clobbers window-start; restore it.
631 (set-window-start (selected-window) found-start t)))
632
633 (setq isearch-mode nil)
634 (set-buffer-modified-p (buffer-modified-p)) ;; update modeline
8e1cae6d 635
35a4d143 636 (if (and (> (length isearch-string) 0) (not nopush))
8e1cae6d
JB
637 ;; Update the ring data.
638 (if isearch-regexp
a8f783b2
RS
639 (if (or (null regexp-search-ring)
640 (not (string= isearch-string (car regexp-search-ring))))
8e1cae6d 641 (progn
08200510 642 (setq regexp-search-ring
a5dcf63f 643 (cons isearch-string regexp-search-ring))
08200510
RS
644 (if (> (length regexp-search-ring) regexp-search-ring-max)
645 (setcdr (nthcdr (1- search-ring-max) regexp-search-ring)
8e1cae6d 646 nil))))
a8f783b2
RS
647 (if (or (null search-ring)
648 (not (string= isearch-string (car search-ring))))
8e1cae6d 649 (progn
a5dcf63f 650 (setq search-ring (cons isearch-string search-ring))
8e1cae6d
JB
651 (if (> (length search-ring) search-ring-max)
652 (setcdr (nthcdr (1- search-ring-max) search-ring) nil))))))
653
8e1cae6d 654 (run-hooks 'isearch-mode-end-hook)
08200510
RS
655 (if isearch-recursive-edit (exit-recursive-edit)))
656
657;;;=======================================================
658;;; Switching buffers should first terminate isearch-mode.
659;;; This is done quite differently for each varient of emacs.
660;;; For lemacs, see Exiting in lemacs below
661
662;; For Emacs 19, the frame switch event is handled.
663(defun isearch-switch-frame-handler ()
664 (interactive) ;; Is this necessary?
665 ;; First terminate isearch-mode.
666 (isearch-done)
667 (select-frame (car (cdr (isearch-last-command-char)))))
668
669;;;========================================================
8e1cae6d
JB
670
671\f
672;;;====================================================
673;; Commands active while inside of the isearch minor mode.
674
675(defun isearch-exit ()
676 "Exit search normally.
677However, if this is the first command after starting incremental
678search and `search-nonincremental-instead' is non-nil, do a
08200510 679nonincremental search instead via `isearch-edit-string'."
8e1cae6d
JB
680 (interactive)
681 (if (and search-nonincremental-instead
682 (= 0 (length isearch-string)))
08200510
RS
683 (let ((isearch-nonincremental t))
684 (isearch-edit-string)))
8e1cae6d
JB
685 (isearch-done))
686
687
688(defun isearch-edit-string ()
08200510
RS
689 "Edit the search string in the minibuffer.
690The following additional command keys are active while editing.
691\\<minibuffer-local-isearch-map>
692\\[exit-minibuffer] to resume incremental searching with the edited string.
693\\[isearch-nonincremental-exit-minibuffer] to do one nonincremental search.
694\\[isearch-forward-exit-minibuffer] to resume isearching forward.
695\\[isearch-backward-exit-minibuffer] to resume isearching backward.
696\\[isearch-ring-advance-edit] to replace the search string with the next item in the search ring.
697\\[isearch-ring-retreat-edit] to replace the search string with the previou item in the search ring.
698\\[isearch-complete-edit] to complete the search string using the search ring.
699
700If first char entered is \\[isearch-yank-word], then do word search instead."
701
702 ;; This code is very hairy for several reasons, explained in the code.
703 ;; Mainly, isearch-mode must be terminated while editing and then restarted.
704 ;; If there were a way to catch any change of buffer from the minibuffer,
705 ;; this could be simplified greatly.
706 ;; Editing doesnt back up the search point. Should it?
8e1cae6d 707 (interactive)
08200510 708 (condition-case err
a5dcf63f 709 (let (isearch-nonincremental ; should search nonincrementally?
08200510
RS
710
711 ;; Locally bind all isearch global variables to protect them
712 ;; from recursive isearching.
713 ;; isearch-string -message and -forward are not bound
714 ;; so they may be changed. Instead, save the values.
715 (isearch-new-string isearch-string)
716 (isearch-new-message isearch-message)
717 (isearch-new-forward isearch-forward)
718 (isearch-new-word isearch-word)
719
720 (isearch-regexp isearch-regexp)
721 (isearch-op-fun isearch-op-fun)
722 (isearch-cmds isearch-cmds)
723 (isearch-success isearch-success)
724 (isearch-wrapped isearch-wrapped)
725 (isearch-barrier isearch-barrier)
726 (isearch-adjusted isearch-adjusted)
727 (isearch-yank-flag isearch-yank-flag)
728 (isearch-invalid-regexp isearch-invalid-regexp)
729 (isearch-other-end isearch-other-end)
730 (isearch-opoint isearch-opoint)
731 (isearch-slow-terminal-mode isearch-slow-terminal-mode)
732 (isearch-small-window isearch-small-window)
733 (isearch-recursive-edit isearch-recursive-edit)
734 ;; Save current configuration so we can restore it here.
735 (isearch-window-configuration (current-window-configuration))
736 )
737
738 ;; Actually terminate isearching until editing is done.
739 ;; This is so that the user can do anything without failure,
740 ;; like switch buffers and start another isearch, and return.
741 (condition-case err
35a4d143 742 (isearch-done t)
08200510
RS
743 (exit nil)) ; was recursive editing
744
745 (isearch-message) ;; for read-char
746 (unwind-protect
747 (let* (;; Why does following read-char echo?
748 ;;(echo-keystrokes 0) ;; not needed with above message
d94307ee
RS
749 (e (let ((cursor-in-echo-area t))
750 (if isearch-event-data-type
751 (allocate-event) (read-char))))
08200510
RS
752 ;; Binding minibuffer-history-symbol to nil is a work-around
753 ;; for some incompatibility with gmhist.
754 (minibuffer-history-symbol))
755 ;; If the first character the user types when we prompt them
756 ;; for a string is the yank-word character, then go into
757 ;; word-search mode. Otherwise unread that character and
758 ;; read a key the normal way.
759 ;; Word search does not apply (yet) to regexp searches,
760 ;; no check is made here.
761 (message (isearch-message-prefix nil nil t))
762 (if (eq 'isearch-yank-word
763 (lookup-key
764 isearch-mode-map
765 (char-to-string
a5dcf63f 766 (if isearch-event-data-type
08200510
RS
767 (or (event-to-character (next-command-event e)) 0)
768 e))))
769 (setq isearch-word t ;; so message-prefix is right
770 isearch-new-word t)
771 (isearch-unread e))
a5dcf63f
RS
772 (setq cursor-in-echo-area nil)
773 (setq isearch-new-string
35a4d143 774 (let (junk-ring)
a5dcf63f
RS
775 (read-from-minibuffer (isearch-message-prefix)
776 isearch-string
777 minibuffer-local-isearch-map nil
35a4d143 778 'junk-ring))
08200510
RS
779 isearch-new-message (mapconcat 'text-char-description
780 isearch-new-string "")))
781 ;; Always resume isearching by restarting it.
782 (isearch-mode isearch-forward
783 isearch-regexp
784 isearch-op-fun
785 isearch-recursive-edit
786 isearch-word)
787
788 ;; Copy new local values to isearch globals
789 (setq isearch-string isearch-new-string
790 isearch-message isearch-new-message
791 isearch-forward isearch-new-forward
792 isearch-word isearch-new-word))
793
794 ;; Empty isearch-string means use default.
795 (if (= 0 (length isearch-string))
a8f783b2
RS
796 (setq isearch-string (car (if isearch-regexp regexp-search-ring
797 search-ring)))
798 ;; This used to set the last search string,
799 ;; but I think it is not right to do that here.
800 ;; Only the string actually used should be saved.
801 )
08200510
RS
802
803 ;; Reinvoke the pending search.
804 (isearch-push-state)
805 (isearch-search)
806 (isearch-update)
807 (if isearch-nonincremental
808 (progn
809 ;; (sit-for 1) ;; needed if isearch-done does: (message "")
810 (isearch-done))))
8e1cae6d 811
08200510
RS
812 (quit ; handle abort-recursive-edit
813 (isearch-abort) ;; outside of let to restore outside global values
814 )))
815
816(defun isearch-nonincremental-exit-minibuffer ()
817 (interactive)
818 (setq isearch-nonincremental t)
819 (exit-minibuffer))
820
821(defun isearch-forward-exit-minibuffer ()
822 (interactive)
823 (setq isearch-new-forward t)
824 (exit-minibuffer))
8e1cae6d 825
08200510 826(defun isearch-reverse-exit-minibuffer ()
8e1cae6d 827 (interactive)
08200510
RS
828 (setq isearch-new-forward nil)
829 (exit-minibuffer))
830
831
832(defun isearch-abort ()
833 "Abort incremental search mode if searching is successful, signalling quit.
834Otherwise, revert to previous successful search and continue searching.
835Use `isearch-exit' to quit without signalling."
836 (interactive)
837;; (ding) signal instead below, if quiting
8e1cae6d
JB
838 (discard-input)
839 (if isearch-success
840 ;; If search is successful, move back to starting point
841 ;; and really do quit.
842 (progn (goto-char isearch-opoint)
35a4d143 843 (isearch-done t) ; exit isearch
a5dcf63f 844 (signal 'quit nil)) ; and pass on quit signal
08200510 845 ;; If search is failing, rub out until it is once more successful.
8e1cae6d
JB
846 (while (not isearch-success) (isearch-pop-state))
847 (isearch-update)))
848
849
850(defun isearch-repeat (direction)
851 ;; Utility for isearch-repeat-forward and -backward.
852 (if (eq isearch-forward (eq direction 'forward))
853 ;; C-s in forward or C-r in reverse.
854 (if (equal isearch-string "")
855 ;; If search string is empty, use last one.
856 (setq isearch-string
8e1cae6d 857 (or (if isearch-regexp
a5dcf63f
RS
858 (car regexp-search-ring)
859 (car search-ring))
8e1cae6d
JB
860 "")
861 isearch-message
08200510 862 (mapconcat 'isearch-text-char-description
8e1cae6d
JB
863 isearch-string ""))
864 ;; If already have what to search for, repeat it.
865 (or isearch-success
866 (progn
867
868 (goto-char (if isearch-forward (point-min) (point-max)))
869 (setq isearch-wrapped t))))
870 ;; C-s in reverse or C-r in forward, change direction.
871 (setq isearch-forward (not isearch-forward)))
872
873 (setq isearch-barrier (point)) ; For subsequent \| if regexp.
874 (setq isearch-success t)
875 (or (equal isearch-string "")
876 (progn
877 ;; If repeating a search that found
878 ;; an empty string, ensure we advance.
879 (if (equal (match-end 0) (match-beginning 0))
880 (forward-char (if isearch-forward 1 -1)))
881 (isearch-search)))
882 (isearch-push-state)
883 (isearch-update))
884
885(defun isearch-repeat-forward ()
886 "Repeat incremental search forwards."
887 (interactive)
888 (isearch-repeat 'forward))
889
890(defun isearch-repeat-backward ()
891 "Repeat incremental search backwards."
892 (interactive)
893 (isearch-repeat 'backward))
894
895(defun isearch-toggle-regexp ()
896 "Toggle regexp searching on or off."
897 ;; The status stack is left unchanged.
898 (interactive)
899 (setq isearch-regexp (not isearch-regexp))
08200510 900 (if isearch-regexp (setq isearch-word nil))
8e1cae6d
JB
901 (isearch-update))
902
903(defun isearch-delete-char ()
904 "Discard last input item and move point back.
905If no previous match was done, just beep."
906 (interactive)
907 (if (null (cdr isearch-cmds))
908 (ding)
909 (isearch-pop-state))
910 (isearch-update))
911
912
913(defun isearch-yank (chunk)
914 ;; Helper for isearch-yank-word and isearch-yank-line
08200510
RS
915 (let ((string (save-excursion
916 (and (not isearch-forward) isearch-other-end
917 (goto-char isearch-other-end))
918 (buffer-substring
919 (point)
920 (save-excursion
921 (cond
922 ((eq chunk 'word)
923 (forward-word 1))
924 ((eq chunk 'line)
925 (end-of-line)))
926 (point))))))
927 ;; Downcase the string if not supposed to case-fold yanked strings.
928 (if (and isearch-case-fold-search
fdf5afa4 929 (eq 'not-yanks search-upper-case))
08200510
RS
930 (setq string (downcase string)))
931 (if isearch-regexp (setq string (regexp-quote string)))
932 (setq isearch-string (concat isearch-string string)
8e1cae6d
JB
933 isearch-message
934 (concat isearch-message
08200510
RS
935 (mapconcat 'isearch-text-char-description
936 string ""))
8e1cae6d
JB
937 ;; Don't move cursor in reverse search.
938 isearch-yank-flag t))
939 (isearch-search-and-update))
940
941
942(defun isearch-yank-word ()
943 "Pull next word from buffer into search string."
944 (interactive)
945 (isearch-yank 'word))
946
947(defun isearch-yank-line ()
948 "Pull rest of line from buffer into search string."
949 (interactive)
950 (isearch-yank 'line))
951
952
953(defun isearch-search-and-update ()
954 ;; Do the search and update the display.
955 (if (and (not isearch-success)
956 ;; unsuccessful regexp search may become
957 ;; successful by addition of characters which
958 ;; make isearch-string valid
959 (not isearch-regexp))
960 nil
961 ;; In reverse search, adding stuff at
962 ;; the end may cause zero or many more chars to be
963 ;; matched, in the string following point.
964 ;; Allow all those possibilities without moving point as
965 ;; long as the match does not extend past search origin.
966 (if (and (not isearch-forward) (not isearch-adjusted)
967 (condition-case ()
968 (looking-at (if isearch-regexp isearch-string
969 (regexp-quote isearch-string)))
970 (error nil))
971 (or isearch-yank-flag
972 (<= (match-end 0)
973 (min isearch-opoint isearch-barrier))))
974 (setq isearch-success t
975 isearch-invalid-regexp nil
976 isearch-other-end (match-end 0))
977 ;; Not regexp, not reverse, or no match at point.
978 (if (and isearch-other-end (not isearch-adjusted))
979 (goto-char (if isearch-forward isearch-other-end
980 (min isearch-opoint
981 isearch-barrier
982 (1+ isearch-other-end)))))
983 (isearch-search)
984 ))
985 (isearch-push-state)
986 (if isearch-op-fun (funcall isearch-op-fun))
987 (isearch-update))
988
989
990;; *, ?, and | chars can make a regexp more liberal.
08200510 991;; They can make a regexp match sooner or make it succeed instead of failing.
8e1cae6d
JB
992;; So go back to place last successful search started
993;; or to the last ^S/^R (barrier), whichever is nearer.
08200510 994;; + needs no special handling because the string must match at least once.
8e1cae6d
JB
995
996(defun isearch-*-char ()
997 "Handle * and ? specially in regexps."
998 (interactive)
999 (if isearch-regexp
1000
1001 (progn
1002 (setq isearch-adjusted t)
1003 (let ((cs (nth (if isearch-forward
1004 5 ; isearch-other-end
1005 2) ; saved (point)
1006 (car (cdr isearch-cmds)))))
1007 ;; (car isearch-cmds) is after last search;
1008 ;; (car (cdr isearch-cmds)) is from before it.
1009 (setq cs (or cs isearch-barrier))
1010 (goto-char
1011 (if isearch-forward
1012 (max cs isearch-barrier)
1013 (min cs isearch-barrier))))))
08200510 1014 (isearch-process-search-char (isearch-last-command-char)))
8e1cae6d
JB
1015
1016
8e1cae6d
JB
1017(defun isearch-|-char ()
1018 "If in regexp search, jump to the barrier."
1019 (interactive)
1020 (if isearch-regexp
1021 (progn
1022 (setq isearch-adjusted t)
1023 (goto-char isearch-barrier)))
08200510 1024 (isearch-process-search-char (isearch-last-command-char)))
8e1cae6d
JB
1025
1026
1027(defun isearch-other-control-char ()
1028 "Any other control char => unread it and exit the search normally.
08200510
RS
1029But only if `search-exit-option' is non-nil, the default.
1030If it is the symbol `edit', the search string is edited in the minibuffer
35a4d143 1031and the control char is unread so that it applies to editing the string."
8e1cae6d 1032 (interactive)
35a4d143
RS
1033 (cond ((eq search-exit-option 'edit)
1034 (isearch-unread (isearch-last-command-char))
1035 (isearch-edit-string))
1036 (search-exit-option;; any other non-nil value
1037 (isearch-unread (isearch-last-command-char))
1038 (isearch-done))
1039 (t;; search-exit-option is nil
1040 (isearch-process-search-char (isearch-last-command-char)))))
8e1cae6d
JB
1041
1042
1043(defun isearch-other-meta-char ()
35a4d143
RS
1044 "Any other meta char => exit the search normally and reread the character.
1045But only if `search-exit-option' is non-nil, the default.
1046If it is the symbol `edit', the search string is edited in the minibuffer
1047and the meta character is unread so that it applies to editing the string."
8e1cae6d 1048 (interactive)
35a4d143
RS
1049 (cond ((eq search-exit-option 'edit)
1050 (let ((key (this-command-keys)))
1051 (isearch-unread (+ 128 (aref key (1- (length key))))))
1052 (isearch-edit-string))
1053 (search-exit-option
1054 (let ((key (this-command-keys)))
1055 (isearch-unread (+ 128 (aref key (1- (length key))))))
1056 (isearch-done))
1057 (t;; otherwise nil
1058 (isearch-process-search-string (this-command-keys)
1059 (this-command-keys)))))
8e1cae6d
JB
1060
1061
1062(defun isearch-quote-char ()
1063 "Quote special characters for incremental search."
1064 (interactive)
1065 (isearch-process-search-char (read-quoted-char (isearch-message t))))
1066
8e1cae6d
JB
1067(defun isearch-return-char ()
1068 "Convert return into newline for incremental search.
1069Obsolete."
1070 (interactive)
1071 (isearch-process-search-char ?\n))
1072
8e1cae6d
JB
1073(defun isearch-printing-char ()
1074 "Any other printing character => add it to the search string and search."
1075 (interactive)
08200510 1076 (isearch-process-search-char (isearch-last-command-char)))
8e1cae6d
JB
1077
1078(defun isearch-whitespace-chars ()
08200510
RS
1079 "Match all whitespace chars, if in regexp mode.
1080If not in regexp mode, activate word search."
8e1cae6d 1081 (interactive)
08200510
RS
1082 (if isearch-regexp
1083 (if search-whitespace-regexp
1084 (isearch-process-search-string search-whitespace-regexp " ")
1085 (isearch-printing-char))
1086 (progn
1087 ;; This way of doing word search doesnt correctly extend current search.
1088 ;; (setq isearch-word t)
1089 ;; (setq isearch-adjusted t)
1090 ;; (goto-char isearch-barrier)
1091 (isearch-printing-char))))
8e1cae6d
JB
1092
1093(defun isearch-process-search-char (char)
1094 ;; Append the char to the search string, update the message and re-search.
08200510
RS
1095 (isearch-process-search-string
1096 (isearch-char-to-string char)
1097 (isearch-text-char-description char)))
8e1cae6d
JB
1098
1099(defun isearch-process-search-string (string message)
1100 (setq isearch-string (concat isearch-string string)
1101 isearch-message (concat isearch-message message))
1102 (isearch-search-and-update))
1103
1104\f
1105;;===========================================================
1106;; Search Ring
1107
08200510
RS
1108(defun isearch-ring-adjust1 (advance)
1109 ;; Helper for isearch-ring-adjust
1110 (let* ((ring (if isearch-regexp regexp-search-ring search-ring))
8e1cae6d
JB
1111 (length (length ring))
1112 (yank-pointer-name (if isearch-regexp
08200510 1113 'regexp-search-ring-yank-pointer
8e1cae6d
JB
1114 'search-ring-yank-pointer))
1115 (yank-pointer (eval yank-pointer-name)))
1116 (if (zerop length)
1117 ()
1118 (set yank-pointer-name
1119 (setq yank-pointer
a5dcf63f
RS
1120 (% (+ (or yank-pointer 0)
1121 (if advance (1- length) 1))
1122 length)))
1123 (setq isearch-string (nth yank-pointer ring)
08200510
RS
1124 isearch-message (mapconcat 'isearch-text-char-description
1125 isearch-string "")))))
1126
1127(defun isearch-ring-adjust (advance)
1128 ;; Helper for isearch-ring-advance and isearch-ring-retreat
1129 (if (cdr isearch-cmds) ;; is there more than one thing on stack?
1130 (isearch-pop-state))
1131 (isearch-ring-adjust1 advance)
8e1cae6d 1132 (isearch-push-state)
08200510
RS
1133 (if search-ring-update
1134 (progn
1135 (isearch-search)
1136 (isearch-update))
1137 (isearch-edit-string)
1138 ))
8e1cae6d
JB
1139
1140(defun isearch-ring-advance ()
1141 "Advance to the next search string in the ring."
08200510 1142 ;; This could be more general to handle a prefix arg, but who would use it.
8e1cae6d
JB
1143 (interactive)
1144 (isearch-ring-adjust 'advance))
1145
1146(defun isearch-ring-retreat ()
1147 "Retreat to the previous search string in the ring."
1148 (interactive)
1149 (isearch-ring-adjust nil))
1150
a5dcf63f
RS
1151(defun isearch-ring-advance-edit (n)
1152 "Insert the next element of the search history into the minibuffer."
1153 (interactive "p")
1154 (let* ((yank-pointer-name (if isearch-regexp
1155 'regexp-search-ring-yank-pointer
1156 'search-ring-yank-pointer))
1157 (yank-pointer (eval yank-pointer-name))
1158 (ring (if isearch-regexp regexp-search-ring search-ring))
1159 (length (length ring)))
1160 (if (zerop length)
1161 ()
1162 (set yank-pointer-name
1163 (setq yank-pointer
1164 (% (+ (or yank-pointer 0)
35a4d143
RS
1165 ;; Add LENGTH here to ensure a positive result.
1166 length
1167 (% (- n) length))
a5dcf63f
RS
1168 length)))
1169
a5dcf63f 1170 (erase-buffer)
35a4d143 1171 (insert (nth yank-pointer ring))
49c13105 1172 (goto-char (point-max)))))
a5dcf63f
RS
1173
1174(defun isearch-ring-retreat-edit (n)
1175 "Inserts the previous element of the search history into the minibuffer."
1176 (interactive "p")
1177 (isearch-ring-advance-edit (- n)))
1178
1179;;(defun isearch-ring-adjust-edit (advance)
1180;; "Use the next or previous search string in the ring while in minibuffer."
1181;; (isearch-ring-adjust1 advance)
1182;; (erase-buffer)
1183;; (insert isearch-string))
1184
1185;;(defun isearch-ring-advance-edit ()
1186;; (interactive)
1187;; (isearch-ring-adjust-edit 'advance))
1188
1189;;(defun isearch-ring-retreat-edit ()
1190;; "Retreat to the previous search string in the ring while in the minibuffer."
1191;; (interactive)
1192;; (isearch-ring-adjust-edit nil))
08200510
RS
1193
1194
1195(defun isearch-complete1 ()
1196 ;; Helper for isearch-complete and isearch-complete-edit
1197 ;; Return t if completion OK, nil if no completion exists.
1198 (let* ((ring (if isearch-regexp regexp-search-ring search-ring))
1199 (alist (mapcar (function (lambda (string) (list string))) ring))
1200 (completion-ignore-case case-fold-search)
1201 (completion (try-completion isearch-string alist)))
1202 (cond
1203 ((eq completion t)
1204 ;; isearch-string stays the same
1205 t)
1206 ((or completion ; not nil, must be a string
1207 (= 0 (length isearch-string))) ; shouldnt have to say this
1208 (if (equal completion isearch-string) ;; no extension?
1209 (if completion-auto-help
1210 (with-output-to-temp-buffer "*Isearch completions*"
1211 (display-completion-list
1212 (all-completions isearch-string alist))))
1213 (setq isearch-string completion))
1214 t)
1215 (t
1216 (message "No completion") ; waits a second if in minibuffer
1217 nil))))
1218
1219(defun isearch-complete ()
1220 "Complete the search string from the strings on the search ring.
1221The completed string is then editable in the minibuffer.
1222If there is no completion possible, say so and continue searching."
1223 (interactive)
1224 (if (isearch-complete1)
1225 (isearch-edit-string)
1226 ;; else
1227 (sit-for 1)
1228 (isearch-update)))
8e1cae6d 1229
08200510
RS
1230(defun isearch-complete-edit ()
1231 "Same as `isearch-complete' except in the minibuffer."
1232 (interactive)
1233 (setq isearch-string (buffer-string))
1234 (if (isearch-complete1)
8e1cae6d 1235 (progn
08200510
RS
1236 (erase-buffer)
1237 (insert isearch-string))))
8e1cae6d
JB
1238
1239\f
1240;;;==============================================================
1241;; The search status stack (and isearch window-local variables, not used).
08200510 1242;; Need a structure for this.
8e1cae6d
JB
1243
1244(defun isearch-top-state ()
8e1cae6d
JB
1245 (let ((cmd (car isearch-cmds)))
1246 (setq isearch-string (car cmd)
1247 isearch-message (car (cdr cmd))
1248 isearch-success (nth 3 cmd)
1249 isearch-forward (nth 4 cmd)
1250 isearch-other-end (nth 5 cmd)
08200510
RS
1251 isearch-word (nth 6 cmd)
1252 isearch-invalid-regexp (nth 7 cmd)
1253 isearch-wrapped (nth 8 cmd)
1254 isearch-barrier (nth 9 cmd))
8e1cae6d
JB
1255 (goto-char (car (cdr (cdr cmd))))))
1256
1257(defun isearch-pop-state ()
8e1cae6d
JB
1258 (setq isearch-cmds (cdr isearch-cmds))
1259 (isearch-top-state)
1260 )
1261
1262(defun isearch-push-state ()
1263 (setq isearch-cmds
1264 (cons (list isearch-string isearch-message (point)
1265 isearch-success isearch-forward isearch-other-end
08200510 1266 isearch-word
8e1cae6d
JB
1267 isearch-invalid-regexp isearch-wrapped isearch-barrier)
1268 isearch-cmds)))
1269
8e1cae6d
JB
1270\f
1271;;;==================================================================
1272;; Message string
1273
1274(defun isearch-message (&optional c-q-hack ellipsis)
1275 ;; Generate and print the message string.
1276 (let ((cursor-in-echo-area ellipsis)
1277 (m (concat
08200510 1278 (isearch-message-prefix c-q-hack ellipsis isearch-nonincremental)
8e1cae6d
JB
1279 isearch-message
1280 (isearch-message-suffix c-q-hack ellipsis)
1281 )))
1282 (if c-q-hack m (message "%s" m))))
1283
08200510 1284(defun isearch-message-prefix (&optional c-q-hack ellipsis nonincremental)
8e1cae6d
JB
1285 ;; If about to search, and previous search regexp was invalid,
1286 ;; check that it still is. If it is valid now,
1287 ;; let the message we display while searching say that it is valid.
1288 (and isearch-invalid-regexp ellipsis
1289 (condition-case ()
1290 (progn (re-search-forward isearch-string (point) t)
1291 (setq isearch-invalid-regexp nil))
1292 (error nil)))
1293 ;; If currently failing, display no ellipsis.
1294 (or isearch-success (setq ellipsis nil))
1295 (let ((m (concat (if isearch-success "" "failing ")
1296 (if isearch-wrapped "wrapped ")
08200510 1297 (if isearch-word "word " "")
8e1cae6d 1298 (if isearch-regexp "regexp " "")
08200510 1299 (if nonincremental "search" "I-search")
8e1cae6d
JB
1300 (if isearch-forward ": " " backward: ")
1301 )))
1302 (aset m 0 (upcase (aref m 0)))
1303 m))
1304
1305
1306(defun isearch-message-suffix (&optional c-q-hack ellipsis)
1307 (concat (if c-q-hack "^Q" "")
1308 (if isearch-invalid-regexp
1309 (concat " [" isearch-invalid-regexp "]")
1310 "")))
1311
1312\f
1313;;;========================================================
1314;;; Searching
1315
1316(defun isearch-search ()
1317 ;; Do the search with the current search string.
1318 (isearch-message nil t)
fdf5afa4 1319 (if search-upper-case
08200510 1320 (setq isearch-case-fold-search (isearch-no-upper-case-p isearch-string)))
8e1cae6d
JB
1321 (condition-case lossage
1322 (let ((inhibit-quit nil)
1323 (case-fold-search isearch-case-fold-search))
1324 (if isearch-regexp (setq isearch-invalid-regexp nil))
1325 (setq isearch-success
1326 (funcall
08200510
RS
1327 (cond (isearch-word
1328 (if isearch-forward
1329 'word-search-forward 'word-search-backward))
1330 (isearch-regexp
1331 (if isearch-forward
1332 're-search-forward 're-search-backward))
1333 (t
1334 (if isearch-forward 'search-forward 'search-backward)))
8e1cae6d
JB
1335 isearch-string nil t))
1336 (if isearch-success
1337 (setq isearch-other-end
1338 (if isearch-forward (match-beginning 0) (match-end 0)))))
1339
08200510 1340 (quit (isearch-unread ?\C-g)
8e1cae6d
JB
1341 (setq isearch-success nil))
1342
1343 (invalid-regexp
1344 (setq isearch-invalid-regexp (car (cdr lossage)))
1345 (if (string-match
1346 "\\`Premature \\|\\`Unmatched \\|\\`Invalid "
1347 isearch-invalid-regexp)
f1c03125
RS
1348 (setq isearch-invalid-regexp "incomplete input")))
1349 (error
1350 ;; stack overflow in regexp search.
1351 (setq isearch-invalid-regexp (car (cdr lossage)))))
8e1cae6d
JB
1352
1353 (if isearch-success
1354 nil
1355 ;; Ding if failed this time after succeeding last time.
1356 (and (nth 3 (car isearch-cmds))
1357 (ding))
1358 (goto-char (nth 2 (car isearch-cmds)))))
1359
08200510
RS
1360
1361\f
1362;;;========================================================
1363;;; Highlighting
1364
1365(defun isearch-highlight (begin end))
1366(defun isearch-dehighlight (totally))
1367
1368;; lemacs uses faces
1369'(progn
1370(defvar isearch-extent nil)
1371
1372(or (find-face 'isearch) ;; this face is initialized by x-faces.el
1373 (make-face 'isearch)) ;; since isearch is preloaded
1374
1375(defun isearch-lemacs-highlight (begin end)
1376 (if (null isearch-highlight)
1377 nil
1378 (if (and (extentp isearch-extent)
1379 (eq (extent-buffer isearch-extent) (current-buffer)))
1380 (set-extent-endpoints isearch-extent begin end)
1381 (if (and (extentp isearch-extent)
1382 (bufferp (extent-buffer isearch-extent))
1383 (buffer-name (extent-buffer isearch-extent)))
1384 (delete-extent isearch-extent))
1385 (setq isearch-extent (make-extent begin end (current-buffer))))
1386 (set-extent-face isearch-extent 'isearch)))
1387
1388(defun isearch-lemacs-dehighlight (totally)
1389 (if (and isearch-highlight isearch-extent)
1390 (if totally
1391 (let ((inhibit-quit t))
1392 (if (and (extentp isearch-extent)
1393 (bufferp (extent-buffer isearch-extent))
1394 (buffer-name (extent-buffer isearch-extent)))
1395 (delete-extent isearch-extent))
1396 (setq isearch-extent nil))
1397 (if (and (extentp isearch-extent)
1398 (bufferp (extent-buffer isearch-extent))
1399 (buffer-name (extent-buffer isearch-extent)))
1400 (set-extent-face isearch-extent 'default)
1401 (isearch-dehighlight t)))))
1402
1403(fset 'isearch-highlight (symbol-function 'isearch-lemacs-highlight))
1404(fset 'isearch-dehighlight (symbol-function 'isearch-lemacs-dehighlight))
1405)
1406
1407;;;===========================================================
1408;;; General utilities
1409
1410;; (fset 'isearch-member-equal (symbol-function 'member)) ; for emacs 19
1411
1412(defun isearch-member-equal (item list)
1413 "Return non-nil if ITEM is `equal' to some item in LIST.
1414Actually return the list whose car is that item."
1415 (while (and list (not (equal item (car list))))
1416 (setq list (cdr list)))
1417 list)
1418
1419
1420(defun isearch-no-upper-case-p (string)
1421 "Return t if there are no upper case chars in string.
1422But upper case chars preceeded by \\ (but not \\\\) do not count since they
1423have special meaning in a regexp."
1424 (let ((case-fold-search nil))
1425 (not (string-match "\\(^\\|\\\\\\\\\\|[^\\]\\)[A-Z]" string))))
1426
1427
8e1cae6d 1428;;;=================================================
08200510 1429;;; Special functions for lemacs events.
8e1cae6d 1430
08200510
RS
1431;; To quiet the byte-compiler.
1432(defvar unread-command-event)
dbc4e1c1 1433(defvar unread-command-events)
08200510 1434(defvar last-command-event)
8e1cae6d 1435
08200510
RS
1436(defun isearch-char-to-string (c)
1437 (if (integerp c)
1438 (make-string 1 c)
1439 (make-string 1 (event-to-character c))))
1440
1441(defun isearch-text-char-description (c)
1442 (isearch-char-to-string c))
1443
1444(defun isearch-unread (char-or-event)
1445 ;; General function to unread a character or event.
797a37c7
JB
1446 (cond
1447 (isearch-event-data-type
1448 (setq unread-command-event char-or-event))
1449 (isearch-gnu-emacs-events
dbc4e1c1 1450 (setq unread-command-events (list char-or-event)))
797a37c7
JB
1451 (t
1452 (setq unread-command-char char-or-event))))
08200510
RS
1453
1454(defun isearch-last-command-char ()
1455 ;; General function to return the last command character.
a5dcf63f 1456 (if isearch-event-data-type
08200510
RS
1457 last-command-event
1458 last-command-char))
1459
1460
1461
1462\f
1463;;;========================================================
1464;;; Exiting in lemacs
1465
1466;; This is a large amount of code to support automatic termination of
1467;; isearch-mode when a command (however it is invoked) is not an
1468;; isearch command, or the buffer is switched out from under
1469;; isearch-mode. Only later versions of lemacs have the pre-command-hook.
1470
1471;;(if isearch-pre-command-hook-exists
1472;;(progn
1473
1474;;;; This list must be modified whenever the available commands are modified.
1475;;(mapcar (function (lambda (command)
1476;; (put command 'isearch-command t)))
1477;; '(isearch-printing-char
1478;; isearch-return-char
1479;; isearch-repeat-forward
1480;; isearch-repeat-backward
1481;; isearch-delete-char
1482;; isearch-abort
1483;; isearch-quote-char
1484;; isearch-exit
1485;; isearch-printing-char
1486;; isearch-printing-char
1487;; isearch-yank-word
1488;; isearch-yank-line
1489;; isearch-*-char
1490;; isearch-*-char
1491;; isearch-|-char
1492;; isearch-toggle-regexp
1493;; isearch-edit-string
1494;; isearch-mode-help
1495;; isearch-ring-advance
1496;; isearch-ring-retreat
1497;; isearch-ring-advance-edit
1498;; isearch-ring-retreat-edit
1499;; isearch-whitespace-chars
1500;; isearch-complete
1501;; isearch-complete-edit
1502;; isearch-edit-string
1503;; isearch-toggle-regexp
1504;; ;; The following may not be needed since isearch-mode is off already.
1505;; isearch-forward-exit-minibuffer
1506;; isearch-reverse-exit-minibuffer
1507;; isearch-nonincremental-exit-minibuffer))
1508
1509;;(defun isearch-pre-command-hook ()
1510;; ;;
1511;; ;; For use as the value of `pre-command-hook' when isearch-mode is active.
1512;; ;; If the command about to be executed is not one of the isearch commands,
1513;; ;; then isearch-mode is turned off before that command is executed.
1514;; ;;
1515;; ;; If the command about to be executed is self-insert-command, or is a
1516;; ;; keyboard macro of a single key sequence which is bound to self-insert-
1517;; ;; command, then we add those chars to the search ring instead of inserting
1518;; ;; them in the buffer. In this way, the set of self-searching characters
1519;; ;; need not be exhaustively enumerated, but is derived from other maps.
1520;; ;;
1521;; (isearch-maybe-frob-keyboard-macros)
1522;; (if (and (symbolp this-command)
1523;; (get this-command 'isearch-command))
1524;; nil
1525;; (isearch-done)))
1526
1527;;(defun isearch-maybe-frob-keyboard-macros ()
1528;; ;;
1529;; ;; If the command about to be executed is `self-insert-command' then change
1530;; ;; the command to `isearch-printing-char' instead, meaning add the last-
1531;; ;; typed character to the search string.
1532;; ;;
1533;; ;; If `this-command' is a string or a vector (that is, a keyboard macro)
1534;; ;; and it contains only one command, which is bound to self-insert-command,
1535;; ;; then do the same thing as for self-inserting commands: arrange for that
1536;; ;; character to be added to the search string. If we didn't do this, then
1537;; ;; typing a compose sequence (a la x-compose.el) would terminate the search
1538;; ;; and insert the character, instead of searching for that character.
1539;; ;;
1540;; (cond ((eq this-command 'self-insert-command)
1541;; (setq this-command 'isearch-printing-char))
1542;; ((and (stringp this-command)
1543;; (eq (key-binding this-command) 'self-insert-command))
1544;; (setq last-command-char (aref this-command 0)
1545;; last-command-event (character-to-event last-command-char)
1546;; this-command 'isearch-printing-char))
1547;; ((and (vectorp this-command)
1548;; (eq (key-binding this-command) 'self-insert-command))
1549;; (let* ((desc (aref this-command 0))
1550;; (code (cond ((integerp desc) desc)
1551;; ((symbolp desc) (get desc character-set-property))
1552;; ((consp desc)
1553;; (and (null (cdr desc))
1554;; (get (car desc) character-set-property)))
1555;; (t nil))))
1556;; (if code
1557;; (setq last-command-char code
1558;; last-command-event (character-to-event last-command-char)
1559;; this-command 'isearch-printing-char))))
1560;; ))
1561
1562;;))
4ae7d00a 1563