X-Git-Url: http://git.chise.org/gitweb/?p=chise%2Fxemacs-chise.git.1;a=blobdiff_plain;f=lisp%2Fisearch-mode.el;h=3eef6c9c10efb0364863031f73a2478df7d62e17;hp=cc5b3fa8287ee7539e2f83a2ea5a6f5a896af170;hb=ea1ea793fe6e244ef5555ed983423a204101af13;hpb=399b9f4466f37412410de8ec4a08e3dc5504ad10 diff --git a/lisp/isearch-mode.el b/lisp/isearch-mode.el index cc5b3fa..3eef6c9 100644 --- a/lisp/isearch-mode.el +++ b/lisp/isearch-mode.el @@ -1,6 +1,6 @@ ;;; isearch-mode.el --- Incremental search minor mode. -;; Copyright (C) 1992, 1993, 1997 Free Software Foundation, Inc. +;; Copyright (C) 1992,93,94,95,96,97,98,1999 Free Software Foundation, Inc. ;; Author: Daniel LaLiberte ;; Maintainer: XEmacs Development Team @@ -19,35 +19,29 @@ ;; General Public License for more details. ;; You should have received a copy of the GNU General Public License -;; along with XEmacs; see the file COPYING. If not, write to the +;; along with XEmacs; see the file COPYING. If not, write to the ;; Free Software Foundation, 59 Temple Place - Suite 330, ;; Boston, MA 02111-1307, USA. -;;; Synched up with: Not synched with FSF. +;;; Synched up with: FSF 20.4. ;;; Commentary: -;; LCD Archive Entry: -;; isearch-mode|Daniel LaLiberte|liberte@cs.uiuc.edu -;; |A minor mode replacement for isearch.el. - -;;==================================================================== ;; Instructions -;; Searching with isearch-mode.el should work just like isearch.el, -;; except it is done in a temporary minor mode that terminates when -;; you finish searching. +;; Searching with isearch-mode.el should work just like isearch.el +;; [the one from Emacs 18], except it is done in a temporary minor +;; mode that terminates when you finish searching. -;; Semi-modal searching is supported, using a recursive edit. If -;; isearching is started non-interactively by calling one of the -;; isearch commands (e.g. (isearch-forward), but not like gnus does -;; it: (call-interactively 'isearch-forward)), isearch-mode does not -;; return until the search is completed. You should still be able -;; switch buffers, so be careful not to get things confused. +;; For programmed use of isearch-mode, e.g. calling (isearch-forward), +;; isearch-mode behaves modally and does not return until the search +;; is completed. It uses a recursive-edit to behave this way. In +;; that case, you should still be able switch buffers, so be careful +;; not to get things confused. ;; The key bindings active within isearch-mode are defined below in ;; `isearch-mode-map' which is given bindings close to the default -;; characters of isearch.el for version 19. With `isearch-mode', +;; characters of the original isearch.el. With `isearch-mode', ;; however, you can bind multi-character keys and it should be easier ;; to add new commands. One bug though: keys with meta-prefix cannot ;; be longer than two chars. Also see minibuffer-local-isearch-map @@ -63,16 +57,18 @@ ;; Exiting immediately from isearch uses isearch-edit-string instead ;; of nonincremental-search, if search-nonincremental-instead is non-nil. ;; The name of this option should probably be changed if we decide to -;; keep the behavior. One difference is that isearch-edit-string does -;; not support word search yet; perhaps isearch-mode should support it -;; even for incremental searches, but how? +;; keep the behavior. No point in forcing nonincremental search until +;; the last possible moment. -;;==================================================================== -;;; Change History: +;; TODO +;; - Integrate generalized command history to isearch-edit-string. +;; - Think about incorporating query-replace. +;; - Hooks and options for failed search. + +;;; Change Log: + +;; Changes before those recorded in ChangeLog: -;; Header: /import/kaplan/kaplan/liberte/Isearch/RCS/isearch-mode.el,v 1.3 92/06/29 13:10:08 liberte Exp Locker: liberte -;; Log: isearch-mode.el,v -;; ;; 20-aug-92 Hacked by jwz for Lucid Emacs 19.3. ;; ;; Revision 1.3 92/06/29 13:10:08 liberte @@ -83,7 +79,7 @@ ;; 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. @@ -100,38 +96,20 @@ ;;; Code: -(defgroup isearch nil - "Incremental search" - :prefix "search-" - :group 'matching) - - -(defun isearch-char-to-string (c) - (if (eventp c) - (make-string 1 (event-to-character c nil nil t)) - (make-string 1 c))) - -;(defun isearch-text-char-description (c) -; (isearch-char-to-string c)) - -(define-function 'isearch-text-char-description 'text-char-description) - ;;;========================================================================= ;;; User-accessible variables -(defvar search-last-string "" - "Last string search for by a search command. -This does not include direct calls to the primitive search functions, -and does not include searches that are aborted.") +(defgroup isearch nil + "Incremental search minor mode." + :prefix "search-" + :group 'matching) -(defvar search-last-regexp "" - "Last string searched for by a regexp search command. -This does not include direct calls to the primitive search functions, -and does not include searches that are aborted.") -(defconst search-exit-option t - "Non-nil means random control characters terminate incremental search.") +(defcustom search-exit-option t + "*Non-nil means random control characters terminate incremental search." + :type 'boolean + :group 'isearch) (defcustom search-slow-window-lines 1 "*Number of lines in slow search display windows. @@ -148,16 +126,70 @@ that the search has reached." :type 'integer :group 'isearch) +;; We have `search-caps-disable-folding'. +;(defcustom search-upper-case 'not-yanks +; "*If non-nil, upper case chars disable case fold searching. +;That is, upper and lower case chars must match exactly. +;This applies no matter where the chars come from, but does not +;apply to chars in regexps that are prefixed with `\\'. +;If this value is `not-yanks', yanked text is always downcased." +; :type '(choice (const :tag "off" nil) +; (const not-yanks) +; (other :tag "on" t)) +; :group 'isearch) + (defcustom search-nonincremental-instead t - "*If non-nil, do a nonincremental search instead if exiting immediately." + "*If non-nil, do a nonincremental search instead if exiting immediately. +Actually, `isearch-edit-string' is called to let you enter the search +string, and RET terminates editing and does a nonincremental search." :type 'boolean :group 'isearch) - -(defcustom search-whitespace-regexp "\\(\\s \\|[\n\r]\\)+" + +;; FSF default is "\\s-+", but I think our default is better so I'm +;; leaving it. +(defcustom search-whitespace-regexp "\\(\\s-\\|[\n\r]\\)+" "*If non-nil, regular expression to match a sequence of whitespace chars." :type 'regexp :group 'isearch) +(defcustom search-highlight t + "*Whether incremental search and query-replace should highlight +the text that currently matches the search string." + :type 'boolean + :group 'isearch) + +;; I think the name `search-highlight' makes more sense, both because +;; of consistency with other search-* variables above, and because it +;; also applies to query-replace. +(define-obsolete-variable-alias 'isearch-highlight 'search-highlight) + +(defcustom search-invisible 'open + "If t incremental search can match hidden text. +nil means don't match invisible text. +If the value is `open', if the text matched is made invisible by +an overlay having an `invisible' property and that overlay has a property +`isearch-open-invisible', then incremental search will show the contents. +\(This applies when using `outline.el' and `hideshow.el'.)" + :type '(choice (const :tag "Match hidden text" t) + (const :tag "Open overlays" open) + (const :tag "Don't match hidden text" nil)) + :group 'isearch) + +(defcustom isearch-hide-immediately t + "If non-nil, re-hide an invisible match right away. +This variable makes a difference when `search-invisible' is set to `open'. +It means that after search makes some invisible text visible +to show the match, it makes the text invisible again when the match moves. +Ordinarily the text becomes invisible again at the end of the search." + :type 'boolean + :group 'isearch) + +(defvar isearch-mode-hook nil + "Function(s) to call after starting up an incremental search.") + +(defvar isearch-mode-end-hook nil + "Function(s) to call after terminating an incremental search.") + ;;;================================================================== ;;; Search ring. @@ -175,22 +207,34 @@ that the search has reached." :type 'integer :group 'isearch) +;; The important difference between pre-20.4-merge yank-pointers and +;; current code is that the yank pointers positions used to be +;; preserved across the isearch sessions. I changed this because I +;; think the FSF code is closer to how the feature is supposed to +;; behave (read: to minibuffer histories.) (defvar search-ring-yank-pointer nil - "The tail of the search ring whose car is the last thing searched for.") + "Index in `search-ring' of last string reused. +nil if none yet.") (defvar regexp-search-ring-yank-pointer nil - "The tail of the regular expression search ring whose car is the last -thing searched for.") + "Index in `regexp-search-ring' of last string reused. +nil if none yet.") + +(defcustom search-ring-update nil + "*Non-nil if advancing or retreating in the search ring should cause search. +Default nil means edit the string from the search ring first." + :type 'boolean + :group 'isearch) ;;;==================================================== ;;; Define isearch-mode keymap. -(defvar isearch-mode-map +(defvar isearch-mode-map (let ((map (make-keymap))) (set-keymap-name map 'isearch-mode-map) ;; Bind all printing characters to `isearch-printing-char'. - ;; This isn't normally necessary, but if a printing character were - ;; bound to something other than self-insert-command in global-map, + ;; This isn't normally necessary, but if a printing character were + ;; bound to something other than self-insert-command in global-map, ;; then it would terminate the search and be executed without this. (let ((i 32) (str (make-string 1 0))) @@ -198,7 +242,10 @@ thing searched for.") (aset str 0 i) (define-key map str 'isearch-printing-char) (setq i (1+ i)))) - (define-key map "\t" 'isearch-printing-char) + + ;; Here FSF sets up various kludges to handle local bindings with + ;; meta char prefix keys. We don't need isearch-other-meta-char + ;; because we handle things differently (via pre-command-hook). ;; Several non-printing chars change the searching behavior. ;; @@ -207,26 +254,29 @@ thing searched for.") (define-key map "\C-r" 'isearch-repeat-backward) (define-key map "\C-g" 'isearch-abort) + ;; I wish this worked... + ;(define-key map [escape escape escape] 'isearch-cancel) + (define-key map [(meta escape) escape] 'isearch-cancel) + (define-key map "\C-q" 'isearch-quote-char) (define-key map "\C-m" 'isearch-exit) (define-key map "\C-j" 'isearch-printing-char) (define-key map "\t" 'isearch-printing-char) + ;; I prefer our default. + ;(define-key map " " 'isearch-whitespace-chars) + (define-key map "\M- " 'isearch-whitespace-chars) (define-key map "\C-w" 'isearch-yank-word) (define-key map "\C-y" 'isearch-yank-line) (define-key map "\M-y" 'isearch-yank-kill) - ;; Define keys for regexp chars * ? | + ;; Define keys for regexp chars * ? |. + ;; Nothing special for + because it matches at least once. (define-key map "*" 'isearch-*-char) (define-key map "?" 'isearch-*-char) (define-key map "|" 'isearch-|-char) - ;; Some bindings you may want to put in your isearch-mode-hook. - ;; Suggest some alternates... - ;; (define-key map "\C-t" 'isearch-toggle-regexp) - ;; (define-key map "\C-^" 'isearch-edit-string) - ;; delete and backspace delete backward, f1 is help, and C-h can be either (define-key map 'delete 'isearch-delete-char) (define-key map 'backspace 'isearch-delete-char) @@ -236,15 +286,23 @@ thing searched for.") (define-key map "\M-n" 'isearch-ring-advance) (define-key map "\M-p" 'isearch-ring-retreat) - (define-key map "\M- " 'isearch-whitespace-chars) (define-key map "\M-\t" 'isearch-complete) - (define-key map 'button2 'isearch-yank-x-selection) + ;; I find this binding somewhat unintuitive, because it doesn't + ;; work if the mouse pointer is over the echo area -- it has to be + ;; over the search window. + (define-key map 'button2 'isearch-yank-selection) map) "Keymap for isearch-mode.") -(defvar minibuffer-local-isearch-map +;; Some bindings you may want to put in your isearch-mode-hook. +;; Suggest some alternates... +;; (define-key isearch-mode-map "\C-t" 'isearch-toggle-case-fold) +;; (define-key isearch-mode-map "\C-t" 'isearch-toggle-regexp) +;; (define-key isearch-mode-map "\C-^" 'isearch-edit-string) + +(defvar minibuffer-local-isearch-map (let ((map (make-sparse-keymap))) ;; #### - this should also be minor-mode-ified (set-keymap-parents map (list minibuffer-local-map)) @@ -254,6 +312,8 @@ thing searched for.") (define-key map "\r" 'isearch-nonincremental-exit-minibuffer) (define-key map "\M-n" 'isearch-ring-advance-edit) (define-key map "\M-p" 'isearch-ring-retreat-edit) + (define-key map 'down 'isearch-ring-advance-edit) + (define-key map 'up 'isearch-ring-retreat-edit) (define-key map "\M-\t" 'isearch-complete-edit) (define-key map "\C-s" 'isearch-forward-exit-minibuffer) (define-key map "\C-r" 'isearch-reverse-exit-minibuffer) @@ -262,7 +322,8 @@ thing searched for.") ;;;======================================================== ;; Internal variables declared globally for byte-compiler. -;; These are all bound locally while editing the search string. +;; These are all set with setq while isearching +;; and bound locally while editing the search string. (defvar isearch-forward nil) ; Searching in the forward direction. (defvar isearch-regexp nil) ; Searching for a regexp. @@ -274,6 +335,7 @@ thing searched for.") (defvar isearch-success t) ; Searching is currently successful. (defvar isearch-invalid-regexp nil) ; Regexp not well formed. +(defvar isearch-within-brackets nil) ; Regexp has unclosed [. (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) @@ -282,6 +344,12 @@ thing searched for.") (defvar isearch-case-fold-search nil) +;; Need this for toggling case in isearch-toggle-case-fold. When this +;; is non-nil, the case-sensitiveness of the search is set by the +;; user, and is may no longer be dynamically changed as per +;; search-caps-disable-folding. +(defvar isearch-fixed-case nil) + (defvar isearch-adjusted nil) (defvar isearch-slow-terminal-mode nil) ;;; If t, using a small window. @@ -308,12 +376,9 @@ thing searched for.") ;; New value of isearch-forward after isearch-edit-string. (defvar isearch-new-forward nil) +;; Accumulate here the extents unhidden during searching. +(defvar isearch-unhidden-extents nil) ; in FSF: isearch-opened-overlays -(defvar isearch-mode-hook nil - "Function(s) to call after starting up an incremental search.") - -(defvar isearch-mode-end-hook nil - "Function(s) to call after terminating an incremental search.") ;;;============================================================== ;; Minor-mode-alist changes - kind of redundant with the @@ -321,21 +386,28 @@ thing searched for.") (add-minor-mode 'isearch-mode 'isearch-mode) -(defvar isearch-mode nil) +(defvar isearch-mode nil) ;; Name of the minor mode, if non-nil. (make-variable-buffer-local 'isearch-mode) +;; We bind these in keydefs.el. +;(define-key global-map "\C-s" 'isearch-forward) +;(define-key global-map "\C-r" 'isearch-backward) +;(define-key global-map "\M-\C-s" 'isearch-forward-regexp) +;(define-key global-map "\M-\C-r" 'isearch-backward-regexp) + ;;;=============================================================== ;;; Entry points to isearch-mode. ;;; These four functions should replace those in loaddefs.el -;;; An alternative is to fset isearch-forward etc to isearch-mode, -;;; and look at the last command to set the options accordingly. +;;; An alternative is to defalias isearch-forward etc to isearch-mode, +;;; and look at this-command to set the options accordingly. -(defun isearch-forward (&optional regexp-p) - "Do incremental search forward. +(defun isearch-forward (&optional regexp-p no-recursive-edit) + "\ +Do incremental search forward. With a prefix argument, do an incremental regular expression search instead. \\ As you type characters, they add to the search string and are found. -The following non-printing keys are bound in `isearch-mode-map'. +The following non-printing keys are bound in `isearch-mode-map'. Type \\[isearch-delete-char] to cancel characters from end of search string. Type \\[isearch-exit] to exit, leaving point at location found. @@ -346,6 +418,8 @@ Type \\[isearch-yank-word] to yank word from buffer onto end of search\ string and search for it. Type \\[isearch-yank-line] to yank rest of line onto end of search string\ and search for it. +Type \\[isearch-yank-kill] to yank last killed text onto end of search string\ + and search for it. Type \\[isearch-quote-char] to quote control character to search for it. Type \\[isearch-whitespace-chars] to match all whitespace chars in regexp. \\[isearch-abort] while searching or when search has failed cancels input\ @@ -377,36 +451,38 @@ The bindings, more precisely: ;; Type \\[isearch-edit-string] to edit the search string in the minibuffer. ;; Terminate editing and return to incremental searching with CR. - (interactive "_P") - (isearch-mode t (not (null regexp-p)) nil (not (interactive-p)))) + (interactive "_P\np") + (isearch-mode t (not (null regexp-p)) nil (not no-recursive-edit))) -(defun isearch-forward-regexp () +(defun isearch-forward-regexp (&optional not-regexp no-recursive-edit) "\ Do incremental search forward for regular expression. +With a prefix argument, do a regular string search instead. Like ordinary incremental search except that your input is treated as a regexp. See \\[isearch-forward] for more info." - (interactive "_") - (isearch-mode t t nil (not (interactive-p)))) + (interactive "_P\np") + (isearch-mode t (null not-regexp) nil (not no-recursive-edit))) -(defun isearch-backward (&optional regexp-p) +(defun isearch-backward (&optional regexp-p no-recursive-edit) "\ Do incremental search backward. -With a prefix argument, do an incremental regular expression search instead. +With a prefix argument, do a regular expression search instead. See \\[isearch-forward] for more information." - (interactive "_P") - (isearch-mode nil (not (null regexp-p)) nil (not (interactive-p)))) + (interactive "_P\np") + (isearch-mode nil (not (null regexp-p)) nil (not no-recursive-edit))) -(defun isearch-backward-regexp () +(defun isearch-backward-regexp (&optional not-regexp no-recursive-edit) "\ Do incremental search backward for regular expression. +With a prefix argument, do a regular string search instead. Like ordinary incremental search except that your input is treated as a regexp. See \\[isearch-forward] for more info." - (interactive "_") - (isearch-mode nil t nil (not (interactive-p)))) + (interactive "_P\np") + (isearch-mode nil (null not-regexp) nil (not no-recursive-edit))) -;; This function is way wrong, because you can't scroll the help -;; screen; as soon as you press a key, it's gone. I don't know of a -;; good way to fix it, though. -hniksic +;; The problem here is that you can't scroll the help screen; as soon +;; as you press a key, it's gone. I don't know of a good way to fix +;; it, though. -hniksic (defun isearch-mode-help () (interactive "_") (let ((w (selected-window))) @@ -420,7 +496,9 @@ is treated as a regexp. See \\[isearch-forward] for more info." ;; 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}" (if executing-kbd-macro (setq recursive-edit nil)) @@ -433,6 +511,7 @@ is treated as a regexp. See \\[isearch-forward] for more info." isearch-word word-p isearch-op-fun op-fun isearch-case-fold-search case-fold-search + isearch-fixed-case nil isearch-string "" isearch-message "" isearch-cmds nil @@ -442,6 +521,7 @@ is treated as a regexp. See \\[isearch-forward] for more info." isearch-adjusted nil isearch-yank-flag nil isearch-invalid-regexp nil + isearch-within-brackets nil isearch-slow-terminal-mode (and (<= (device-baud-rate) search-slow-speed) (> (window-height) @@ -451,10 +531,14 @@ is treated as a regexp. See \\[isearch-forward] for more info." isearch-just-started t isearch-opoint (point) + search-ring-yank-pointer nil + regexp-search-ring-yank-pointer nil + isearch-opened-extents nil isearch-window-configuration (current-window-configuration) - ;; #### Should we remember the old value of - ;; overriding-local-map? + ;; #### What we really need is a buffer-local + ;; overriding-local-map. See isearch-pre-command-hook for + ;; more details. overriding-local-map (progn (set-keymap-parents isearch-mode-map (nconc (current-minor-mode-maps) @@ -463,7 +547,6 @@ is treated as a regexp. See \\[isearch-forward] for more info." isearch-mode-map) isearch-selected-frame (selected-frame) - isearch-mode (gettext " Isearch") ) ;; XEmacs change: without clearing the match data, sometimes old values @@ -471,7 +554,10 @@ is treated as a regexp. See \\[isearch-forward] for more info." (store-match-data nil) (add-hook 'pre-command-hook 'isearch-pre-command-hook) - (set-buffer-modified-p (buffer-modified-p)) ; update modeline + + (setq isearch-mode (gettext " Isearch")) + (redraw-modeline) + (isearch-push-state) ) ; inhibit-quit is t before here @@ -479,26 +565,26 @@ is treated as a regexp. See \\[isearch-forward] for more info." (isearch-update) (run-hooks 'isearch-mode-hook) - ;; isearch-mode can be made modal (in the sense of not returning to - ;; the calling function until searching is completed) by entering + ;; 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. (if recursive-edit (let ((isearch-recursive-edit t)) (recursive-edit))) - ) + isearch-success) ;;;==================================================== ;; Some high level utilities. Others below. (defun isearch-update () - ;; Called after each command to update the display. - (if (null unread-command-event) + ;; Called after each command to update the display. + (if (null unread-command-events) (progn (if (not (input-pending-p)) (isearch-message)) (if (and isearch-slow-terminal-mode - (not (or isearch-small-window + (not (or isearch-small-window (pos-visible-in-window-p)))) (let ((found-point (point))) (setq isearch-small-window t) @@ -520,27 +606,24 @@ is treated as a regexp. See \\[isearch-forward] for more info." (if (< isearch-other-end (point)) (isearch-highlight isearch-other-end (point)) (isearch-highlight (point) isearch-other-end)) - (if (extentp isearch-extent) - (isearch-dehighlight nil))) + (isearch-dehighlight)) )) (setq ;; quit-flag nil not for isearch-mode isearch-adjusted nil isearch-yank-flag nil) + (isearch-highlight-all-update) ) -(defun isearch-done () +(defun isearch-done (&optional nopush edit) ;; Called by all commands that terminate isearch-mode. (let ((inhibit-quit t)) ; danger danger! (if (and isearch-buffer (buffer-live-p isearch-buffer)) - (save-excursion - ;; Some loser process filter might have switched the - ;; window's buffer, so be sure to set these variables back - ;; in the buffer we frobbed them in. But only if the buffer - ;; is still alive. - (set-buffer isearch-buffer) - ;; #### Should we restore the old value of - ;; overriding-local-map? + ;; Some loser process filter might have switched the window's + ;; buffer, so be sure to set these variables back in the + ;; buffer we frobbed them in. But only if the buffer is still + ;; alive. + (with-current-buffer isearch-buffer (setq overriding-local-map nil) ;; Use remove-hook instead of just setting it to our saved value ;; in case some process filter has created a buffer and modified @@ -549,8 +632,11 @@ is treated as a regexp. See \\[isearch-forward] for more info." (remove-hook 'pre-command-hook 'isearch-pre-command-hook) (set-keymap-parents isearch-mode-map nil) (setq isearch-mode nil) - (set-buffer-modified-p (buffer-modified-p));; update modeline - (isearch-dehighlight t))) + (redraw-modeline) + (isearch-dehighlight) + (isearch-highlight-all-cleanup) + (isearch-restore-invisible-extents nil nil) + )) ;; it's not critical that this be inside inhibit-quit, but leaving ;; things in small-window-mode would be bad. @@ -568,37 +654,41 @@ is treated as a regexp. See \\[isearch-forward] for more info." ;; Maybe should test difference between and set mark iff > threshold. (if (and (buffer-live-p isearch-buffer) (/= (point isearch-buffer) isearch-opoint)) + ;; #### FSF doesn't do this if the region is active. Should + ;; we do the same? (progn (push-mark isearch-opoint t nil isearch-buffer) (or executing-kbd-macro (> (minibuffer-depth) 0) - (display-message 'command "Mark saved where search started")))) - ) + (display-message 'command "Mark saved where search started"))))) (setq isearch-buffer nil) ) ; inhibit-quit is t before here - (if (> (length isearch-string) 0) + (if (and (> (length isearch-string) 0) (not nopush)) ;; Update the ring data. - (if isearch-regexp - (if (not (setq regexp-search-ring-yank-pointer - (member isearch-string regexp-search-ring))) - (progn - (setq regexp-search-ring - (cons isearch-string regexp-search-ring) - regexp-search-ring-yank-pointer regexp-search-ring) - (if (> (length regexp-search-ring) regexp-search-ring-max) - (setcdr (nthcdr (1- regexp-search-ring-max) regexp-search-ring) - nil)))) - (if (not (setq search-ring-yank-pointer - ;; really need equal test instead of eq. - (member isearch-string search-ring))) - (progn - (setq search-ring (cons isearch-string search-ring) - search-ring-yank-pointer 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) - (if isearch-recursive-edit (exit-recursive-edit))) + + (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)))))) ;;;==================================================== @@ -607,12 +697,16 @@ is treated as a regexp. See \\[isearch-forward] for more info." (defun isearch-exit () "Exit search normally. However, if this is the first command after starting incremental -search and `search-nonincremental-instead' is non-nil, do an -incremental search via `isearch-edit-string'." +search and `search-nonincremental-instead' is non-nil, do a +nonincremental search instead via `isearch-edit-string'." (interactive) - (if (and search-nonincremental-instead + (if (and search-nonincremental-instead (= 0 (length isearch-string))) - (let ((isearch-nonincremental t)) + (let ((isearch-nonincremental t) + ;; Highlighting only gets in the way of nonincremental + ;; search. + (search-highlight nil) + (isearch-highlight-all-matches nil)) (isearch-edit-string)) (isearch-done))) @@ -621,115 +715,112 @@ incremental search via `isearch-edit-string'." "Edit the search string in the minibuffer. The following additional command keys are active while editing. \\ -\\[exit-minibuffer] to exit editing and resume incremental searching. +\\[exit-minibuffer] to resume incremental searching with the edited string. +\\[isearch-nonincremental-exit-minibuffer] to do one nonincremental search. \\[isearch-forward-exit-minibuffer] to resume isearching forward. -\\[isearch-backward-exit-minibuffer] to resume isearching backward. -\\[isearch-ring-advance-edit] to replace the search string with the next\ - item in the search ring. -\\[isearch-ring-retreat-edit] to replace the search string with the next\ - item in the search ring. -\\[isearch-complete-edit] to complete the search string from the search ring." +\\[isearch-reverse-exit-minibuffer] to resume isearching backward. +\\[isearch-ring-advance-edit] to replace the search string with the next item in the search ring. +\\[isearch-ring-retreat-edit] to replace the search string with the previous item in the search ring. +\\[isearch-complete-edit] to complete the search string using the search ring. +\\ +If first char entered is \\[isearch-yank-word], then do word search instead." + ;; This code is very hairy for several reasons, explained in the code. + ;; Mainly, isearch-mode must be terminated while editing and then restarted. + ;; If there were a way to catch any change of buffer from the minibuffer, + ;; this could be simplified greatly. ;; Editing doesn't back up the search point. Should it? (interactive) (condition-case nil - (let ((minibuffer-local-map minibuffer-local-isearch-map) - isearch-nonincremental ; should search nonincrementally? - isearch-new-string - isearch-new-message - (isearch-new-forward isearch-forward) - - ;; Locally bind all isearch global variables to protect them - ;; from recursive isearching. - (isearch-string isearch-string) - (isearch-message isearch-message) - (isearch-forward isearch-forward) ; set by commands below. - - (isearch-forward isearch-forward) - (isearch-regexp isearch-regexp) - (isearch-word isearch-word) - (isearch-op-fun isearch-op-fun) - (isearch-cmds isearch-cmds) - (isearch-success isearch-success) - (isearch-wrapped isearch-wrapped) - (isearch-barrier isearch-barrier) - (isearch-adjusted isearch-adjusted) - (isearch-yank-flag isearch-yank-flag) - (isearch-invalid-regexp isearch-invalid-regexp) - (isearch-other-end isearch-other-end) - (isearch-opoint isearch-opoint) - (isearch-slow-terminal-mode isearch-slow-terminal-mode) - (isearch-small-window isearch-small-window) - (isearch-recursive-edit isearch-recursive-edit) - (isearch-window-configuration (current-window-configuration)) - (isearch-selected-frame (selected-frame)) - ) - ;; Actually terminate isearching until editing is done. - ;; This is so that the user can do anything without failure, - ;; like switch buffers and start another isearch, and return. + (progn + (let ((isearch-nonincremental isearch-nonincremental) + + ;; Locally bind all isearch global variables to protect them + ;; from recursive isearching. + ;; isearch-string -message and -forward are not bound + ;; so they may be changed. Instead, save the values. + (isearch-new-string isearch-string) + (isearch-new-message isearch-message) + (isearch-new-forward isearch-forward) + (isearch-new-word isearch-word) + + (isearch-regexp isearch-regexp) + (isearch-op-fun isearch-op-fun) + (isearch-cmds isearch-cmds) + (isearch-success isearch-success) + (isearch-wrapped isearch-wrapped) + (isearch-barrier isearch-barrier) + (isearch-adjusted isearch-adjusted) + (isearch-fixed-case isearch-fixed-case) + (isearch-yank-flag isearch-yank-flag) + (isearch-invalid-regexp isearch-invalid-regexp) + (isearch-within-brackets isearch-within-brackets) + ;;; 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) + (isearch-opoint isearch-opoint) + (isearch-slow-terminal-mode isearch-slow-terminal-mode) + (isearch-small-window isearch-small-window) + (isearch-recursive-edit isearch-recursive-edit) + (isearch-window-configuration (current-window-configuration)) + (isearch-selected-frame (selected-frame)) + ) + ;; Actually terminate isearching until editing is done. + ;; This is so that the user can do anything without failure, + ;; like switch buffers and start another isearch, and return. ;; (condition-case nil - (isearch-done) + (isearch-done t t) ;;#### What does this mean? There is no such condition! -;; (exit nil)) ; was recursive editing - - (unwind-protect - (let ((prompt (isearch-message-prefix nil t)) - event) - ;; 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 - ;; read a string the normal way. - (let ((cursor-in-echo-area t)) - (display-message 'prompt prompt) - (setq event (next-command-event)) - (if (eq 'isearch-yank-word - (lookup-key isearch-mode-map (vector event))) - (setq isearch-word t) - (setq unread-command-event event))) - (setq isearch-new-string -;; (if (fboundp 'gmhist-old-read-from-minibuffer) -;; ;; Eschew gmhist crockery -;; (gmhist-old-read-from-minibuffer prompt isearch-string) - (read-string - prompt isearch-string - 't ;does its own history (but shouldn't) -;; (if isearch-regexp -;; ;; The search-rings aren't exactly minibuffer -;; ;; histories, but they are close enough -;; (cons 'regexp-search-ring -;; (- (length regexp-search-ring-yank-pointer) -;; (length regexp-search-ring))) -;; (cons 'search-ring -;; (- (length search-ring-yank-pointer) -;; (length search-ring)))) - ) -;; ) - isearch-new-message (mapconcat - 'isearch-text-char-description - isearch-new-string "")) - ) - ;; Always resume isearching by restarting it. - (isearch-mode isearch-forward - isearch-regexp - isearch-op-fun - isearch-recursive-edit - isearch-word) - ) - - ;; Copy new values in outer locals to isearch globals - (setq isearch-string isearch-new-string - isearch-message isearch-new-message - isearch-forward isearch-new-forward) +;; (exit nil)) ; was recursive editing - ;; Empty isearch-string means use default. - (if (= 0 (length isearch-string)) - (setq isearch-string (if isearch-regexp search-last-regexp - search-last-string)) - ;; Set last search string now so it is set even if we fail. - (if search-last-regexp - (setq search-last-regexp isearch-string) - (setq search-last-string isearch-string))) + (unwind-protect + (progn + ;; Fake the prompt message for the sake of + ;; next-command-event below. + (isearch-message) + ;; 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 read a string + ;; the normal way. + (let* ((cursor-in-echo-area t) + (event (next-command-event))) + (if (eq 'isearch-yank-word + (lookup-key isearch-mode-map (vector event))) + (setq isearch-word t;; so message-prefix is right + isearch-new-word t) + (setq unread-command-event event))) + (setq isearch-new-string + (read-from-minibuffer + (isearch-message-prefix nil isearch-nonincremental) + isearch-string + minibuffer-local-isearch-map + nil + 't ;does its own history (but shouldn't) + ) + isearch-new-message (mapconcat + 'isearch-text-char-description + isearch-new-string ""))) + ;; Always resume isearching by restarting it. + (isearch-mode isearch-forward + isearch-regexp + isearch-op-fun + isearch-recursive-edit + isearch-word) + + ;; Copy new values in outer locals to isearch globals + (setq isearch-string isearch-new-string + isearch-message isearch-new-message + isearch-forward isearch-new-forward + isearch-word isearch-new-word)) + + ;; Empty isearch-string means use default. + (if (= 0 (length isearch-string)) + (setq isearch-string (or (car (if isearch-regexp + regexp-search-ring + search-ring)) + "")))) ;; Reinvoke the pending search. (isearch-push-state) @@ -756,25 +847,33 @@ The following additional command keys are active while editing. (setq isearch-new-forward nil) (exit-minibuffer)) +(defun isearch-cancel () + "Terminate the search and go back to the starting point." + (interactive) + (goto-char isearch-opoint) + (isearch-done t) + (signal 'quit '(isearch))) ; and pass on quit signal (defun isearch-abort () - "Quit 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 quiting +;; (ding) signal instead below, if quitting (discard-input) (if isearch-success ;; If search is successful, move back to starting point ;; and really do quit. (progn (goto-char isearch-opoint) - (isearch-done) ; exit isearch + (setq isearch-success nil) + (isearch-done t) ; exit isearch (signal 'quit '(isearch))) ; 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)) @@ -783,35 +882,30 @@ Use `isearch-exit' to quit without signalling." ;; If search string is empty, use last one. (setq isearch-string (or (if isearch-regexp - (if regexp-search-ring-yank-pointer - (car regexp-search-ring-yank-pointer) - (car regexp-search-ring)) - (if search-ring-yank-pointer - (car search-ring-yank-pointer) - (car search-ring))) + (car regexp-search-ring) + (car search-ring)) "") isearch-message (mapconcat 'isearch-text-char-description isearch-string "")) ;; If already have what to search for, repeat it. (or isearch-success - (progn - + (progn (goto-char (if isearch-forward (point-min) (point-max))) (setq isearch-wrapped t)))) ;; C-s in reverse or C-r in forward, change direction. (setq isearch-forward (not isearch-forward))) (setq isearch-barrier (point)) ; For subsequent \| if regexp. + (if (equal isearch-string "") (setq isearch-success t) - (if (and (equal (match-end 0) (match-beginning 0)) - isearch-success + (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)) - ;; nowhere to advance to, so fail (and wrap next time) + ;; If there's nowhere to advance to, fail (and wrap next time). (progn (setq isearch-success nil) (and executing-kbd-macro @@ -821,6 +915,7 @@ Use `isearch-exit' to quit without signalling." (forward-char (if isearch-forward 1 -1)) (isearch-search)) (isearch-search))) + (isearch-push-state) (isearch-update)) @@ -845,18 +940,21 @@ Use `isearch-exit' to quit without signalling." (defun isearch-toggle-case-fold () "Toggle case folding in searching on or off." (interactive) - (setq isearch-case-fold-search - (if isearch-case-fold-search nil 'yes)) - (message "%s%s [case %ssensitive]" - (isearch-message-prefix) - isearch-message - (if isearch-case-fold-search "in" "")) + (setq isearch-case-fold-search (if isearch-case-fold-search nil 'yes) + isearch-fixed-case t) + (lmessage 'progress "%s%s [case %ssensitive]" + (isearch-message-prefix) + isearch-message + (if isearch-case-fold-search "in" "")) (setq isearch-adjusted t) + ;; Update the highlighting here so that it gets done before the + ;; one-second pause. + (isearch-highlight-all-update) (sit-for 1) (isearch-update)) (defun isearch-delete-char () - "Discard last input item and move point back. + "Discard last input item and move point back. If no previous match was done, just beep." (interactive) (if (null (cdr isearch-cmds)) @@ -876,6 +974,7 @@ backwards." (isearch-delete-char) (isearch-mode-help))) +;; This is similar to FSF isearch-yank-string, but more general. (defun isearch-yank (chunk) ;; Helper for isearch-yank-* functions. CHUNK can be a string or a ;; function. @@ -886,7 +985,7 @@ backwards." (goto-char isearch-other-end)) (buffer-substring (point) - (save-excursion + (progn (funcall chunk) (point))))))) ;; if configured so that typing upper-case characters turns off case @@ -904,7 +1003,6 @@ backwards." isearch-yank-flag t)) (isearch-search-and-update)) - (defun isearch-yank-word () "Pull next word from buffer into search string." (interactive) @@ -925,30 +1023,34 @@ backwards." (interactive) (isearch-yank 'forward-sexp)) -(defun isearch-yank-x-selection () - "Pull the current X selection into the search string." +(defun isearch-yank-selection () + "Pull the current selection into the search string." (interactive) - (isearch-yank (x-get-selection))) + (isearch-yank (get-selection))) -(defun isearch-yank-x-clipboard () - "Pull the current X clipboard selection into the search string." +(defun isearch-yank-clipboard () + "Pull the current clipboard selection into the search string." (interactive) - (isearch-yank (x-get-clipboard))) + (isearch-yank (get-clipboard))) (defun isearch-fix-case () - (if (and isearch-case-fold-search search-caps-disable-folding) - (setq isearch-case-fold-search + ;; The commented-out (and ...) form implies that, once + ;; isearch-case-fold-search becomes nil due to a capital letter + ;; typed in, it can never be restored to the original value. In + ;; that case, it's impossible to revert a case-sensitive search back + ;; to case-insensitive. + (if ;(and isearch-case-fold-search search-caps-disable-folding) + (and case-fold-search + ;; Make sure isearch-toggle-case-fold works. + (not isearch-fixed-case) + search-caps-disable-folding) + (setq isearch-case-fold-search (no-upper-case-p isearch-string isearch-regexp))) (setq isearch-mode (if case-fold-search (if isearch-case-fold-search " Isearch" ;As God Intended Mode " ISeARch") ;Warn about evil case via StuDLYcAps. - "Isearch" -; (if isearch-case-fold-search -; " isearch" ;Presumably case-sensitive losers -; ;will notice this 1-char difference. -; " Isearch") ;Weenie mode. - ))) + " Isearch"))) (defun isearch-search-and-update () ;; Do the search and update the display. @@ -972,16 +1074,17 @@ backwards." (regexp-quote isearch-string))))) (error nil)) (or isearch-yank-flag - (<= (match-end 0) + (<= (match-end 0) (min isearch-opoint isearch-barrier)))) - (setq isearch-success t + (setq isearch-success t isearch-invalid-regexp nil + isearch-within-brackets nil isearch-other-end (match-end 0)) ;; Not regexp, not reverse, or no match at point. (if (and isearch-other-end (not isearch-adjusted)) (goto-char (if isearch-forward isearch-other-end - (min isearch-opoint - isearch-barrier + (min isearch-opoint + isearch-barrier (1+ isearch-other-end))))) (isearch-search) )) @@ -991,31 +1094,34 @@ backwards." ;; *, ?, and | chars can make a regexp more liberal. -;; They can make a regexp match sooner -;; or make it succeed instead of failing. +;; They can make a regexp match sooner or make it succeed instead of failing. ;; So go back to place last successful search started ;; or to the last ^S/^R (barrier), whichever is nearer. +;; + needs no special handling because the string must match at least once. (defun isearch-*-char () "Handle * and ? specially in regexps." (interactive) - (if isearch-regexp - - (progn - (setq isearch-adjusted t) - (let ((cs (nth (if isearch-forward - 5 ; isearch-other-end - 2) ; saved (point) - (car (cdr isearch-cmds))))) + (if isearch-regexp + (let ((idx (length isearch-string))) + (while (and (> idx 0) + (eq (aref isearch-string (1- idx)) ?\\)) + (setq idx (1- idx))) + (when (= (mod (- (length isearch-string) idx) 2) 0) + (setq isearch-adjusted t) + ;; Get the isearch-other-end from before the last search. + ;; We want to start from there, + ;; so that we don't retreat farther than that. ;; (car isearch-cmds) is after last search; ;; (car (cdr isearch-cmds)) is from before it. - (setq cs (or cs isearch-barrier)) - (goto-char - (if isearch-forward - (max cs isearch-barrier) - (min cs isearch-barrier)))))) + (let ((cs (nth 5 (car (cdr isearch-cmds))))) + (setq cs (or cs isearch-barrier)) + (goto-char + (if isearch-forward + (max cs isearch-barrier) + (min cs isearch-barrier))))))) (isearch-process-search-char last-command-event)) - + (defun isearch-|-char () @@ -1027,42 +1133,59 @@ backwards." (goto-char isearch-barrier))) (isearch-process-search-char last-command-event)) +;; FSF: +;(defalias 'isearch-other-control-char 'isearch-other-meta-char) +; +;(defun isearch-other-meta-char () +;... +; + (defun isearch-quote-char () "Quote special characters for incremental search." (interactive) + ;; #### Here FSF does some special conversion of chars in 0200-0377 + ;; range. Maybe we should do the same. (isearch-process-search-char (read-quoted-char (isearch-message t)))) - (defun isearch-return-char () "Convert return into newline for incremental search. Obsolete." (interactive) (isearch-process-search-char ?\n)) - (defun isearch-printing-char () - "Any other printing character => add it to the search string and search." + "Add this ordinary printing character to the search string and search." (interactive) - (isearch-process-search-char last-command-event)) - + (let ((event last-command-event)) + ;; If we are called by isearch-whitespace-chars because the + ;; context disallows whitespace search (e.g. within brackets), + ;; replace M-SPC with a space. FSF has similar code. + (and (eq this-command 'isearch-whitespace-chars) + (null (event-to-character event)) + (setq event (character-to-event ?\ ))) + (isearch-process-search-char event))) (defun isearch-whitespace-chars () "Match all whitespace chars, if in regexp mode." + ;; FSF docstring adds: "If you want to search for just a space, type + ;; C-q SPC." But we don't need the addition because we have a + ;; different (better) default for the variable. (interactive) - (if (and isearch-regexp search-whitespace-regexp) - (isearch-process-search-string search-whitespace-regexp " ") - (beep) - (isearch-process-search-char ?\ ) -; (if isearch-word -; nil -; (setq isearch-word t) -; (goto-char isearch-other-end) -; (isearch-process-search-char ?\ )) - )) + (if isearch-regexp + (if (and search-whitespace-regexp (not isearch-within-brackets) + (not isearch-invalid-regexp)) + (isearch-process-search-string search-whitespace-regexp " ") + (isearch-printing-char)) + (progn + ;; This way of doing word search doesn't correctly extend current search. + ;; (setq isearch-word t) + ;; (setq isearch-adjusted t) + ;; (goto-char isearch-barrier) + (isearch-printing-char)))) (defun isearch-process-search-char (char) ;; Append the char to the search string, update the message and re-search. - (isearch-process-search-string (isearch-char-to-string char) + (isearch-process-search-string (isearch-char-to-string char) (isearch-text-char-description char))) (defun isearch-process-search-string (string message) @@ -1074,12 +1197,6 @@ Obsolete." ;;=========================================================== ;; Search Ring -(defcustom search-ring-update nil - "*Non-nil if advancing or retreating in the search ring should cause search. -Default nil means edit the string from the search ring first." - :type 'boolean - :group 'isearch) - (defun isearch-ring-adjust1 (advance) ;; Helper for isearch-ring-adjust (let* ((ring (if isearch-regexp regexp-search-ring search-ring)) @@ -1092,25 +1209,25 @@ Default nil means edit the string from the search ring first." () (set yank-pointer-name (setq yank-pointer - (nthcdr (% (+ (- length (length yank-pointer)) - (if advance (1- length) 1)) - length) ring))) - (setq isearch-string (car yank-pointer) + (mod (+ (or yank-pointer 0) + (if advance -1 1)) + length))) + (setq isearch-string (nth yank-pointer ring) isearch-message (mapconcat 'isearch-text-char-description isearch-string ""))))) (defun isearch-ring-adjust (advance) ;; Helper for isearch-ring-advance and isearch-ring-retreat - (if (cdr isearch-cmds) ;; is there more than one thing on stack? - (isearch-pop-state)) +; (if (cdr isearch-cmds) ;; is there more than one thing on stack? +; (isearch-pop-state)) (isearch-ring-adjust1 advance) - (isearch-push-state) (if search-ring-update (progn (isearch-search) (isearch-update)) (isearch-edit-string) - )) + ) + (isearch-push-state)) (defun isearch-ring-advance () "Advance to the next search string in the ring." @@ -1123,30 +1240,59 @@ Default nil means edit the string from the search ring first." (interactive) (isearch-ring-adjust nil)) -(defun isearch-ring-adjust-edit (advance) - "Use the next or previous search string in the ring while in minibuffer." - (isearch-ring-adjust1 advance) - (erase-buffer) - (insert isearch-string)) +(defun isearch-ring-advance-edit (n) + "Insert the next element of the search history into the minibuffer." + (interactive "p") + (let* ((yank-pointer-name (if isearch-regexp + 'regexp-search-ring-yank-pointer + 'search-ring-yank-pointer)) + (yank-pointer (eval yank-pointer-name)) + (ring (if isearch-regexp regexp-search-ring search-ring)) + (length (length ring))) + (if (zerop length) + () + (set yank-pointer-name + (setq yank-pointer + (mod (- (or yank-pointer 0) n) + length))) -(defun isearch-ring-advance-edit () - (interactive) - (isearch-ring-adjust-edit 'advance)) + (erase-buffer) + (insert (nth yank-pointer ring)) + (goto-char (point-max))))) -(defun isearch-ring-retreat-edit () - "Retreat to the previous search string in the ring while in the minibuffer." - (interactive) - (isearch-ring-adjust-edit nil)) +(defun isearch-ring-retreat-edit (n) + "Inserts the previous element of the search history into the minibuffer." + (interactive "p") + (isearch-ring-advance-edit (- n))) + +;; Merging note: FSF comments out these functions and implements them +;; differently (see above), presumably because the versions below mess +;; with isearch-string, while what we really want them to do is simply +;; to insert the correct string to the minibuffer. + +;;(defun isearch-ring-adjust-edit (advance) +;; "Use the next or previous search string in the ring while in minibuffer." +;; (isearch-ring-adjust1 advance) +;; (erase-buffer) +;; (insert isearch-string)) + +;;(defun isearch-ring-advance-edit () +;; (interactive) +;; (isearch-ring-adjust-edit 'advance)) + +;;(defun isearch-ring-retreat-edit () +;; "Retreat to the previous search string in the ring while in the minibuffer." +;; (interactive) +;; (isearch-ring-adjust-edit nil)) (defun isearch-complete1 () ;; Helper for isearch-complete and isearch-complete-edit - ;; Return t if completion OK, + ;; Return t if completion OK, nil if no completion exists. (let* ((ring (if isearch-regexp regexp-search-ring search-ring)) (alist (mapcar (function (lambda (string) (list string))) ring)) (completion-ignore-case case-fold-search) - (completion (try-completion isearch-string alist)) - ) + (completion (try-completion isearch-string alist))) (cond ((eq completion t) ;; isearch-string stays the same @@ -1154,12 +1300,14 @@ Default nil means edit the string from the search ring first." ((or completion ; not nil, must be a string (= 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*" - (display-completion-list - (all-completions isearch-string alist)))) - (setq isearch-string completion)) - t) + (progn + (if completion-auto-help + (with-output-to-temp-buffer "*Isearch completions*" + (display-completion-list + (all-completions isearch-string alist)))) + t) + (and completion + (setq isearch-string completion)))) (t (temp-minibuffer-message "No completion") nil)))) @@ -1186,32 +1334,61 @@ If there is no completion possible, say so and continue searching." ;;;============================================================== -;; The search status stack (and isearch window-local variables, not used). +;; The search status stack. (defun isearch-top-state () -;; (fetch-window-local-variables) (let ((cmd (car isearch-cmds))) + ;; #### Grr, this is so error-prone. If you add something to + ;; isearch-push-state, don't forget to update this. I thout I'd + ;; make a list of variables, and just do (mapcar* #'set vars + ;; values), but the (point) thing would spoil it, leaving to more + ;; complication. (setq isearch-string (car cmd) isearch-message (car (cdr cmd)) isearch-success (nth 3 cmd) isearch-forward (nth 4 cmd) isearch-other-end (nth 5 cmd) - isearch-invalid-regexp (nth 6 cmd) - isearch-wrapped (nth 7 cmd) - isearch-barrier (nth 8 cmd)) + isearch-word (nth 6 cmd) + isearch-invalid-regexp (nth 7 cmd) + isearch-wrapped (nth 8 cmd) + isearch-barrier (nth 9 cmd) + isearch-within-brackets (nth 10 cmd)) (goto-char (car (cdr (cdr cmd)))))) (defun isearch-pop-state () -;; (fetch-window-local-variables) - (setq isearch-cmds (cdr isearch-cmds)) + (pop isearch-cmds) (isearch-top-state) - ) + + ;; Make sure isearch-case-fold-search gets the correct value. FSF + ;; simply stores isearch-case-fold-search to isearch-cmds. We + ;; should probably do the same. + (isearch-fix-case) + + ;; Here, as well as in isearch-search we must deal with the point + ;; landing at an invisible area which may need unhiding. + (if (or (not (eq search-invisible 'open)) + (not isearch-hide-immediately)) + ;; If search-invisible is t, invisible text is just like any + ;; other text. If it is nil, it is always skipped and we can't + ;; land inside. In both cases, we don't need to do anything. + ;; + ;; Similarly, if isearch-hide-immediately is nil, needn't + ;; re-hide the area here, and neither can we land back into a + ;; hidden one. + nil + (when isearch-other-end + ;; This will unhide the extents. + (isearch-range-invisible (point) isearch-other-end)) + (isearch-restore-invisible-extents (point) + (or isearch-other-end (point))))) (defun isearch-push-state () - (setq isearch-cmds + (setq isearch-cmds (cons (list isearch-string isearch-message (point) - isearch-success isearch-forward isearch-other-end - isearch-invalid-regexp isearch-wrapped isearch-barrier) + isearch-success isearch-forward isearch-other-end + isearch-word + isearch-invalid-regexp isearch-wrapped isearch-barrier + isearch-within-brackets) isearch-cmds))) @@ -1222,27 +1399,41 @@ If there is no completion possible, say so and continue searching." ;; Generate and print the message string. (let ((cursor-in-echo-area ellipsis) (m (concat - (isearch-message-prefix c-q-hack) + (isearch-message-prefix c-q-hack ellipsis isearch-nonincremental) isearch-message - (isearch-message-suffix c-q-hack) + (isearch-message-suffix c-q-hack ellipsis) ))) - (if c-q-hack m (display-message 'progress (format "%s" m))))) + (if c-q-hack + m + (display-message 'progress (format "%s" m))))) -(defun isearch-message-prefix (&optional c-q-hack nonincremental) +(defun isearch-message-prefix (&optional c-q-hack ellipsis nonincremental) ;; If about to search, and previous search regexp was invalid, ;; check that it still is. If it is valid now, ;; let the message we display while searching say that it is valid. - (and isearch-invalid-regexp + (and isearch-invalid-regexp ellipsis (condition-case () (progn (re-search-forward isearch-string (point) t) - (setq isearch-invalid-regexp nil)) + (setq isearch-invalid-regexp nil + isearch-within-brackets nil)) (error nil))) - ;; #### - Yo! Emacs assembles strings all over the place, they can't all - ;; be internationalized in the manner proposed below... Add an explicit - ;; call to `gettext' and have the string snarfer pluck the english - ;; strings out of the comment below. XEmacs is on a purespace diet! -Stig + ;; If currently failing, display no ellipsis. + (or isearch-success (setq ellipsis nil)) + ;; #### - ! Emacs assembles strings all over the place, they can't + ;; all be internationalized in the manner proposed below... Add an + ;; explicit call to `gettext' and have the string snarfer pluck the + ;; english strings out of the comment below. XEmacs is on a + ;; purespace diet! -Stig + + ;; The comment below is dead and buried, but it can be rebuilt if + ;; necessary. -hniksic (let ((m (concat (if isearch-success nil "failing ") - (if isearch-wrapped "wrapped ") + (if (and isearch-wrapped + (if isearch-forward + (> (point) isearch-opoint) + (< (point) isearch-opoint))) + "overwrapped " + (if isearch-wrapped "wrapped ")) (if isearch-word "word ") (if isearch-regexp "regexp ") (if nonincremental "search" "I-search") @@ -1252,14 +1443,12 @@ If there is no completion possible, say so and continue searching." (aset m 0 (upcase (aref m 0))) (gettext m))) -(defun isearch-message-suffix (&optional c-q-hack) +(defun isearch-message-suffix (&optional c-q-hack ellipsis) (concat (if c-q-hack "^Q" "") (if isearch-invalid-regexp (concat " [" isearch-invalid-regexp "]") ""))) -;;;;; #### - yuck...this is soooo lame. Is this really worth 4k of purespace??? -;;; ;;;(let ((i (logior (if isearch-success 32 0) ;;; (if isearch-wrapped 16 0) ;;; (if isearch-word 8 0) @@ -1268,68 +1457,7 @@ If there is no completion possible, say so and continue searching." ;;; (if isearch-forward 1 0)))) ;;; (cond ;;; ((= i 63) (gettext "Wrapped word regexp search: ")) ; 111111 -;;; ((= i 62) (gettext "Wrapped word regexp search backward: ")) ; 111110 -;;; ((= i 61) (gettext "Wrapped word regexp I-search: ")) ; 111101 -;;; ((= i 60) (gettext "Wrapped word regexp I-search backward: ")) ; 111100 -;;; ((= i 59) (gettext "Wrapped word search: ")) ; 111011 -;;; ((= i 58) (gettext "Wrapped word search backward: ")) ; 111010 -;;; ((= i 57) (gettext "Wrapped word I-search: ")) ; 111001 -;;; ((= i 56) (gettext "Wrapped word I-search backward: ")) ; 111000 -;;; ((= i 55) (gettext "Wrapped regexp search: ")) ; 110111 -;;; ((= i 54) (gettext "Wrapped regexp search backward: ")) ; 110110 -;;; ((= i 53) (gettext "Wrapped regexp I-search: ")) ; 110101 -;;; ((= i 52) (gettext "Wrapped regexp I-search backward: ")) ; 110100 -;;; ((= i 51) (gettext "Wrapped search: ")) ; 110011 -;;; ((= i 50) (gettext "Wrapped search backward: ")) ; 110010 -;;; ((= i 49) (gettext "Wrapped I-search: ")) ; 110001 -;;; ((= i 48) (gettext "Wrapped I-search backward: ")) ; 110000 -;;; ((= i 47) (gettext "Word regexp search: ")) ; 101111 -;;; ((= i 46) (gettext "Word regexp search backward: ")) ; 101110 -;;; ((= i 45) (gettext "Word regexp I-search: ")) ; 101101 -;;; ((= i 44) (gettext "Word regexp I-search backward: ")) ; 101100 -;;; ((= i 43) (gettext "Word search: ")) ; 101011 -;;; ((= i 42) (gettext "Word search backward: ")) ; 101010 -;;; ((= i 41) (gettext "Word I-search: ")) ; 101001 -;;; ((= i 40) (gettext "Word I-search backward: ")) ; 101000 -;;; ((= i 39) (gettext "Regexp search: ")) ; 100111 -;;; ((= i 38) (gettext "Regexp search backward: ")) ; 100110 -;;; ((= i 37) (gettext "Regexp I-search: ")) ; 100101 -;;; ((= i 36) (gettext "Regexp I-search backward: ")) ; 100100 -;;; ((= i 35) (gettext "Search: ")) ; 100011 -;;; ((= i 34) (gettext "Search backward: ")) ; 100010 -;;; ((= i 33) (gettext "I-search: ")) ; 100001 -;;; ((= i 32) (gettext "I-search backward: ")) ; 100000 -;;; ((= i 31) (gettext "Failing wrapped word regexp search: ")) ; 011111 -;;; ((= i 30) (gettext "Failing wrapped word regexp search backward: ")) ; 011110 -;;; ((= i 29) (gettext "Failing wrapped word regexp I-search: ")) ; 011101 -;;; ((= i 28) (gettext "Failing wrapped word regexp I-search backward: ")) ; 011100 -;;; ((= i 27) (gettext "Failing wrapped word search: ")) ; 011011 -;;; ((= i 26) (gettext "Failing wrapped word search backward: ")) ; 011010 -;;; ((= i 25) (gettext "Failing wrapped word I-search: ")) ; 011001 -;;; ((= i 24) (gettext "Failing wrapped word I-search backward: ")) ; 011000 -;;; ((= i 23) (gettext "Failing wrapped regexp search: ")) ; 010111 -;;; ((= i 22) (gettext "Failing wrapped regexp search backward: ")) ; 010110 -;;; ((= i 21) (gettext "Failing wrapped regexp I-search: ")) ; 010101 -;;; ((= i 20) (gettext "Failing wrapped regexp I-search backward: ")) ; 010100 -;;; ((= i 19) (gettext "Failing wrapped search: ")) ; 010011 -;;; ((= i 18) (gettext "Failing wrapped search backward: ")) ; 010010 -;;; ((= i 17) (gettext "Failing wrapped I-search: ")) ; 010001 -;;; ((= i 16) (gettext "Failing wrapped I-search backward: ")) ; 010000 -;;; ((= i 15) (gettext "Failing word regexp search: ")) ; 001111 -;;; ((= i 14) (gettext "Failing word regexp search backward: ")) ; 001110 -;;; ((= i 13) (gettext "Failing word regexp I-search: ")) ; 001101 -;;; ((= i 12) (gettext "Failing word regexp I-search backward: ")) ; 001100 -;;; ((= i 11) (gettext "Failing word search: ")) ; 001011 -;;; ((= i 10) (gettext "Failing word search backward: ")) ; 001010 -;;; ((= i 9) (gettext "Failing word I-search: ")) ; 001001 -;;; ((= i 8) (gettext "Failing word I-search backward: ")) ; 001000 -;;; ((= i 7) (gettext "Failing regexp search: ")) ; 000111 -;;; ((= i 6) (gettext "Failing regexp search backward: ")) ; 000110 -;;; ((= i 5) (gettext "Failing regexp I-search: ")) ; 000101 -;;; ((= i 4) (gettext "Failing regexp I-search backward: ")) ; 000100 -;;; ((= i 3) (gettext "Failing search: ")) ; 000011 -;;; ((= i 2) (gettext "Failing search backward: ")) ; 000010 -;;; ((= i 1) (gettext "Failing I-search: ")) ; 000001 +;;; ...and so on, ad nauseam... ;;; ((= i 0) (gettext "Failing I-search backward: ")) ; 000000 ;;; (t (error "Something's rotten"))))) @@ -1343,6 +1471,7 @@ If there is no completion possible, say so and continue searching." (put 'isearch-repeat-backward 'isearch-command t) (put 'isearch-delete-char 'isearch-command t) (put 'isearch-help-or-delete-char 'isearch-command t) +(put 'isearch-cancel 'isearch-command t) (put 'isearch-abort 'isearch-command t) (put 'isearch-quote-char 'isearch-command t) (put 'isearch-exit 'isearch-command t) @@ -1371,6 +1500,8 @@ If there is no completion possible, say so and continue searching." (put 'isearch-forward-exit-minibuffer 'isearch-command t) (put 'isearch-reverse-exit-minibuffer 'isearch-command t) (put 'isearch-nonincremental-exit-minibuffer 'isearch-command t) +(put 'isearch-yank-selection 'isearch-command t) +(put 'isearch-yank-clipboard 'isearch-command t) (put 'isearch-yank-x-selection 'isearch-command t) (put 'isearch-yank-x-clipboard 'isearch-command t) @@ -1408,11 +1539,24 @@ If there is no completion possible, say so and continue searching." ;; (cond ((not (eq (current-buffer) isearch-buffer)) ;; If the buffer (likely meaning "frame") has changed, bail. - ;; This can also happen if a proc filter has popped up another - ;; buffer, which is arguably a bad thing for it to have done, - ;; but the way in which isearch would have hosed you in that - ;; case is unarguably even worse. -jwz - (isearch-done)) + ;; This can happen if the user types something into another + ;; frame. It can also happen if a proc filter has popped up + ;; another buffer, which is arguably a bad thing for it to + ;; have done, but the way in which isearch would have hosed + ;; you in that case is unarguably even worse. -jwz + (isearch-done) + + ;; `this-command' is set according to the value of + ;; `overriding-local-map', set by isearch-mode. This is + ;; wrong because that keymap makes sense only in isearch + ;; buffer. To make sure the right command is called, adjust + ;; `this-command' to the appropriate value, now that + ;; `isearch-done' has set `overriding-local-map' to nil. + + ;; FSF does similar magic in `isearch-other-meta-char', which + ;; is horribly complex. I *hope* what we do works in all + ;; cases. + (setq this-command (key-binding (this-command-keys)))) (t (isearch-maybe-frob-keyboard-macros) (if (and this-command @@ -1453,15 +1597,10 @@ If there is no completion possible, say so and continue searching." ;;;======================================================== ;;; Highlighting -(defcustom isearch-highlight t - "*Whether isearch and query-replace should highlight the text which -currently matches the search-string.") - (defvar isearch-extent nil) -;; this face is initialized by x-faces.el since isearch is preloaded. -;; this face is now created in initialize-faces -;;(make-face 'isearch) +;; this face is initialized by faces.el since isearch is preloaded. +;(make-face 'isearch) (defun isearch-make-extent (begin end) (let ((x (make-extent begin end (current-buffer)))) @@ -1469,28 +1608,28 @@ currently matches the search-string.") ;; highlighted extents we may be passing through, since isearch, being ;; modal, is more interesting (there's nothing they could do with a ;; mouse-highlighted extent while in the midst of a search anyway). - (set-extent-priority x (1+ mouse-highlight-priority)) + (set-extent-priority x (+ mouse-highlight-priority 2)) (set-extent-face x 'isearch) (setq isearch-extent x))) (defun isearch-highlight (begin end) - (if (null isearch-highlight) + (if (null search-highlight) nil ;; make sure isearch-extent is in the current buffer - (or (extentp isearch-extent) + (or (and (extentp isearch-extent) + (extent-live-p isearch-extent)) (isearch-make-extent begin end)) (set-extent-endpoints isearch-extent begin end (current-buffer)))) -(defun isearch-dehighlight (totally) - (if (and isearch-highlight isearch-extent) - (if totally - (let ((inhibit-quit t)) - (if (extentp isearch-extent) - (delete-extent isearch-extent)) - (setq isearch-extent nil)) - (if (extentp isearch-extent) - (detach-extent isearch-extent) - (setq isearch-extent nil))))) +;; This used to have a TOTALLY flag that also deleted the extent. I +;; don't think this is necessary any longer, as isearch-highlight can +;; simply move the extent to another buffer. The IGNORED argument is +;; for the code that calls this function with an argument. --hniksic +(defun isearch-dehighlight (&optional ignored) + (and search-highlight + (extentp isearch-extent) + (extent-live-p isearch-extent) + (detach-extent isearch-extent))) ;;;======================================================== @@ -1502,33 +1641,54 @@ currently matches the search-string.") (isearch-fix-case) (condition-case lossage (let ((inhibit-quit nil) - (case-fold-search isearch-case-fold-search)) + (case-fold-search isearch-case-fold-search) + (retry t)) (if isearch-regexp (setq isearch-invalid-regexp nil)) - (setq isearch-success - (funcall - (cond (isearch-word - (if isearch-forward - 'word-search-forward 'word-search-backward)) - (isearch-regexp - (if isearch-forward - 're-search-forward 're-search-backward)) - (t - (if isearch-forward 'search-forward 'search-backward))) - isearch-string nil t)) + (setq isearch-within-brackets nil) + (while retry + (setq isearch-success + (funcall + (cond (isearch-word + (if isearch-forward + 'word-search-forward 'word-search-backward)) + (isearch-regexp + (if isearch-forward + 're-search-forward 're-search-backward)) + (t + (if isearch-forward 'search-forward 'search-backward))) + isearch-string nil t)) + ;; Clear RETRY unless we matched some invisible text + ;; and we aren't supposed to do that. + (if (or (eq search-invisible t) + (not isearch-success) + (bobp) (eobp) + (= (match-beginning 0) (match-end 0)) + (not (isearch-range-invisible + (match-beginning 0) (match-end 0)))) + (setq retry nil))) (setq isearch-just-started nil) - (if isearch-success - (setq isearch-other-end - (if isearch-forward (match-beginning 0) (match-end 0))))) - - (quit (setq unread-command-event (character-to-event (quit-char))) + (when isearch-success + (setq isearch-other-end + (if isearch-forward (match-beginning 0) (match-end 0))) + (and isearch-hide-immediately + (isearch-restore-invisible-extents (match-beginning 0) + (match-end 0))))) + + (quit (setq unread-command-events (nconc unread-command-events + (character-to-event (quit-char)))) (setq isearch-success nil)) - (invalid-regexp + (invalid-regexp (setq isearch-invalid-regexp (car (cdr lossage))) + (setq isearch-within-brackets (string-match "\\`Unmatched \\[" + isearch-invalid-regexp)) (if (string-match "\\`Premature \\|\\`Unmatched \\|\\`Invalid " isearch-invalid-regexp) - (setq isearch-invalid-regexp (gettext "incomplete input"))))) + (setq isearch-invalid-regexp (gettext "incomplete input")))) + (error + ;; stack overflow in regexp search. + (setq isearch-invalid-regexp (car (cdr lossage))))) (if isearch-success nil @@ -1548,59 +1708,79 @@ currently matches the search-string.") (ding nil 'isearch-failed)) (goto-char (nth 2 (car isearch-cmds))))) -;;;================================================= -;; This is called from incremental-search -;; if the first input character is the exit character. - -;; We store the search string in `isearch-string' -;; which has been bound already by `isearch-search' -;; so that, when we exit, it is copied into `search-last-string'. - +;; Replaced with isearch-edit-string. ;(defun nonincremental-search (forward regexp) -; ;; This may be broken. Anyway, it is replaced by the isearch-edit-string. -; ;; Missing features: word search option, command history. -; (setq isearch-forward forward -; isearch-regexp regexp) -; (let (char function -; inhibit-quit -; (cursor-in-echo-area t)) -; ;; Prompt assuming not word search, -; (setq isearch-message -; (if isearch-regexp -; (if isearch-forward "Regexp search: " -; "Regexp search backward: ") -; (if isearch-forward "Search: " "Search backward: "))) -; (message "%s" isearch-message) -; ;; Read 1 char and switch to word search if it is ^W. -; (setq char (read-char)) -; (if (eq char search-yank-word-char) -; (setq isearch-message (if isearch-forward "Word search: " -; "Word search backward: ")) -; ;; Otherwise let that 1 char be part of the search string. -; (setq unread-command-event (character-to-event char)) -; ) -; (setq function -; (if (eq char search-yank-word-char) -; (if isearch-forward 'word-search-forward 'word-search-backward) -; (if isearch-regexp -; (if isearch-forward 're-search-forward 're-search-backward) -; (if isearch-forward 'search-forward 'search-backward)))) -; ;; Read the search string with corrected prompt. -; (setq isearch-string (read-string isearch-message isearch-string)) -; ;; Empty means use default. -; (if (= 0 (length isearch-string)) -; (setq isearch-string search-last-string) -; ;; Set last search string now so it is set even if we fail. -; (setq search-last-string isearch-string)) -; ;; Since we used the minibuffer, we should be available for redo. -; (setq command-history -; (cons (list function isearch-string) command-history)) -; ;; Go ahead and search. -; (if search-caps-disable-folding -; (setq isearch-case-fold-search -; (no-upper-case-p isearch-string isearch-regexp))) -; (let ((case-fold-search isearch-case-fold-search)) -; (funcall function isearch-string)))) +;... + +(defun isearch-unhide-extent (extent) + ;; Store the values for the `invisible' and `intangible' + ;; properties, and then set them to nil. This way the text hidden + ;; by this extent becomes visible. + (put extent 'isearch-invisible (get extent 'invisible)) + (put extent 'isearch-intangible (get extent 'intangible)) + (put extent 'invisible nil) + (put extent 'intangible nil)) + +(defun isearch-range-invisible (beg end) + "Return t if all the text from BEG to END is invisible. +Before that, if search-invisible is `open', unhide the extents with an +`isearch-open-invisible' property." + ;; isearch-search uses this to skip the extents that are invisible, + ;; but don't have `isearch-open-invisible' set. It is unclear + ;; what's supposed to happen if only a part of [BEG, END) overlaps + ;; the extent. + (let (to-be-unhidden) + (if (map-extents + (lambda (extent ignored) + (if (and (<= (extent-start-position extent) beg) + (>= (extent-end-position extent) end)) + ;; All of the region is covered by the extent. + (if (and (eq search-invisible 'open) + (get extent 'isearch-open-invisible)) + (progn + (push extent to-be-unhidden) + nil) ; keep mapping + ;; We can't or won't unhide this extent, so we must + ;; skip the whole match. We return from map-extents + ;; immediately. + t) + ;; Else, keep looking. + nil)) + nil beg end nil 'all-extents-closed 'invisible) + ;; The whole match must be skipped. Signal it by returning t + ;; to the caller. + t + ;; If any extents need to be unhidden, unhide them. + (mapc #'isearch-unhide-extent to-be-unhidden) + ;; Will leave this assert for some time, to catch bugs. + (assert (null (intersection to-be-unhidden isearch-unhidden-extents))) + (setq isearch-unhidden-extents (nconc to-be-unhidden + isearch-unhidden-extents)) + nil))) + +(defun isearch-restore-extent (extent) + (put extent 'invisible (get extent 'isearch-invisible)) + (put extent 'intangible (get extent 'isearch-intangible)) + (remprop extent 'isearch-invisible) + (remprop extent 'isearch-intangible)) + +;; FSF calls this function `isearch-clean-overlays'. +(defun isearch-restore-invisible-extents (beg end) + (cond + ((null beg) + ;; Delete all -- this is called at the end of isearch. + (mapc #'isearch-restore-extent isearch-unhidden-extents) + (setq isearch-unhidden-extents nil)) + (t + ;; Extents that do not overlap the match area can be safely + ;; restored to their hidden state. + (setq isearch-unhidden-extents + (delete-if (lambda (extent) + (unless (extent-in-region-p extent beg end + 'all-extents-closed) + (isearch-restore-extent extent) + t)) + isearch-unhidden-extents))))) (defun isearch-no-upper-case-p (string) "Return t if there are no upper case chars in string. @@ -1611,6 +1791,18 @@ have special meaning in a regexp." (not (string-match "\\(^\\|[^\\]\\)[A-Z]" string)))) (make-obsolete 'isearch-no-upper-case-p 'no-upper-case-p) +;; Portability functions to support various Emacs versions. + +(defun isearch-char-to-string (c) + (if (eventp c) + (make-string 1 (event-to-character c nil nil t)) + (make-string 1 c))) + +;(defun isearch-text-char-description (c) +; (isearch-char-to-string c)) + +(define-function 'isearch-text-char-description 'text-char-description) + ;; Used by etags.el and info.el (defmacro with-caps-disable-folding (string &rest body) "\ Eval BODY with `case-fold-search' let to nil if STRING contains @@ -1624,4 +1816,204 @@ uppercase letters and `search-caps-disable-folding' is t." (put 'with-caps-disable-folding 'lisp-indent-function 1) (put 'with-caps-disable-folding 'edebug-form-spec '(form body)) + +;;;======================================================== +;;; Advanced highlighting + +;; When active, *every* visible match for the current search string is +;; highlighted: the current one using the normal isearch match color +;; and all the others using the `isearch-secondary' face. The extra +;; highlighting makes it easier to anticipate where the cursor will +;; land each time you press C-s or C-r to repeat a pending search. +;; Only the matches visible at any point are highlighted -- when you +;; move through the buffer, the highlighting is readjusted. + +;; This is based on ideas from Bob Glickstein's `ishl' package. It +;; has been merged with XEmacs by Darryl Okahata, and then completely +;; rewritten by Hrvoje Niksic. + +;; The code makes the following assumptions about the rest of this +;; file, so be careful when modifying it. + +;; * `isearch-highlight-all-update' should get called when the search +;; string changes, or when the search advances. This is done from +;; `isearch-update'. +;; * `isearch-highlight-all-cleanup' should get called when the search +;; is done. This is performed in `isearch-done'. +;; * `isearch-string' is expected to contain the current search string +;; as entered by the user. +;; * `isearch-opoint' is expected to contain the location where the +;; current search began. +;; * the type of the current search is expected to be given by +;; `isearch-word' and `isearch-regexp'. +;; * the variable `isearch-invalid-regexp' is expected to be true iff +;; `isearch-string' is an invalid regexp. + +(defcustom isearch-highlight-all-matches search-highlight + "*Non-nil means highlight all visible matches." + :type 'boolean + :group 'isearch) + +;; We can't create this face here, as isearch.el is preloaded. +;; #### Think up a better name for this! +;(defface isearch-secondary '((t (:foreground "red3"))) +; "Face to use for highlighting all matches." +; :group 'isearch) + +(defvar isearch-highlight-extents nil) +(defvar isearch-window-start nil) +(defvar isearch-window-end nil) +;; We compare isearch-string and isearch-case-fold-search to saved +;; values for better efficiency. +(defvar isearch-highlight-last-string nil) +(defvar isearch-highlight-last-case-fold-search nil) +(defvar isearch-highlight-last-regexp nil) + +(defun isearch-delete-extents-in-range (start end) + ;; Delete all highlighting extents that overlap [START, END). + (setq isearch-highlight-extents + (delete-if (lambda (extent) + (when (extent-in-region-p extent start end) + (delete-extent extent) + t)) + isearch-highlight-extents))) + +(defun isearch-highlight-all-cleanup () + ;; Stop lazily highlighting and remove extra highlighting from + ;; buffer. + (mapc #'delete-extent isearch-highlight-extents) + (setq isearch-highlight-extents nil) + (setq isearch-highlight-all-start nil + isearch-window-end nil + isearch-highlight-last-string nil)) + +(defun isearch-highlight-all-update () + ;; Update the highlighting if necessary. This needs to check if the + ;; search string has changed, or if the window has changed position + ;; in the buffer. + (let ((need-start-over nil)) + ;; NB: we don't check for isearch-success because if the point is + ;; after the last match, the search can be unsuccessful, and yet + ;; there are things to highlight. + (cond ((not isearch-highlight-all-matches)) + ((or (equal isearch-string "") + isearch-invalid-regexp) + (isearch-highlight-all-cleanup)) + ((not (eq isearch-case-fold-search + isearch-highlight-last-case-fold-search)) + ;; This case is usually caused by search string being + ;; changed, which would be caught below, but it can also be + ;; tripped using isearch-toggle-case-fold. + (setq need-start-over t)) + ((not (eq isearch-regexp isearch-highlight-last-regexp)) + ;; Ditto for isearch-toggle-regexp. + (setq need-start-over t)) + ((equal isearch-string isearch-highlight-last-string) + ;; The search string is the same. We need to do something + ;; if our position has changed. + + ;; It would be nice if we didn't have to do this; however, + ;; window-start doesn't support a GUARANTEE flag, so we must + ;; force redisplay to get the correct valye for start and end + ;; of window. + (sit-for 0) + + ;; Check whether our location has changed. + (let ((start (window-start)) + (end (min (window-end) (point-max)))) + (cond ((and (= start isearch-window-start) + (= end isearch-window-end)) + ;; Our position is unchanged -- do nothing. + ) + ((and (> start isearch-window-start) + (> end isearch-window-end) + (<= start isearch-window-end)) + ;; We've migrated downward, but we overlap the old + ;; region. Delete the old non-overlapping extents + ;; and fill in the rest. + (isearch-delete-extents-in-range isearch-window-start start) + (isearch-highlightify-region isearch-window-end end) + (setq isearch-window-start start + isearch-window-end end)) + ((and (<= start isearch-window-start) + (<= end isearch-window-end) + (> end isearch-window-start)) + ;; We've migrated upward, but we overlap the old + ;; region. Delete the old non-overlapping extents + ;; and fill in the rest. + (isearch-delete-extents-in-range + end isearch-window-end) + (isearch-highlightify-region start isearch-window-start) + (setq isearch-window-start start + isearch-window-end end)) + (t + ;; The regions don't overlap, or they overlap in a + ;; weird way. + (setq need-start-over t))))) + (t + ;; The search string has changed. + + ;; If more input is pending, don't start over because + ;; starting over forces redisplay, and that slows down + ;; typing. + (unless (input-pending-p) + (setq need-start-over t)))) + (when need-start-over + ;; Force redisplay before removing the old extents, in order to + ;; avoid flicker. + (sit-for 0) + (isearch-highlight-all-cleanup) + (setq isearch-window-start (window-start) + isearch-window-end (min (window-end) (point-max))) + (isearch-highlightify-region isearch-window-start isearch-window-end)) + + (setq isearch-highlight-last-string isearch-string + isearch-highlight-last-case-fold-search isearch-case-fold-search + isearch-highlight-last-regexp isearch-regexp))) + +(defun isearch-highlight-advance (string forwardp) + ;; Search ahead for the next or previous match. This is the same as + ;; isearch-search, but without the extra baggage. Maybe it should + ;; be in a separate function. + (let ((case-fold-search isearch-case-fold-search)) + (funcall (cond (isearch-word (if forwardp + 'word-search-forward + 'word-search-backward)) + (isearch-regexp (if forwardp + 're-search-forward + 're-search-backward)) + (t (if forwardp + 'search-forward + 'search-backward))) + string nil t))) + +(defun isearch-highlightify-region (start end) + ;; Highlight all occurrences of isearch-string between START and + ;; END. To do this right, we have to search forward as long as + ;; there are matches that overlap [START, END), and then search + ;; backward the same way. + (save-excursion + (goto-char isearch-opoint) + (let ((lastpoint (point))) + (while (and (isearch-highlight-advance isearch-string t) + (/= lastpoint (point)) + (< (match-beginning 0) end)) + (let ((extent (make-extent (match-beginning 0) + (match-end 0)))) + (set-extent-priority extent (1+ mouse-highlight-priority)) + (put extent 'face 'isearch-secondary) + (push extent isearch-highlight-extents)) + (setq lastpoint (point)))) + (goto-char isearch-opoint) + (let ((lastpoint (point))) + (while (and (isearch-highlight-advance isearch-string nil) + (/= lastpoint (point)) + (>= (match-end 0) start)) + (let ((extent (make-extent (match-beginning 0) + (match-end 0)))) + (set-extent-priority extent (1+ mouse-highlight-priority)) + (put extent 'face 'isearch-secondary) + (push extent isearch-highlight-extents)) + (setq lastpoint (point)))))) + ;;; isearch-mode.el ends here