| 1 | ;;; isearch.el --- incremental search commands |
| 2 | |
| 3 | ;; Copyright (C) 1985, 1986 Free Software Foundation, Inc. |
| 4 | |
| 5 | ;; Maintainer: FSF |
| 6 | |
| 7 | ;; This file is part of GNU Emacs. |
| 8 | |
| 9 | ;; GNU Emacs is free software; you can redistribute it and/or modify |
| 10 | ;; it under the terms of the GNU General Public License as published by |
| 11 | ;; the Free Software Foundation; either version 2, or (at your option) |
| 12 | ;; any later version. |
| 13 | |
| 14 | ;; GNU Emacs is distributed in the hope that it will be useful, |
| 15 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 16 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 17 | ;; GNU General Public License for more details. |
| 18 | |
| 19 | ;; You should have received a copy of the GNU General Public License |
| 20 | ;; along with GNU Emacs; see the file COPYING. If not, write to |
| 21 | ;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. |
| 22 | |
| 23 | ;;; Code: |
| 24 | |
| 25 | (defvar search-last-string "" "\ |
| 26 | Last string search for by a non-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 | (defvar search-last-regexp "" "\ |
| 31 | Last string searched for by a regexp search command. |
| 32 | This does not include direct calls to the primitive search functions, |
| 33 | and does not include searches that are aborted.") |
| 34 | |
| 35 | |
| 36 | (defconst search-repeat-char ?\C-s "\ |
| 37 | *Character to repeat incremental search forwards.") |
| 38 | (defconst search-reverse-char ?\C-r "\ |
| 39 | *Character to repeat incremental search backwards.") |
| 40 | (defconst search-exit-char ?\C-m "\ |
| 41 | *Character to exit incremental search.") |
| 42 | (defconst search-delete-char ?\177 "\ |
| 43 | *Character to delete from incremental search string.") |
| 44 | (defconst search-quote-char ?\C-q "\ |
| 45 | *Character to quote special characters for incremental search.") |
| 46 | (defconst search-yank-word-char ?\C-w "\ |
| 47 | *Character to pull next word from buffer into search string.") |
| 48 | (defconst search-yank-line-char ?\C-y "\ |
| 49 | *Character to pull rest of line from buffer into search string.") |
| 50 | (defconst search-ring-advance-char ?\M-n "\ |
| 51 | *Character to pull next (more recent) search string from the ring of same.") |
| 52 | (defconst search-ring-retreat-char ?\M-p "\ |
| 53 | *Character to pull previous (older) search string from the ring of same.") |
| 54 | |
| 55 | (defconst search-exit-option t "\ |
| 56 | *Non-nil means random control characters terminate incremental search.") |
| 57 | |
| 58 | (defvar search-slow-window-lines 1 "\ |
| 59 | *Number of lines in slow search display windows. |
| 60 | These are the short windows used during incremental search on slow terminals. |
| 61 | Negative means put the slow search window at the top (normally it's at bottom) |
| 62 | and the value is minus the number of lines.") |
| 63 | |
| 64 | (defvar search-slow-speed 1200 "\ |
| 65 | *Highest terminal speed at which to use \"slow\" style incremental search. |
| 66 | This is the style where a one-line window is created to show the line |
| 67 | that the search has reached.") |
| 68 | |
| 69 | (defconst search-upper-case t |
| 70 | "*Non-nil means an upper-case letter as search input means case-sensitive. |
| 71 | Any upper-case letter given explicitly as input to the incremental search |
| 72 | has the effect of turning off `case-fold-search' for the rest of this search. |
| 73 | Deleting the letter from the search string cancels the effect.") |
| 74 | |
| 75 | (fset 'search-forward-regexp 're-search-forward) |
| 76 | (fset 'search-backward-regexp 're-search-backward) |
| 77 | |
| 78 | (defvar search-ring nil |
| 79 | "List of recent non-regexp incremental searches. |
| 80 | Each element is a cons cell of the form (STRING . UPPERCASE-FLAG).") |
| 81 | |
| 82 | (defvar regexp-search-ring nil |
| 83 | "List of recent regexp incremental searches. |
| 84 | Each element is a cons cell of the form (STRING . UPPERCASE-FLAG).") |
| 85 | |
| 86 | (defconst search-ring-max 16 |
| 87 | "*Maximum length of search ring before oldest elements are thrown away.") |
| 88 | |
| 89 | (defvar search-ring-yank-pointer nil |
| 90 | "The tail of the search ring whose car is the last thing searched for.") |
| 91 | |
| 92 | (defvar regexp-search-ring-yank-pointer nil |
| 93 | "The tail of the regular expression search ring whose car is the last |
| 94 | thing searched for.") |
| 95 | |
| 96 | |
| 97 | (defun isearch-forward () |
| 98 | "Do incremental search forward. |
| 99 | As you type characters, they add to the search string and are found. |
| 100 | Type Delete to cancel characters from end of search string. |
| 101 | Type RET to exit, leaving point at location found. |
| 102 | Type C-s to search again forward, C-r to search again backward. |
| 103 | Type C-w to yank word from buffer onto end of search string and search for it. |
| 104 | Type C-y to yank rest of line onto end of search string, etc. |
| 105 | Type C-q to quote control character to search for it. |
| 106 | Other control and meta characters terminate the search |
| 107 | and are then executed normally. |
| 108 | The above special characters are mostly controlled by parameters; |
| 109 | do M-x apropos on search-.*-char to find them. |
| 110 | C-g while searching or when search has failed |
| 111 | cancels input back to what has been found successfully. |
| 112 | C-g when search is successful aborts and moves point to starting point." |
| 113 | (interactive) |
| 114 | (isearch t)) |
| 115 | (define-key global-map "\C-s" 'isearch-forward) |
| 116 | |
| 117 | (defun isearch-forward-regexp () |
| 118 | "Do incremental search forward for regular expression. |
| 119 | Like ordinary incremental search except that your input |
| 120 | is treated as a regexp. See \\[isearch-forward] for more info." |
| 121 | (interactive) |
| 122 | (isearch t t)) |
| 123 | (define-key esc-map "\C-s" 'isearch-forward-regexp) |
| 124 | |
| 125 | (defun isearch-backward () |
| 126 | "Do incremental search backward. |
| 127 | See \\[isearch-forward] for more information." |
| 128 | (interactive) |
| 129 | (isearch nil)) |
| 130 | (define-key global-map "\C-r" 'isearch-backward) |
| 131 | |
| 132 | (defun isearch-backward-regexp () |
| 133 | "Do incremental search backward for regular expression. |
| 134 | Like ordinary incremental search except that your input |
| 135 | is treated as a regexp. See \\[isearch-forward] for more info." |
| 136 | (interactive) |
| 137 | (isearch nil t)) |
| 138 | (define-key esc-map "\C-r" 'isearch-backward-regexp) |
| 139 | |
| 140 | |
| 141 | ;; This function does all the work of incremental search. |
| 142 | ;; The functions attached to ^R and ^S are trivial, |
| 143 | ;; merely calling this one, but they are always loaded by default |
| 144 | ;; whereas this file can optionally be autoloadable. |
| 145 | ;; This is the only entry point in this file. |
| 146 | |
| 147 | ;; OP-FUN is a function to be called after each input character is processed. |
| 148 | ;; (It is not called after characters that exit the search.) |
| 149 | |
| 150 | (defun isearch (forward &optional regexp op-fun) |
| 151 | (let ((search-string "") |
| 152 | (search-message "") |
| 153 | ;; List of previous states during this search. |
| 154 | (history nil) |
| 155 | ;; t means search is currently successful. |
| 156 | (success t) |
| 157 | ;; Set once the search has wrapped around the end of the buffer. |
| 158 | (wrapped nil) |
| 159 | ;; Nominal starting point for searching |
| 160 | ;; Usually this is the same as the opoint, |
| 161 | ;; but it is changed by wrapping |
| 162 | ;; and also by repeating the search. |
| 163 | (barrier (point)) |
| 164 | ;; Set temporarily when adding a character to a regexp |
| 165 | ;; enables it to match more rather than fewer places in the buffer. |
| 166 | liberalized |
| 167 | ;; Set temporarily by yanking text into the search string. |
| 168 | yank-flag |
| 169 | (invalid-regexp nil) |
| 170 | ;; non-nil means an explicit uppercase letter seen in the input |
| 171 | (uppercase-flag nil) |
| 172 | ;; Non-nil means start using a small window |
| 173 | ;; if the search moves outside what is currently on the frame. |
| 174 | (slow-terminal-mode (and (<= baud-rate search-slow-speed) |
| 175 | (> (window-height) |
| 176 | (* 4 search-slow-window-lines)))) |
| 177 | ;; t means a small window is currently in use. |
| 178 | (small-window nil) ;if t, using a small window |
| 179 | ;; These variables preserve information from the small window |
| 180 | ;; through exit from the save-window-excursion. |
| 181 | (found-point nil) |
| 182 | (found-start nil) |
| 183 | ;; Point is at one end of the last match. |
| 184 | ;; This variable records the other end of that match. |
| 185 | (other-end nil) |
| 186 | ;; Value of point at start of search, |
| 187 | ;; for moving the cursor back on quitting. |
| 188 | (opoint (point)) |
| 189 | (inhibit-quit t) ;Prevent ^G from quitting, so we can read it. |
| 190 | ;; The frame we're working on; if this changes, we exit isearch. |
| 191 | (frame (if (fboundp 'selected-frame) (selected-frame)))) |
| 192 | |
| 193 | (isearch-push-state) |
| 194 | (save-window-excursion |
| 195 | (catch 'search-done |
| 196 | (while t |
| 197 | (or (and (numberp unread-command-char) (>= unread-command-char 0)) |
| 198 | (progn |
| 199 | (or (input-pending-p) |
| 200 | (isearch-message)) |
| 201 | (if (and slow-terminal-mode |
| 202 | (not (or small-window (pos-visible-in-window-p)))) |
| 203 | (progn |
| 204 | (setq small-window t) |
| 205 | (setq found-point (point)) |
| 206 | (move-to-window-line 0) |
| 207 | (let ((window-min-height 1)) |
| 208 | (split-window nil (if (< search-slow-window-lines 0) |
| 209 | (1+ (- search-slow-window-lines)) |
| 210 | (- (window-height) |
| 211 | (1+ search-slow-window-lines))))) |
| 212 | (if (< search-slow-window-lines 0) |
| 213 | (progn (vertical-motion (- 1 search-slow-window-lines)) |
| 214 | (set-window-start (next-window) (point)) |
| 215 | (set-window-hscroll (next-window) |
| 216 | (window-hscroll)) |
| 217 | (set-window-hscroll (selected-window) 0)) |
| 218 | (other-window 1)) |
| 219 | (goto-char found-point))))) |
| 220 | (let ((char (if quit-flag |
| 221 | ?\C-g |
| 222 | (read-event)))) |
| 223 | (setq quit-flag nil liberalized nil yank-flag nil) |
| 224 | (cond ((and (or (not (integerp char)) |
| 225 | (and (>= char 128) |
| 226 | (not (= char search-ring-advance-char)) |
| 227 | (not (= char search-ring-retreat-char)))) |
| 228 | search-exit-option) |
| 229 | (setq unread-command-char char) |
| 230 | (throw 'search-done t)) |
| 231 | |
| 232 | ;; If the user switches to a different frame, exit. |
| 233 | ((not (eq frame last-event-frame)) |
| 234 | (setq unread-command-char char) |
| 235 | (throw 'search-done t)) |
| 236 | |
| 237 | ((eq char search-exit-char) |
| 238 | ;; RET means exit search normally. |
| 239 | ;; Except, if first thing typed, it means do nonincremental |
| 240 | (if (= 0 (length search-string)) |
| 241 | (nonincremental-search forward regexp)) |
| 242 | (throw 'search-done t)) |
| 243 | ((= char ?\C-g) |
| 244 | ;; ^G means the user tried to quit. |
| 245 | (ding) |
| 246 | (discard-input) |
| 247 | (if success |
| 248 | ;; If search is successful, move back to starting point |
| 249 | ;; and really do quit. |
| 250 | (progn (goto-char opoint) |
| 251 | (signal 'quit nil)) |
| 252 | ;; If search is failing, rub out until it is once more |
| 253 | ;; successful. |
| 254 | (while (not success) (isearch-pop)))) |
| 255 | ((or (eq char search-repeat-char) |
| 256 | (eq char search-reverse-char)) |
| 257 | (if (eq forward (eq char search-repeat-char)) |
| 258 | ;; C-s in forward or C-r in reverse. |
| 259 | (if (equal search-string "") |
| 260 | ;; If search string is empty, use last one. |
| 261 | (isearch-get-string-from-ring) |
| 262 | ;; If already have what to search for, repeat it. |
| 263 | (or success |
| 264 | (progn (goto-char (if forward (point-min) (point-max))) |
| 265 | (setq wrapped t)))) |
| 266 | ;; C-s in reverse or C-r in forward, change direction. |
| 267 | (setq forward (not forward))) |
| 268 | (setq barrier (point)) ; For subsequent \| if regexp. |
| 269 | (setq success t) |
| 270 | (or (equal search-string "") |
| 271 | (progn |
| 272 | ;; If repeating a search that found an empty string, |
| 273 | ;; ensure we advance. Test history to make sure we |
| 274 | ;; actually have done a search already; otherwise, |
| 275 | ;; the match data will be random. |
| 276 | (if (and (cdr history) |
| 277 | (= (match-end 0) (match-beginning 0))) |
| 278 | (forward-char (if forward 1 -1))) |
| 279 | (isearch-search))) |
| 280 | (isearch-push-state)) |
| 281 | ((= char search-delete-char) |
| 282 | ;; Rubout means discard last input item and move point |
| 283 | ;; back. If buffer is empty, just beep. |
| 284 | (if (null (cdr history)) |
| 285 | (ding) |
| 286 | (isearch-pop))) |
| 287 | ((= char search-ring-advance-char) |
| 288 | (isearch-pop) |
| 289 | (if regexp |
| 290 | (let ((length (length regexp-search-ring))) |
| 291 | (if (zerop length) |
| 292 | () |
| 293 | (setq regexp-search-ring-yank-pointer |
| 294 | (nthcdr (% (+ 1 (- length (length regexp-search-ring-yank-pointer))) |
| 295 | length) |
| 296 | regexp-search-ring)) |
| 297 | (isearch-get-string-from-ring))) |
| 298 | (let ((length (length search-ring))) |
| 299 | (if (zerop length) |
| 300 | () |
| 301 | (setq search-ring-yank-pointer |
| 302 | (nthcdr (% (+ 1 (- length (length search-ring-yank-pointer))) |
| 303 | length) |
| 304 | search-ring)) |
| 305 | (isearch-get-string-from-ring)))) |
| 306 | (isearch-push-state) |
| 307 | (isearch-search)) |
| 308 | ((= char search-ring-retreat-char) |
| 309 | (isearch-pop) |
| 310 | (if regexp |
| 311 | (let ((length (length regexp-search-ring))) |
| 312 | (if (zerop length) |
| 313 | () |
| 314 | (setq regexp-search-ring-yank-pointer |
| 315 | (nthcdr (% (+ (- length (length regexp-search-ring-yank-pointer)) |
| 316 | (1- length)) |
| 317 | length) |
| 318 | regexp-search-ring)) |
| 319 | (isearch-get-string-from-ring))) |
| 320 | (let ((length (length search-ring))) |
| 321 | (if (zerop length) |
| 322 | () |
| 323 | (setq search-ring-yank-pointer |
| 324 | (nthcdr (% (+ (- length (length search-ring-yank-pointer)) |
| 325 | (1- length)) |
| 326 | length) |
| 327 | search-ring)) |
| 328 | (isearch-get-string-from-ring)))) |
| 329 | (isearch-push-state) |
| 330 | (isearch-search)) |
| 331 | (t |
| 332 | (cond ((or (eq char search-yank-word-char) |
| 333 | (eq char search-yank-line-char)) |
| 334 | ;; ^W means gobble next word from buffer. |
| 335 | ;; ^Y means gobble rest of line from buffer. |
| 336 | (let ((word (save-excursion |
| 337 | (and (not forward) other-end |
| 338 | (goto-char other-end)) |
| 339 | (buffer-substring |
| 340 | (point) |
| 341 | (save-excursion |
| 342 | (if (eq char search-yank-line-char) |
| 343 | (end-of-line) |
| 344 | (forward-word 1)) |
| 345 | (point)))))) |
| 346 | (if regexp |
| 347 | (setq word (regexp-quote word))) |
| 348 | (setq search-string (concat search-string word) |
| 349 | search-message |
| 350 | (concat search-message |
| 351 | (mapconcat 'text-char-description |
| 352 | word "")) |
| 353 | ;; Don't move cursor in reverse search. |
| 354 | yank-flag t))) |
| 355 | ;; Any other control char => |
| 356 | ;; unread it and exit the search normally. |
| 357 | ((and search-exit-option |
| 358 | (/= char search-quote-char) |
| 359 | (or (>= char ?\177) |
| 360 | (and (< char ? ) |
| 361 | (/= char ?\t) |
| 362 | (/= char ?\n)))) |
| 363 | (setq unread-command-char char) |
| 364 | (throw 'search-done t)) |
| 365 | (t |
| 366 | ;; Any other character => add it to the |
| 367 | ;; search string and search. |
| 368 | (cond ((= char search-quote-char) |
| 369 | (setq char (read-quoted-char |
| 370 | (isearch-message t)))) |
| 371 | ((= char ?\r) |
| 372 | ;; RET translates to newline. |
| 373 | (setq char ?\n))) |
| 374 | (setq search-string (concat search-string |
| 375 | (char-to-string char)) |
| 376 | search-message (concat search-message |
| 377 | (text-char-description char)) |
| 378 | uppercase-flag (or uppercase-flag |
| 379 | (not (= char (downcase char))))))) |
| 380 | (if (and (not success) |
| 381 | ;; unsuccessful regexp search may become |
| 382 | ;; successful by addition of characters which |
| 383 | ;; make search-string valid |
| 384 | (not regexp)) |
| 385 | nil |
| 386 | ;; Check for chars that can make a regexp more liberal. |
| 387 | ;; They can make a regexp match sooner |
| 388 | ;; or make it succeed instead of failing. |
| 389 | ;; So go back to place last successful search started |
| 390 | ;; or to the last ^S/^R (barrier), whichever is nearer. |
| 391 | (and regexp history |
| 392 | (cond ((and (memq char '(?* ??)) |
| 393 | ;; Don't treat *, ? as special |
| 394 | ;; within [] or after \. |
| 395 | (not (nth 6 (car history)))) |
| 396 | (setq liberalized t) |
| 397 | ;; This used to use element 2 |
| 398 | ;; in a reverse search, but it seems that 5 |
| 399 | ;; (which is the end of the old match) |
| 400 | ;; is better in that case too. |
| 401 | (let ((cs (nth 5 ; old other-end. |
| 402 | (car (cdr history))))) |
| 403 | ;; (car history) is after last search; |
| 404 | ;; (car (cdr history)) is from before it. |
| 405 | (setq cs (or cs barrier)) |
| 406 | (goto-char |
| 407 | (if forward |
| 408 | (max cs barrier) |
| 409 | (min cs barrier))))) |
| 410 | ((eq char ?\|) |
| 411 | (setq liberalized t) |
| 412 | (goto-char barrier)))) |
| 413 | ;; Turn off case-sensitivity if string requests it. |
| 414 | (let ((case-fold-search |
| 415 | (and case-fold-search |
| 416 | (not (and uppercase-flag |
| 417 | search-upper-case))))) |
| 418 | ;; In reverse search, adding stuff at |
| 419 | ;; the end may cause zero or many more chars to be |
| 420 | ;; matched, in the string following point. |
| 421 | ;; Allow all those possibilities without moving point as |
| 422 | ;; long as the match does not extend past search origin. |
| 423 | (if (and (not forward) (not liberalized) |
| 424 | (condition-case () |
| 425 | (looking-at (if regexp search-string |
| 426 | (regexp-quote search-string))) |
| 427 | (error nil)) |
| 428 | (or yank-flag |
| 429 | ;; Used to have (min opoint barrier) |
| 430 | ;; instead of barrier. |
| 431 | ;; This lost when wrapping. |
| 432 | (<= (match-end 0) barrier))) |
| 433 | (setq success t invalid-regexp nil |
| 434 | other-end (match-end 0)) |
| 435 | ;; Not regexp, not reverse, or no match at point. |
| 436 | (if (and other-end (not liberalized)) |
| 437 | (goto-char (if forward other-end |
| 438 | ;; Used to have opoint inside the min. |
| 439 | ;; This lost when wrapping. |
| 440 | (min barrier (1+ other-end))))) |
| 441 | (isearch-search)))) |
| 442 | (isearch-push-state)))) |
| 443 | (if op-fun (funcall op-fun)))) |
| 444 | (setq found-start (window-start (selected-window))) |
| 445 | (setq found-point (point))) |
| 446 | (if (> (length search-string) 0) |
| 447 | (if (and regexp (not (member search-string regexp-search-ring))) |
| 448 | (progn |
| 449 | (setq regexp-search-ring (cons (cons search-string uppercase-flag) |
| 450 | regexp-search-ring) |
| 451 | regexp-search-ring-yank-pointer regexp-search-ring) |
| 452 | (if (> (length regexp-search-ring) search-ring-max) |
| 453 | (setcdr (nthcdr (1- search-ring-max) regexp-search-ring) nil))) |
| 454 | (if (not (member search-string search-ring)) |
| 455 | (progn |
| 456 | (setq search-ring (cons (cons search-string uppercase-flag) |
| 457 | search-ring) |
| 458 | search-ring-yank-pointer search-ring) |
| 459 | (if (> (length search-ring) search-ring-max) |
| 460 | (setcdr (nthcdr (1- search-ring-max) search-ring) nil)))))) |
| 461 | ;; If we displayed a single-line window, set point in this window. |
| 462 | (if small-window |
| 463 | (goto-char found-point)) |
| 464 | ;; If there was movement, mark the starting position. |
| 465 | ;; Maybe should test difference between and set mark iff > threshold. |
| 466 | (if (/= (point) opoint) |
| 467 | (push-mark opoint) |
| 468 | (message "")) |
| 469 | (or small-window |
| 470 | ;; Exiting the save-window-excursion clobbers this; restore it. |
| 471 | (set-window-start (selected-window) found-start t)))) |
| 472 | |
| 473 | (defun isearch-message (&optional c-q-hack ellipsis) |
| 474 | ;; If about to search, and previous search regexp was invalid, |
| 475 | ;; check that it still is. If it is valid now, |
| 476 | ;; let the message we display while searching say that it is valid. |
| 477 | (and invalid-regexp ellipsis |
| 478 | (condition-case () |
| 479 | (progn (re-search-forward search-string (point) t) |
| 480 | (setq invalid-regexp nil)) |
| 481 | (error nil))) |
| 482 | ;; If currently failing, display no ellipsis. |
| 483 | (or success (setq ellipsis nil)) |
| 484 | (let ((m (concat (if success "" "failing ") |
| 485 | (if wrapped "wrapped ") |
| 486 | (if (or (not case-fold-search) |
| 487 | (and uppercase-flag search-upper-case)) |
| 488 | "case-sensitive ") |
| 489 | (if regexp "regexp " "") |
| 490 | "I-search" |
| 491 | (if forward ": " " backward: ") |
| 492 | search-message |
| 493 | (if c-q-hack "^Q" "") |
| 494 | (if invalid-regexp |
| 495 | (concat " [" invalid-regexp "]") |
| 496 | "")))) |
| 497 | (aset m 0 (upcase (aref m 0))) |
| 498 | (let ((cursor-in-echo-area ellipsis)) |
| 499 | (if c-q-hack m (message "%s" m))))) |
| 500 | |
| 501 | ;; Get the search string from the "front" of the ring of previous searches. |
| 502 | (defun isearch-get-string-from-ring () |
| 503 | (let ((elt (car (if regexp |
| 504 | (or regexp-search-ring-yank-pointer regexp-search-ring) |
| 505 | (or search-ring-yank-pointer search-ring))))) |
| 506 | ;; ELT describes the most recent search or where we have rotated the ring. |
| 507 | (if elt |
| 508 | (setq search-string (car elt) |
| 509 | uppercase-flag (cdr elt)) |
| 510 | (setq search-string "" uppercase-flag nil))) |
| 511 | ;; Let's give this one the benefit of the doubt. |
| 512 | (setq invalid-regexp nil) |
| 513 | (setq search-message (mapconcat 'text-char-description search-string ""))) |
| 514 | |
| 515 | (defun isearch-pop () |
| 516 | (setq history (cdr history)) |
| 517 | (let ((cmd (car history))) |
| 518 | (setq search-string (car cmd) |
| 519 | search-message (car (cdr cmd)) |
| 520 | success (nth 3 cmd) |
| 521 | forward (nth 4 cmd) |
| 522 | other-end (nth 5 cmd) |
| 523 | invalid-regexp (nth 6 cmd) |
| 524 | wrapped (nth 7 cmd) |
| 525 | barrier (nth 8 cmd) |
| 526 | uppercase-flag (nth 9 cmd)) |
| 527 | (goto-char (car (cdr (cdr cmd)))))) |
| 528 | |
| 529 | (defun isearch-push-state () |
| 530 | (setq history (cons (list search-string search-message (point) |
| 531 | success forward other-end invalid-regexp |
| 532 | wrapped barrier uppercase-flag) |
| 533 | history))) |
| 534 | |
| 535 | (defun isearch-search () |
| 536 | (let ((case-fold-search |
| 537 | (and case-fold-search |
| 538 | (not (and uppercase-flag |
| 539 | search-upper-case))))) |
| 540 | (isearch-message nil t) |
| 541 | (condition-case lossage |
| 542 | (let ((inhibit-quit nil)) |
| 543 | (if regexp (setq invalid-regexp nil)) |
| 544 | (setq success |
| 545 | (funcall |
| 546 | (if regexp |
| 547 | (if forward 're-search-forward 're-search-backward) |
| 548 | (if forward 'search-forward 'search-backward)) |
| 549 | search-string nil t)) |
| 550 | (if success |
| 551 | (setq other-end |
| 552 | (if forward (match-beginning 0) (match-end 0))))) |
| 553 | (quit (setq unread-command-char ?\C-g) |
| 554 | (setq success nil)) |
| 555 | (invalid-regexp (setq invalid-regexp (car (cdr lossage))) |
| 556 | (if (string-match "\\`Premature \\|\\`Unmatched \\|\\`Invalid " |
| 557 | invalid-regexp) |
| 558 | (setq invalid-regexp "incomplete input")))) |
| 559 | (if success |
| 560 | nil |
| 561 | ;; Ding if failed this time after succeeding last time. |
| 562 | (and (nth 3 (car history)) |
| 563 | (ding)) |
| 564 | (goto-char (nth 2 (car history)))))) |
| 565 | |
| 566 | ;; This is called from incremental-search |
| 567 | ;; if the first input character is the exit character. |
| 568 | ;; The interactive-arg-reader uses free variables `forward' and `regexp' |
| 569 | ;; which are bound by `incremental-search'. |
| 570 | |
| 571 | ;; We store the search string in `search-string' |
| 572 | ;; which has been bound already by `incremental-search' |
| 573 | ;; so that, when we exit, it is copied into `search-last-string'. |
| 574 | |
| 575 | (defun nonincremental-search (forward regexp) |
| 576 | (let (message char function string inhibit-quit) |
| 577 | (let ((cursor-in-echo-area t)) |
| 578 | ;; Prompt assuming not word search, |
| 579 | (setq message (if regexp |
| 580 | (if forward "Regexp search: " |
| 581 | "Regexp search backward: ") |
| 582 | (if forward "Search: " "Search backward: "))) |
| 583 | (message "%s" message) |
| 584 | ;; Read 1 char and switch to word search if it is ^W. |
| 585 | (setq char (read-event))) |
| 586 | (if (and (numberp char) (eq char search-yank-word-char)) |
| 587 | (setq message (if forward "Word search: " "Word search backward: ")) |
| 588 | ;; Otherwise let that 1 char be part of the search string. |
| 589 | (setq unread-command-char char)) |
| 590 | (setq function |
| 591 | (if (eq char search-yank-word-char) |
| 592 | (if forward 'word-search-forward 'word-search-backward) |
| 593 | (if regexp |
| 594 | (if forward 're-search-forward 're-search-backward) |
| 595 | (if forward 'search-forward 'search-backward)))) |
| 596 | ;; Read the search string with corrected prompt. |
| 597 | (setq string (read-string message)) |
| 598 | ;; Empty means use default. |
| 599 | (if (= 0 (length string)) |
| 600 | (setq string search-last-string) |
| 601 | ;; Set last search string now so it is set even if we fail. |
| 602 | (setq search-last-string string)) |
| 603 | ;; Since we used the minibuffer, we should be available for redo. |
| 604 | (setq command-history (cons (list function string) command-history)) |
| 605 | ;; Go ahead and search. |
| 606 | (funcall function string))) |
| 607 | |
| 608 | ;;; isearch.el ends here |