;;; isearch.el --- incremental search minor mode.
-;; Copyright (C) 1992, 1993, 1994 Free Software Foundation, Inc.
+;; Copyright (C) 1992, 1993, 1994, 1995 Free Software Foundation, Inc.
;; Author: Daniel LaLiberte <liberte@cs.uiuc.edu>
-
-;; |$Date: 1994/12/15 02:06:45 $|$Revision: 1.80 $
+;; Maintainer: FSF
;; This file is part of GNU Emacs.
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs; see the file COPYING. If not, write to
-;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+;; along with GNU Emacs; see the file COPYING. If not, write to the
+;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+;; Boston, MA 02111-1307, USA.
;;; Commentary:
-;;;====================================================================
;; Instructions
;; For programmed use of isearch-mode, e.g. calling (isearch-forward),
;;; Change Log:
-;;; Changes before those recorded in ChangeLog:
-
-;;; Revision 1.4 92/09/14 16:26:02 liberte
-;;; Added prefix args to isearch-forward, etc. to switch between
-;;; string and regular expression searching.
-;;; Added some support for lemacs.
-;;; Added general isearch-highlight option - but only for lemacs so far.
-;;; Added support for frame switching in emacs 19.
-;;; Added word search option to isearch-edit-string.
-;;; Renamed isearch-quit to isearch-abort.
-;;; Numerous changes to comments and doc strings.
-;;;
-;;; Revision 1.3 92/06/29 13:10:08 liberte
-;;; Moved modal isearch-mode handling into isearch-mode.
-;;; Got rid of buffer-local isearch variables.
-;;; isearch-edit-string used by ring adjustments, completion, and
-;;; nonincremental searching. C-s and C-r are additional exit commands.
-;;; Renamed all regex to regexp.
-;;; Got rid of found-start and found-point globals.
-;;; Generalized handling of upper-case chars.
-
-;;; Revision 1.2 92/05/27 11:33:57 liberte
-;;; Emacs version 19 has a search ring, which is supported here.
-;;; Other fixes found in the version 19 isearch are included here.
-;;;
-;;; Also see variables search-caps-disable-folding,
-;;; search-nonincremental-instead, search-whitespace-regexp, and
-;;; commands isearch-toggle-regexp, isearch-edit-string.
-;;;
-;;; semi-modal isearching is supported.
-
-;;; Changes for 1.1
-;;; 3/18/92 Fixed invalid-regexp.
-;;; 3/18/92 Fixed yanking in regexps.
+;; Changes before those recorded in ChangeLog:
+
+;; Revision 1.4 92/09/14 16:26:02 liberte
+;; Added prefix args to isearch-forward, etc. to switch between
+;; string and regular expression searching.
+;; Added some support for lemacs.
+;; Added general isearch-highlight option - but only for lemacs so far.
+;; Added support for frame switching in emacs 19.
+;; Added word search option to isearch-edit-string.
+;; Renamed isearch-quit to isearch-abort.
+;; Numerous changes to comments and doc strings.
+;;
+;; Revision 1.3 92/06/29 13:10:08 liberte
+;; Moved modal isearch-mode handling into isearch-mode.
+;; Got rid of buffer-local isearch variables.
+;; isearch-edit-string used by ring adjustments, completion, and
+;; nonincremental searching. C-s and C-r are additional exit commands.
+;; Renamed all regex to regexp.
+;; Got rid of found-start and found-point globals.
+;; Generalized handling of upper-case chars.
+
+;; Revision 1.2 92/05/27 11:33:57 liberte
+;; Emacs version 19 has a search ring, which is supported here.
+;; Other fixes found in the version 19 isearch are included here.
+;;
+;; Also see variables search-caps-disable-folding,
+;; search-nonincremental-instead, search-whitespace-regexp, and
+;; commands isearch-toggle-regexp, isearch-edit-string.
+;;
+;; semi-modal isearching is supported.
+
+;; Changes for 1.1
+;; 3/18/92 Fixed invalid-regexp.
+;; 3/18/92 Fixed yanking in regexps.
;;; Code:
\f
-;;;========================================================================
;;; Some additional options and constants.
(defconst search-exit-option t
"*If non-nil, regular expression to match a sequence of whitespace chars.
You might want to use something like \"[ \\t\\r\\n]+\" instead.")
-;; I removed the * from the doc string because highlighting is not
-;; currently a clean thing to do. Once highlighting is made clean,
-;; this feature can be re-enabled and advertised.
(defvar search-highlight nil
"*Non-nil means incremental search highlights the current match.")
(defvar isearch-mode-end-hook nil
"Function(s) to call after terminating an incremental search.")
-;;;==================================================================
;;; Search ring.
(defvar search-ring nil
"*Non-nil if advancing or retreating in the search ring should cause search.
Default value, nil, means edit the string instead.")
-;;;====================================================
;;; Define isearch-mode keymap.
(defvar isearch-mode-map nil
(define-key map "\C-w" 'isearch-yank-word)
(define-key map "\C-y" 'isearch-yank-line)
- ;; Bind the ASCII-equivalent "function keys" explicitly to nil
- ;; so that the default binding does not apply.
- ;; As a result, these keys translate thru function-key-map
- ;; as normal, and they have the effect of the equivalent ASCII char.
- ;; We bind [escape] below.
- (define-key map [tab] 'nil)
- (define-key map [kp-0] 'nil)
- (define-key map [kp-1] 'nil)
- (define-key map [kp-2] 'nil)
- (define-key map [kp-3] 'nil)
- (define-key map [kp-4] 'nil)
- (define-key map [kp-5] 'nil)
- (define-key map [kp-6] 'nil)
- (define-key map [kp-7] 'nil)
- (define-key map [kp-8] 'nil)
- (define-key map [kp-9] 'nil)
- (define-key map [kp-add] 'nil)
- (define-key map [kp-subtract] 'nil)
- (define-key map [kp-multiply] 'nil)
- (define-key map [kp-divide] 'nil)
- (define-key map [kp-decimal] 'nil)
- (define-key map [kp-separator] 'nil)
- (define-key map [kp-equal] 'nil)
- (define-key map [kp-tab] 'nil)
- (define-key map [kp-space] 'nil)
- (define-key map [kp-enter] 'nil)
- (define-key map [delete] 'nil)
- (define-key map [backspace] 'nil)
- (define-key map [return] 'nil)
- (define-key map [newline] 'nil)
-
;; Define keys for regexp chars * ? |.
;; Nothing special for + because it matches at least once.
(define-key map "*" 'isearch-*-char)
(define-key map "\M-\t" 'isearch-complete)
- ;; Switching frames should terminate isearch-mode
- (define-key map [switch-frame] 'isearch-switch-frame-handler)
-
+ ;; Pass frame events transparently so they won't exit the search.
+ ;; In particular, if we have more than one display open, then a
+ ;; switch-frame might be generated by someone typing at another keyboard.
+ (define-key map [switch-frame] nil)
+ (define-key map [delete-frame] nil)
+ (define-key map [iconify-frame] nil)
+ (define-key map [make-frame-visible] nil)
+
(setq isearch-mode-map map)
))
(setq minibuffer-local-isearch-map map)
))
-;;;========================================================
;; Internal variables declared globally for byte-compiler.
;; These are all set with setq while isearching
;; and bound locally while editing the search string.
(defvar isearch-other-end nil) ; Start (end) of match if forward (backward).
(defvar isearch-wrapped nil) ; Searching restarted from the top (bottom).
(defvar isearch-barrier 0)
+(defvar isearch-just-started nil)
; case-fold-search while searching.
; either nil, t, or 'yes. 'yes means the same as t except that mixed
(defvar isearch-new-forward nil)
-;;;==============================================================
;; Minor-mode-alist changes - kind of redundant with the
;; echo area, but if isearching in multiple windows, it can be useful.
(define-key global-map "\C-r" 'isearch-backward)
(define-key esc-map "\C-r" 'isearch-backward-regexp)
-;;;===============================================================
;;; Entry points to isearch-mode.
;;; These four functions should replace those in loaddefs.el
;;; An alternative is to defalias isearch-forward etc to isearch-mode,
options; do M-x apropos on search-.* to find them.
Other control and meta characters terminate the search
and are then executed normally (depending on `search-exit-option').
+Likewise for function keys and mouse button events.
If this function is called non-interactively, it does not return to
the calling function until the search is done."
(isearch-update))
\f
-;;;==================================================================
;; isearch-mode only sets up incremental search for the minor mode.
;; All the work is done by the isearch-mode commands.
(defun isearch-mode (forward &optional regexp op-fun recursive-edit word-p)
- "Start isearch minor mode. Called by isearch-forward, etc."
+ "Start isearch minor mode. Called by `isearch-forward', etc.
+
+\\{isearch-mode-map}"
;; Initialize global vars.
(setq isearch-forward forward
(* 4 search-slow-window-lines)))
isearch-other-end nil
isearch-small-window nil
+ isearch-just-started t
isearch-opoint (point)
search-ring-yank-pointer nil
regexp-search-ring-yank-pointer nil)
+ (looking-at "")
(setq isearch-window-configuration
(if isearch-slow-terminal-mode (current-window-configuration) nil))
(setq isearch-mode " Isearch") ;; forward? regexp?
- (set-buffer-modified-p (buffer-modified-p)) ; update modeline
+ (force-mode-line-update)
(isearch-push-state)
- (make-local-variable 'overriding-local-map)
- (setq overriding-local-map isearch-mode-map)
+ (setq overriding-terminal-local-map isearch-mode-map)
(isearch-update)
(run-hooks 'isearch-mode-hook)
+ (add-hook 'mouse-leave-buffer-hook 'isearch-done)
+
;; isearch-mode can be made modal (in the sense of not returning to
;; the calling function until searching is completed) by entering
;; a recursive-edit and exiting it when done isearching.
isearch-success)
-;;;====================================================
;; Some high level utilities. Others below.
(defun isearch-update ()
isearch-yank-flag nil)
)
-
(defun isearch-done (&optional nopush edit)
+ (remove-hook 'mouse-leave-buffer-hook 'isearch-done)
;; Called by all commands that terminate isearch-mode.
;; If NOPUSH is non-nil, we don't push the string on the search ring.
- (setq overriding-local-map nil)
+ (setq overriding-terminal-local-map nil)
;; (setq pre-command-hook isearch-old-pre-command-hook) ; for lemacs
(isearch-dehighlight t)
(let ((found-start (window-start (selected-window)))
(or (and transient-mark-mode mark-active)
(progn
(push-mark isearch-opoint t)
- (or executing-macro (> (minibuffer-depth) 0)
+ (or executing-kbd-macro (> (minibuffer-depth) 0)
(message "Mark saved where search started"))))))
(setq isearch-mode nil)
- (set-buffer-modified-p (buffer-modified-p)) ;; update modeline
+ (force-mode-line-update)
(if (and (> (length isearch-string) 0) (not nopush))
;; Update the ring data.
- (if isearch-regexp
- (if (or (null regexp-search-ring)
- (not (string= isearch-string (car regexp-search-ring))))
- (progn
- (setq regexp-search-ring
- (cons isearch-string regexp-search-ring))
- (if (> (length regexp-search-ring) regexp-search-ring-max)
- (setcdr (nthcdr (1- search-ring-max) regexp-search-ring)
- nil))))
- (if (or (null search-ring)
- (not (string= isearch-string (car search-ring))))
- (progn
- (setq search-ring (cons isearch-string search-ring))
- (if (> (length search-ring) search-ring-max)
- (setcdr (nthcdr (1- search-ring-max) search-ring) nil))))))
+ (isearch-update-ring isearch-string isearch-regexp))
(run-hooks 'isearch-mode-end-hook)
(and (not edit) isearch-recursive-edit (exit-recursive-edit)))
-;;;=======================================================
+(defun isearch-update-ring (string &optional regexp)
+ "Add STRING to the beginning of the search ring.
+REGEXP says which ring to use."
+ (if regexp
+ (if (or (null regexp-search-ring)
+ (not (string= string (car regexp-search-ring))))
+ (progn
+ (setq regexp-search-ring
+ (cons string regexp-search-ring))
+ (if (> (length regexp-search-ring) regexp-search-ring-max)
+ (setcdr (nthcdr (1- search-ring-max) regexp-search-ring)
+ nil))))
+ (if (or (null search-ring)
+ (not (string= string (car search-ring))))
+ (progn
+ (setq search-ring (cons string search-ring))
+ (if (> (length search-ring) search-ring-max)
+ (setcdr (nthcdr (1- search-ring-max) search-ring) nil))))))
+
;;; Switching buffers should first terminate isearch-mode.
;;; This is done quite differently for each variant of emacs.
;;; For lemacs, see Exiting in lemacs below
(isearch-done)
(handle-switch-frame (car (cdr (isearch-last-command-char)))))
-;;;========================================================
-
\f
-;;;====================================================
;; Commands active while inside of the isearch minor mode.
(defun isearch-exit ()
(isearch-yank-flag isearch-yank-flag)
(isearch-invalid-regexp isearch-invalid-regexp)
(isearch-within-brackets isearch-within-brackets)
- (isearch-other-end isearch-other-end)
+;;; Don't bind this. We want isearch-search, below, to set it.
+;;; And the old value won't matter after that.
+;;; (isearch-other-end isearch-other-end)
+;;; Perhaps some of these other variables should be bound for a
+;;; shorter period, ending before the next isearch-search.
+;;; But there doesn't seem to be a real bug, so let's not risk it now.
(isearch-opoint isearch-opoint)
(isearch-slow-terminal-mode isearch-slow-terminal-mode)
(isearch-small-window isearch-small-window)
(read-event)))
;; Binding minibuffer-history-symbol to nil is a work-around
;; for some incompatibility with gmhist.
- (minibuffer-history-symbol))
+ (minibuffer-history-symbol)
+ (message-log-max nil))
;; If the first character the user types when we prompt them
;; for a string is the yank-word character, then go into
;; word-search mode. Otherwise unread that character and
;; Empty isearch-string means use default.
(if (= 0 (length isearch-string))
- (setq isearch-string (car (if isearch-regexp regexp-search-ring
- search-ring)))
+ (setq isearch-string (or (car (if isearch-regexp
+ regexp-search-ring
+ search-ring))
+ ""))
;; This used to set the last search string,
;; but I think it is not right to do that here.
;; Only the string actually used should be saved.
(signal 'quit nil)) ; and pass on quit signal
(defun isearch-abort ()
- "Abort incremental search mode if searching is successful, signalling quit.
+ "Abort incremental search mode if searching is successful, signaling quit.
Otherwise, revert to previous successful search and continue searching.
-Use `isearch-exit' to quit without signalling."
+Use `isearch-exit' to quit without signaling."
(interactive)
;; (ding) signal instead below, if quitting
(discard-input)
;; If search is successful, move back to starting point
;; and really do quit.
(progn (goto-char isearch-opoint)
+ (setq isearch-success nil)
(isearch-done t) ; exit isearch
(signal 'quit nil)) ; and pass on quit signal
- ;; If search is failing, rub out until it is once more successful.
- (while (not isearch-success) (isearch-pop-state))
+ ;; If search is failing, or has an incomplete regexp,
+ ;; rub out until it is once more successful.
+ (while (or (not isearch-success) isearch-invalid-regexp)
+ (isearch-pop-state))
(isearch-update)))
-
(defun isearch-repeat (direction)
;; Utility for isearch-repeat-forward and -backward.
(if (eq isearch-forward (eq direction 'forward))
(if (equal isearch-string "")
(setq isearch-success t)
- (if (and isearch-success (equal (match-end 0) (match-beginning 0)))
+ (if (and isearch-success (equal (match-end 0) (match-beginning 0))
+ (not isearch-just-started))
;; If repeating a search that found
;; an empty string, ensure we advance.
(if (if isearch-forward (eobp) (bobp))
(interactive)
(setq isearch-case-fold-search
(if isearch-case-fold-search nil 'yes))
- (message "%s%s [case %ssensitive]"
- (isearch-message-prefix nil nil isearch-nonincremental)
- isearch-message
- (if isearch-case-fold-search "in" ""))
+ (let ((message-log-max nil))
+ (message "%s%s [case %ssensitive]"
+ (isearch-message-prefix nil nil isearch-nonincremental)
+ isearch-message
+ (if isearch-case-fold-search "in" "")))
(setq isearch-adjusted t)
(sit-for 1)
(isearch-update))
;; long as the match does not extend past search origin.
(if (and (not isearch-forward) (not isearch-adjusted)
(condition-case ()
- (looking-at (if isearch-regexp isearch-string
- (regexp-quote isearch-string)))
+ (let ((case-fold-search isearch-case-fold-search))
+ (looking-at (if isearch-regexp isearch-string
+ (regexp-quote isearch-string))))
(error nil))
(or isearch-yank-flag
(<= (match-end 0)
If it is the symbol `edit', the search string is edited in the minibuffer
and the meta character is unread so that it applies to editing the string."
(interactive)
- (cond ((eq search-exit-option 'edit)
- (let ((key (this-command-keys)))
- (apply 'isearch-unread (listify-key-sequence key)))
- (isearch-edit-string))
- (search-exit-option
- (let* ((key (this-command-keys))
- (main-event (aref key 0))
- window)
- (apply 'isearch-unread (listify-key-sequence key))
- ;; Properly handle scroll-bar and mode-line clicks
- ;; for which a dummy prefix event was generated as (aref key 0).
- (and (> (length key) 1)
- (symbolp (aref key 0))
- (listp (aref key 1))
- (not (numberp (posn-point (event-start (aref key 1)))))
- ;; Convert the event back into its raw form,
- ;; with the dummy prefix implicit in the mouse event,
- ;; so it will get split up once again.
- (progn (setq unread-command-events
- (cdr unread-command-events))
- (setq main-event (car unread-command-events))
- (setcar (cdr (event-start main-event))
- (car (nth 1 (event-start main-event))))))
- ;; If we got a mouse click, maybe it was read with the buffer
- ;; it was clicked on. If so, that buffer, not the current one,
- ;; is in isearch mode. So end the search in that buffer.
- (if (and (listp main-event)
- (setq window (posn-window (event-start main-event)))
- (windowp window))
- (save-excursion
- (set-buffer (window-buffer window))
- (isearch-done))
- (isearch-done))))
- (t;; otherwise nil
- (isearch-process-search-string (this-command-keys)
- (this-command-keys)))))
+ (let* ((key (this-command-keys))
+ (main-event (aref key 0))
+ (keylist (listify-key-sequence key)))
+ (cond ((and (= (length key) 1)
+ (let ((lookup (lookup-key function-key-map key)))
+ (not (or (null lookup) (integerp lookup)
+ (keymapp lookup)))))
+ ;; Handle a function key that translates into something else.
+ ;; If the key has a global definition too,
+ ;; exit and unread the key itself, so its global definition runs.
+ ;; Otherwise, unread the translation,
+ ;; so that the translated key takes effect within isearch.
+ (cancel-kbd-macro-events)
+ (if (lookup-key global-map key)
+ (progn
+ (isearch-done)
+ (apply 'isearch-unread keylist))
+ (apply 'isearch-unread
+ (listify-key-sequence (lookup-key function-key-map key)))))
+ (
+ ;; Handle an undefined shifted control character
+ ;; by downshifting it if that makes it defined.
+ ;; (As read-key-sequence would normally do,
+ ;; if we didn't have a default definition.)
+ (let ((mods (event-modifiers main-event)))
+ (and (integerp main-event)
+ (memq 'shift mods)
+ (memq 'control mods)
+ (lookup-key isearch-mode-map
+ (let ((copy (copy-sequence key)))
+ (aset copy 0
+ (- main-event (- ?\C-\S-a ?\C-a)))
+ copy)
+ nil)))
+ (setcar keylist (- main-event (- ?\C-\S-a ?\C-a)))
+ (cancel-kbd-macro-events)
+ (apply 'isearch-unread keylist))
+ ((eq search-exit-option 'edit)
+ (apply 'isearch-unread keylist)
+ (isearch-edit-string))
+ (search-exit-option
+ (let (window)
+ (cancel-kbd-macro-events)
+ (apply 'isearch-unread keylist)
+ ;; Properly handle scroll-bar and mode-line clicks
+ ;; for which a dummy prefix event was generated as (aref key 0).
+ (and (> (length key) 1)
+ (symbolp (aref key 0))
+ (listp (aref key 1))
+ (not (numberp (posn-point (event-start (aref key 1)))))
+ ;; Convert the event back into its raw form,
+ ;; with the dummy prefix implicit in the mouse event,
+ ;; so it will get split up once again.
+ (progn (setq unread-command-events
+ (cdr unread-command-events))
+ (setq main-event (car unread-command-events))
+ (setcar (cdr (event-start main-event))
+ (car (nth 1 (event-start main-event))))))
+ ;; If we got a mouse click, maybe it was read with the buffer
+ ;; it was clicked on. If so, that buffer, not the current one,
+ ;; is in isearch mode. So end the search in that buffer.
+ (if (and (listp main-event)
+ (setq window (posn-window (event-start main-event)))
+ (windowp window))
+ (save-excursion
+ (set-buffer (window-buffer window))
+ (isearch-done))
+ (isearch-done))))
+ (t;; otherwise nil
+ (isearch-process-search-string key key)))))
(defun isearch-quote-char ()
"Quote special characters for incremental search."
(isearch-search-and-update))
\f
-;;===========================================================
;; Search Ring
(defun isearch-ring-adjust1 (advance)
;; isearch-string stays the same
t)
((or completion ; not nil, must be a string
- (= 0 (length isearch-string))) ; shouldnt have to say this
+ (= 0 (length isearch-string))) ; shouldn't have to say this
(if (equal completion isearch-string) ;; no extension?
(if completion-auto-help
(with-output-to-temp-buffer "*Isearch completions*"
(insert isearch-string))))
\f
-;;;==============================================================
;; The search status stack (and isearch window-local variables, not used).
;; Need a structure for this.
isearch-cmds)))
\f
-;;;==================================================================
;; Message string
(defun isearch-message (&optional c-q-hack ellipsis)
isearch-message
(isearch-message-suffix c-q-hack ellipsis)
)))
- (if c-q-hack m (message "%s" m))))
+ (if c-q-hack
+ m
+ (let ((message-log-max nil))
+ (message "%s" m)))))
(defun isearch-message-prefix (&optional c-q-hack ellipsis nonincremental)
;; If about to search, and previous search regexp was invalid,
;; If currently failing, display no ellipsis.
(or isearch-success (setq ellipsis nil))
(let ((m (concat (if isearch-success "" "failing ")
+ (if (and isearch-wrapped
+ (if isearch-forward
+ (> (point) isearch-opoint)
+ (< (point) isearch-opoint)))
+ "over")
(if isearch-wrapped "wrapped ")
(if isearch-word "word " "")
(if isearch-regexp "regexp " "")
"")))
\f
-;;;========================================================
;;; Searching
(defun isearch-search ()
(t
(if isearch-forward 'search-forward 'search-backward)))
isearch-string nil t))
+ (setq isearch-just-started nil)
(if isearch-success
(setq isearch-other-end
(if isearch-forward (match-beginning 0) (match-end 0)))))
\f
-;;;========================================================
;;; Highlighting
(defvar isearch-overlay nil)
(if isearch-overlay
(delete-overlay isearch-overlay)))
-;;;===========================================================
;;; General utilities
(defun isearch-no-upper-case-p (string regexp-flag)
"Return t if there are no upper case chars in STRING.
-If REGEXP-FLAG is non-nil, disregard letters preceeded by `\\' (but not `\\\\')
+If REGEXP-FLAG is non-nil, disregard letters preceded by `\\' (but not `\\\\')
since they have special meaning in a regexp."
(let ((case-fold-search nil))
(not (string-match (if regexp-flag "\\(^\\|\\\\\\\\\\|[^\\]\\)[A-Z]"
string))))
-;;;=================================================
;; Portability functions to support various Emacs versions.
;; To quiet the byte-compiler.
(isearch-char-to-string c)))
;; General function to unread characters or events.
+;; Also insert them in a keyboard macro being defined.
(defun isearch-unread (&rest char-or-events)
+ (mapcar 'store-kbd-macro-event char-or-events)
(setq unread-command-events
(append char-or-events unread-command-events)))