|
|
1.1 root 1: ;; Incremental search
2: ;; Copyright (C) 1985, 1986 Free Software Foundation, Inc.
3:
4: ;; This file is part of GNU Emacs.
5:
6: ;; GNU Emacs is free software; you can redistribute it and/or modify
7: ;; it under the terms of the GNU General Public License as published by
8: ;; the Free Software Foundation; either version 1, or (at your option)
9: ;; any later version.
10:
11: ;; GNU Emacs is distributed in the hope that it will be useful,
12: ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
13: ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14: ;; GNU General Public License for more details.
15:
16: ;; You should have received a copy of the GNU General Public License
17: ;; along with GNU Emacs; see the file COPYING. If not, write to
18: ;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
19:
20: ; in loaddefs.el
21: ;(defvar search-last-string ""
22: ; "Last string search for by a search command.
23: ;This does not include direct calls to the primitive search functions,
24: ;and does not include searches that are aborted.")
25: ;(defvar search-last-regexp ""
26: ; "Last string searched for by a regexp search command.
27: ;This does not include direct calls to the primitive search functions,
28: ;and does not include searches that are aborted.")
29: ;
30: ;(defconst search-repeat-char ?\C-s
31: ; "Character to repeat incremental search forwards.")
32: ;(defconst search-reverse-char ?\C-r
33: ; "Character to repeat incremental search backwards.")
34: ;(defconst search-exit-char ?\e
35: ; "Character to exit incremental search.")
36: ;(defconst search-delete-char ?\177
37: ; "Character to delete from incremental search string.")
38: ;(defconst search-quote-char ?\C-q
39: ; "Character to quote special characters for incremental search.")
40: ;(defconst search-yank-word-char ?\C-w
41: ; "Character to pull next word from buffer into search string.")
42: ;(defconst search-yank-line-char ?\C-y
43: ; "Character to pull rest of line from buffer into search string.")
44: ;(defconst search-exit-option t
45: ; "Non-nil means random control characters terminate incremental search.")
46: ;
47: ;(defvar search-slow-window-lines 1
48: ; "*Number of lines in slow search display windows.")
49: ;(defconst search-slow-speed 1200
50: ; "*Highest terminal speed at which to use \"slow\" style incremental search.
51: ;This is the style where a one-line window is created to show the line
52: ;that the search has reached.")
53:
54: ;; This function does all the work of incremental search.
55: ;; The functions attached to ^R and ^S are trivial,
56: ;; merely calling this one, but they are always loaded by default
57: ;; whereas this file can optionally be autoloadable.
58: ;; This is the only entry point in this file.
59:
60: (defun isearch (forward &optional regexp)
61: (let ((search-string "")
62: (search-message "")
63: (cmds nil)
64: (success t)
65: (wrapped nil)
66: (barrier (point))
67: adjusted
68: (invalid-regexp nil)
69: (slow-terminal-mode (and (<= (baud-rate) search-slow-speed)
70: (> (window-height)
71: (* 4 search-slow-window-lines))))
72: (other-end nil) ;Start of last match if fwd, end if backwd.
73: (small-window nil) ;if t, using a small window
74: (found-point nil) ;to restore point from a small window
75: ;; This is the window-start value found by the search.
76: (found-start nil)
77: (opoint (point))
78: (inhibit-quit t)) ;Prevent ^G from quitting immediately.
79: (isearch-push-state)
80: (save-window-excursion
81: (catch 'search-done
82: (while t
83: (or (>= unread-command-char 0)
84: (progn
85: (or (input-pending-p)
86: (isearch-message))
87: (if (and slow-terminal-mode
88: (not (or small-window (pos-visible-in-window-p))))
89: (progn
90: (setq small-window t)
91: (setq found-point (point))
92: (move-to-window-line 0)
93: (let ((window-min-height 1))
94: (split-window nil (if (< search-slow-window-lines 0)
95: (1+ (- search-slow-window-lines))
96: (- (window-height)
97: (1+ search-slow-window-lines)))))
98: (if (< search-slow-window-lines 0)
99: (progn (vertical-motion (- 1 search-slow-window-lines))
100: (set-window-start (next-window) (point))
101: (set-window-hscroll (next-window)
102: (window-hscroll))
103: (set-window-hscroll (selected-window) 0))
104: (other-window 1))
105: (goto-char found-point)))))
106: (let ((char (if quit-flag
107: ?\C-g
108: (read-char))))
109: (setq quit-flag nil adjusted nil)
110: ;; Meta character means exit search.
111: (cond ((and (>= char 128)
112: search-exit-option)
113: (setq unread-command-char char)
114: (throw 'search-done t))
115: ((eq char search-exit-char)
116: ;; Esc means exit search normally.
117: ;; Except, if first thing typed, it means do nonincremental
118: (if (= 0 (length search-string))
119: (nonincremental-search forward regexp))
120: (throw 'search-done t))
121: ((= char ?\C-g)
122: ;; ^G means the user tried to quit.
123: (ding)
124: (discard-input)
125: (if success
126: ;; If search is successful, move back to starting point
127: ;; and really do quit.
128: (progn (goto-char opoint)
129: (signal 'quit nil))
130: ;; If search is failing, rub out until it is once more
131: ;; successful.
132: (while (not success) (isearch-pop))))
133: ((or (eq char search-repeat-char)
134: (eq char search-reverse-char))
135: (if (eq forward (eq char search-repeat-char))
136: ;; C-s in forward or C-r in reverse.
137: (if (equal search-string "")
138: ;; If search string is empty, use last one.
139: (setq search-string
140: (if regexp
141: search-last-regexp search-last-string)
142: search-message
143: (mapconcat 'text-char-description
144: search-string ""))
145: ;; If already have what to search for, repeat it.
146: (or success
147: (progn (goto-char (if forward (point-min) (point-max)))
148: (setq wrapped t))))
149: ;; C-s in reverse or C-r in forward, change direction.
150: (setq forward (not forward)))
151: (setq barrier (point)) ; For subsequent \| if regexp.
152: (setq success t)
153: (or (equal search-string "")
154: (isearch-search))
155: (isearch-push-state))
156: ((= char search-delete-char)
157: ;; Rubout means discard last input item and move point
158: ;; back. If buffer is empty, just beep.
159: (if (null (cdr cmds))
160: (ding)
161: (isearch-pop)))
162: (t
163: (cond ((or (eq char search-yank-word-char)
164: (eq char search-yank-line-char))
165: ;; ^W means gobble next word from buffer.
166: ;; ^Y means gobble rest of line from buffer.
167: (let ((word (save-excursion
168: (and (not forward) other-end
169: (goto-char other-end))
170: (buffer-substring
171: (point)
172: (save-excursion
173: (if (eq char search-yank-line-char)
174: (end-of-line)
175: (forward-word 1))
176: (point))))))
177: (if regexp
178: (setq word (regexp-quote word)))
179: (setq search-string (concat search-string word)
180: search-message
181: (concat search-message
182: (mapconcat 'text-char-description
183: word "")))))
184: ;; Any other control char =>
185: ;; unread it and exit the search normally.
186: ((and search-exit-option
187: (/= char search-quote-char)
188: (or (= char ?\177)
189: (and (< char ? ) (/= char ?\t) (/= char ?\r))))
190: (setq unread-command-char char)
191: (throw 'search-done t))
192: (t
193: ;; Any other character => add it to the
194: ;; search string and search.
195: (cond ((= char search-quote-char)
196: (setq char (read-quoted-char
197: (isearch-message t))))
198: ((= char ?\r)
199: ;; unix braindeath
200: (setq char ?\n)))
201: (setq search-string (concat search-string
202: (char-to-string char))
203: search-message (concat search-message
204: (text-char-description char)))))
205: (if (and (not success)
206: ;; unsuccessful regexp search may become
207: ;; successful by addition of characters which
208: ;; make search-string valid
209: (not regexp))
210: nil
211: ;; If a regexp search may have been made more
212: ;; liberal, retreat the search start.
213: ;; Go back to place last successful search started
214: ;; or to the last ^S/^R (barrier), whichever is nearer.
215: (and regexp success cmds
216: (cond ((and (memq char '(?* ??))
217: ;; Don't treat *, ? as special
218: ;; within [] or after \.
219: (not (nth 6 (car cmds))))
220: (setq adjusted t)
221: ;; This used to use element 2
222: ;; in a reverse search, but it seems that 5
223: ;; (which is the end of the old match)
224: ;; is better in that case too.
225: (let ((cs (nth 5 ; old other-end.
226: (car (cdr cmds)))))
227: ;; (car cmds) is after last search;
228: ;; (car (cdr cmds)) is from before it.
229: (setq cs (or cs barrier))
230: (goto-char
231: (if forward
232: (max cs barrier)
233: (min cs barrier)))))
234: ((eq char ?\|)
235: (setq adjusted t)
236: (goto-char barrier))))
237: ;; In reverse regexp search, adding a character at
238: ;; the end may cause zero or many more chars to be
239: ;; matched, in the string following point.
240: ;; Allow all those possibiities without moving point as
241: ;; long as the match does not extend past search origin.
242: (if (and regexp (not forward) (not adjusted)
243: (condition-case ()
244: (looking-at search-string)
245: (error nil))
246: (<= (match-end 0) (min opoint barrier)))
247: (setq success t invalid-regexp nil
248: other-end (match-end 0))
249: ;; Not regexp, not reverse, or no match at point.
250: (if (and other-end (not adjusted))
251: (goto-char (if forward other-end
252: (min opoint barrier (1+ other-end)))))
253: (isearch-search)))
254: (isearch-push-state))))))
255: (setq found-start (window-start (selected-window)))
256: (setq found-point (point)))
257: (if (> (length search-string) 0)
258: (if regexp
259: (setq search-last-regexp search-string)
260: (setq search-last-string search-string)))
261: ;; If we displayed a single-line window, set point in this window.
262: (if small-window
263: (goto-char found-point))
264: ;; If there was movement, mark the starting position.
265: ;; Maybe should test difference between and set mark iff > threshold.
266: (if (/= (point) opoint)
267: (push-mark opoint)
268: (message ""))
269: (or small-window
270: ;; Exiting the save-window-excursion clobbers this; restore it.
271: (set-window-start (selected-window) found-start t))))
272:
273: (defun isearch-message (&optional c-q-hack ellipsis)
274: ;; If about to search, and previous search regexp was invalid,
275: ;; check that it still is. If it is valid now,
276: ;; let the message we display while searching say that it is valid.
277: (and invalid-regexp ellipsis
278: (condition-case ()
279: (progn (re-search-forward search-string (point) t)
280: (setq invalid-regexp nil))
281: (error nil)))
282: ;; If currently failing, display no ellipsis.
283: (or success (setq ellipsis nil))
284: (let ((m (concat (if success "" "failing ")
285: (if wrapped "wrapped ")
286: (if regexp "regexp " "")
287: "I-search"
288: (if forward ": " " backward: ")
289: search-message
290: (if c-q-hack "^Q" "")
291: (if invalid-regexp
292: (concat " [" invalid-regexp "]")
293: ""))))
294: (aset m 0 (upcase (aref m 0)))
295: (let ((cursor-in-echo-area ellipsis))
296: (if c-q-hack m (message "%s" m)))))
297:
298: (defun isearch-pop ()
299: (setq cmds (cdr cmds))
300: (let ((cmd (car cmds)))
301: (setq search-string (car cmd)
302: search-message (car (cdr cmd))
303: success (nth 3 cmd)
304: forward (nth 4 cmd)
305: other-end (nth 5 cmd)
306: invalid-regexp (nth 6 cmd)
307: wrapped (nth 7 cmd)
308: barrier (nth 8 cmd))
309: (goto-char (car (cdr (cdr cmd))))))
310:
311: (defun isearch-push-state ()
312: (setq cmds (cons (list search-string search-message (point)
313: success forward other-end invalid-regexp
314: wrapped barrier)
315: cmds)))
316:
317: (defun isearch-search ()
318: (isearch-message nil t)
319: (condition-case lossage
320: (let ((inhibit-quit nil))
321: (if regexp (setq invalid-regexp nil))
322: (setq success
323: (funcall
324: (if regexp
325: (if forward 're-search-forward 're-search-backward)
326: (if forward 'search-forward 'search-backward))
327: search-string nil t))
328: (if success
329: (setq other-end
330: (if forward (match-beginning 0) (match-end 0)))))
331: (quit (setq unread-command-char ?\C-g)
332: (setq success nil))
333: (invalid-regexp (setq invalid-regexp (car (cdr lossage)))
334: (if (string-match "\\`Premature \\|\\`Unmatched \\|\\`Invalid "
335: invalid-regexp)
336: (setq invalid-regexp "incomplete input"))))
337: (if success
338: nil
339: ;; Ding if failed this time after succeeding last time.
340: (and (nth 3 (car cmds))
341: (ding))
342: (goto-char (nth 2 (car cmds)))))
343:
344: ;; This is called from incremental-search
345: ;; if the first input character is the exit character.
346: ;; The interactive-arg-reader uses free variables `forward' and `regexp'
347: ;; which are bound by `incremental-search'.
348:
349: ;; We store the search string in `search-string'
350: ;; which has been bound already by `incremental-search'
351: ;; so that, when we exit, it is copied into `search-last-string'.
352:
353: (defun nonincremental-search (forward regexp)
354: (let (message char function string inhibit-quit)
355: (let ((cursor-in-echo-area t))
356: ;; Prompt assuming not word search,
357: (setq message (if regexp
358: (if forward "Regexp search: "
359: "Regexp search backward: ")
360: (if forward "Search: " "Search backward: ")))
361: (message "%s" message)
362: ;; Read 1 char and switch to word search if it is ^W.
363: (setq char (read-char)))
364: (if (eq char search-yank-word-char)
365: (setq message (if forward "Word search: " "Word search backward: "))
366: ;; Otherwise let that 1 char be part of the search string.
367: (setq unread-command-char char))
368: (setq function
369: (if (eq char search-yank-word-char)
370: (if forward 'word-search-forward 'word-search-backward)
371: (if regexp
372: (if forward 're-search-forward 're-search-backward)
373: (if forward 'search-forward 'search-backward))))
374: ;; Read the search string with corrected prompt.
375: (setq string (read-string message))
376: (let ((var (if regexp 'search-last-regexp 'search-last-string)))
377: ;; Empty means use default.
378: (if (= 0 (length string))
379: (setq string (symbol-value var))
380: ;; Set last search string now so it is set even if we fail.
381: (set var string)))
382: ;; Since we used the minibuffer, we should be available for redo.
383: (setq command-history (cons (list function string) command-history))
384: ;; Go ahead and search.
385: (funcall function string)))
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.