]> code.delx.au - gnu-emacs/blobdiff - lisp/isearch.el
Merge from emacs-24; up to 2012-12-17T11:17:34Z!rgm@gnu.org
[gnu-emacs] / lisp / isearch.el
index 7f68fb4ad32c474c9650e0fac015a34ecf7b982e..31b1918429f123df3aaa32f4a50a3937433c8955 100644 (file)
@@ -1,6 +1,6 @@
 ;;; isearch.el --- incremental search minor mode
 
-;; Copyright (C) 1992-1997, 1999-201 Free Software Foundation, Inc.
+;; Copyright (C) 1992-1997, 1999-2013 Free Software Foundation, Inc.
 
 ;; Author: Daniel LaLiberte <liberte@cs.uiuc.edu>
 ;; Maintainer: FSF
@@ -57,6 +57,7 @@
 
 ;;; Code:
 
+(eval-when-compile (require 'cl-lib))
 \f
 ;; Some additional options and constants.
 
@@ -110,17 +111,24 @@ string, and RET terminates editing and does a nonincremental search."
 
 (defcustom search-whitespace-regexp (purecopy "\\s-+")
   "If non-nil, regular expression to match a sequence of whitespace chars.
-This applies to regular expression incremental search.
-When you put a space or spaces in the incremental regexp, it stands for
-this, unless it is inside of a regexp construct such as [...] or *, + or ?.
+When you enter a space or spaces in the incremental search, it
+will match any sequence matched by this regexp.  As an exception,
+spaces are treated normally in regexp incremental search if they
+occur in a regexp construct like [...] or *, + or ?.
+
+If the value is a string, it applies to both ordinary and
+regexp incremental search.  If the value is nil, or
+`isearch-lax-whitespace' is nil for ordinary incremental search, or
+`isearch-regexp-lax-whitespace' is nil for regexp incremental search,
+then each space you type matches literally, against one space.
+
 You might want to use something like \"[ \\t\\r\\n]+\" instead.
 In the Customization buffer, that is `[' followed by a space,
-a tab, a carriage return (control-M), a newline, and `]+'.
-
-When this is nil, each space you type matches literally, against one space."
-  :type '(choice (const :tag "Find Spaces Literally" nil)
+a tab, a carriage return (control-M), a newline, and `]+'."
+  :type '(choice (const :tag "Match Spaces Literally" nil)
                 regexp)
-  :group 'isearch)
+  :group 'isearch
+  :version "24.3")
 
 (defcustom search-invisible 'open
   "If t incremental search can match hidden text.
@@ -168,7 +176,7 @@ is non-nil if the user quits the search.")
 
 (defvar isearch-message-function nil
   "Function to call to display the search prompt.
-If nil, use `isearch-message'.")
+If nil, use function `isearch-message'.")
 
 (defvar isearch-wrap-function nil
   "Function to call to wrap the search when search is failed.
@@ -366,10 +374,12 @@ but outside of this help window when you type them in Isearch mode,
 they exit Isearch mode before displaying global help."
   isearch-help-map)
 
+(defvar isearch--display-help-action '(nil (inhibit-same-window . t)))
+
 (defun isearch-help-for-help ()
   "Display Isearch help menu."
   (interactive)
-  (let (same-window-buffer-names same-window-regexps)
+  (let ((display-buffer-overriding-action isearch--display-help-action))
     (isearch-help-for-help-internal))
   (isearch-update))
 
@@ -377,7 +387,7 @@ they exit Isearch mode before displaying global help."
   "Show a list of all keys defined in Isearch mode, and their definitions.
 This is like `describe-bindings', but displays only Isearch keys."
   (interactive)
-  (let (same-window-buffer-names same-window-regexps)
+  (let ((display-buffer-overriding-action isearch--display-help-action))
     (with-help-window "*Help*"
       (with-current-buffer standard-output
        (princ "Isearch Mode Bindings:\n")
@@ -386,14 +396,14 @@ This is like `describe-bindings', but displays only Isearch keys."
 (defun isearch-describe-key ()
   "Display documentation of the function invoked by isearch key."
   (interactive)
-  (let (same-window-buffer-names same-window-regexps)
+  (let ((display-buffer-overriding-action isearch--display-help-action))
     (call-interactively 'describe-key))
   (isearch-update))
 
 (defun isearch-describe-mode ()
   "Display documentation of Isearch mode."
   (interactive)
-  (let (same-window-buffer-names same-window-regexps)
+  (let ((display-buffer-overriding-action isearch--display-help-action))
     (describe-function 'isearch-forward))
   (isearch-update))
 
@@ -413,13 +423,6 @@ This is like `describe-bindings', but displays only Isearch keys."
     ;; Make function keys, etc, which aren't bound to a scrolling-function
     ;; exit the search.
     (define-key map [t] 'isearch-other-control-char)
-    ;; Control chars, by default, end isearch mode transparently.
-    ;; We need these explicit definitions because, in a dense keymap,
-    ;; the binding for t does not affect characters.
-    ;; We use a dense keymap to save space.
-    (while (< i ?\s)
-      (define-key map (make-string 1 i) 'isearch-other-control-char)
-      (setq i (1+ i)))
 
     ;; Single-byte printing chars extend the search string by default.
     (setq i ?\s)
@@ -434,8 +437,8 @@ This is like `describe-bindings', but displays only Isearch keys."
     ;; default local key binding for any key not otherwise bound.
     (let ((meta-map (make-sparse-keymap)))
       (define-key map (char-to-string meta-prefix-char) meta-map)
-      (define-key map [escape] meta-map))
-    (define-key map (vector meta-prefix-char t) 'isearch-other-meta-char)
+      (define-key map [escape] meta-map)
+      (define-key meta-map [t] 'isearch-other-meta-char))
 
     ;; Several non-printing chars change the searching behavior.
     (define-key map "\C-s" 'isearch-repeat-forward)
@@ -501,14 +504,24 @@ This is like `describe-bindings', but displays only Isearch keys."
     (define-key map "\M-r" 'isearch-toggle-regexp)
     (define-key map "\M-e" 'isearch-edit-string)
 
+    (define-key map "\M-sc" 'isearch-toggle-case-fold)
     (define-key map "\M-sr" 'isearch-toggle-regexp)
     (define-key map "\M-sw" 'isearch-toggle-word)
+    (define-key map "\M-s_" 'isearch-toggle-symbol)
+    (define-key map "\M-s " 'isearch-toggle-lax-whitespace)
 
     (define-key map [?\M-%] 'isearch-query-replace)
     (define-key map [?\C-\M-%] 'isearch-query-replace-regexp)
     (define-key map "\M-so" 'isearch-occur)
     (define-key map "\M-shr" 'isearch-highlight-regexp)
 
+    ;; The key translations defined in the C-x 8 prefix should insert
+    ;; characters into the search string.  See iso-transl.el.
+    (define-key map "\C-x" nil)
+    (define-key map [?\C-x t] 'isearch-other-control-char)
+    (define-key map "\C-x8" nil)
+    (define-key map "\C-x8\r" 'isearch-insert-char-by-name)
+
     map)
   "Keymap for `isearch-mode'.")
 
@@ -530,8 +543,29 @@ This is like `describe-bindings', but displays only Isearch keys."
 
 (defvar isearch-forward nil)   ; Searching in the forward direction.
 (defvar isearch-regexp nil)    ; Searching for a regexp.
-(defvar isearch-word nil)      ; Searching for words.
-(defvar isearch-hidden nil) ; Non-nil if the string exists but is invisible.
+(defvar isearch-word nil
+  "Regexp-based search mode for words/symbols.
+If t, do incremental search for a sequence of words, ignoring punctuation.
+If the value is a function (e.g. `isearch-symbol-regexp'), it is called to
+convert the search string to a regexp used by regexp search functions.
+The property `isearch-message-prefix' put on this function specifies the
+prefix string displayed in the search message.")
+
+(defvar isearch-lax-whitespace t
+  "If non-nil, a space will match a sequence of whitespace chars.
+When you enter a space or spaces in ordinary incremental search, it
+will match any sequence matched by the regexp defined by the variable
+`search-whitespace-regexp'.  If the value is nil, each space you type
+matches literally, against one space.  You can toggle the value of this
+variable by the command `isearch-toggle-lax-whitespace'.")
+
+(defvar isearch-regexp-lax-whitespace nil
+  "If non-nil, a space will match a sequence of whitespace chars.
+When you enter a space or spaces in regexp incremental search, it
+will match any sequence matched by the regexp defined by the variable
+`search-whitespace-regexp'.  If the value is nil, each space you type
+matches literally, against one space.  You can toggle the value of this
+variable by the command `isearch-toggle-lax-whitespace'.")
 
 (defvar isearch-cmds nil
   "Stack of search status sets.
@@ -592,6 +626,9 @@ Each set is a vector of the form:
 ;; Accumulate here the overlays opened during searching.
 (defvar isearch-opened-overlays nil)
 
+;; Non-nil if the string exists but is invisible.
+(defvar isearch-hidden nil)
+
 ;; The value of input-method-function when isearch is invoked.
 (defvar isearch-input-method-function nil)
 
@@ -614,6 +651,7 @@ Each set is a vector of the form:
 (define-key global-map "\C-r" 'isearch-backward)
 (define-key esc-map "\C-r" 'isearch-backward-regexp)
 (define-key search-map "w" 'isearch-forward-word)
+(define-key search-map "_" 'isearch-forward-symbol)
 
 ;; Entry points to isearch-mode.
 
@@ -653,6 +691,13 @@ If you try to exit with the search string still empty, it invokes
 Type \\[isearch-toggle-case-fold] to toggle search case-sensitivity.
 Type \\[isearch-toggle-regexp] to toggle regular-expression mode.
 Type \\[isearch-toggle-word] to toggle word mode.
+Type \\[isearch-toggle-symbol] to toggle symbol mode.
+
+Type \\[isearch-toggle-lax-whitespace] to toggle whitespace matching.
+In incremental searches, a space or spaces normally matches any whitespace
+defined by the variable `search-whitespace-regexp'; see also the variables
+`isearch-lax-whitespace' and `isearch-regexp-lax-whitespace'.
+
 Type \\[isearch-edit-string] to edit the search string in the minibuffer.
 
 Also supported is a search ring of the previous 16 search strings.
@@ -697,22 +742,20 @@ the calling function until the search is done."
   (isearch-mode t (not (null regexp-p)) nil (not no-recursive-edit)))
 
 (defun isearch-forward-regexp (&optional not-regexp no-recursive-edit)
-  "\
-Do incremental search forward for regular expression.
+  "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 the command `isearch-forward' for more information.
 
-In regexp incremental searches, a space or spaces normally matches
-any whitespace (the variable `search-whitespace-regexp' controls
-precisely what that means).  If you want to search for a literal space
-and nothing else, enter C-q SPC."
+In incremental searches, a space or spaces normally matches any
+whitespace defined by the variable `search-whitespace-regexp'.
+To search for a literal space and nothing else, enter C-q SPC.
+To toggle whitespace matching, use `isearch-toggle-lax-whitespace'."
   (interactive "P\np")
   (isearch-mode t (null not-regexp) nil (not no-recursive-edit)))
 
 (defun isearch-forward-word (&optional not-word no-recursive-edit)
-  "\
-Do incremental search forward for a sequence of words.
+  "Do incremental search forward for a sequence of words.
 With a prefix argument, do a regular string search instead.
 Like ordinary incremental search except that your input is treated
 as a sequence of words without regard to how the words are separated.
@@ -720,17 +763,24 @@ See the command `isearch-forward' for more information."
   (interactive "P\np")
   (isearch-mode t nil nil (not no-recursive-edit) (null not-word)))
 
+(defun isearch-forward-symbol (&optional not-symbol no-recursive-edit)
+  "Do incremental search forward for a symbol.
+The prefix argument is currently unused.
+Like ordinary incremental search except that your input is treated
+as a symbol surrounded by symbol boundary constructs \\_< and \\_>.
+See the command `isearch-forward' for more information."
+  (interactive "P\np")
+  (isearch-mode t nil nil (not no-recursive-edit) 'isearch-symbol-regexp))
+
 (defun isearch-backward (&optional regexp-p no-recursive-edit)
-  "\
-Do incremental search backward.
+  "Do incremental search backward.
 With a prefix argument, do a regular expression search instead.
 See the command `isearch-forward' for more information."
   (interactive "P\np")
   (isearch-mode nil (not (null regexp-p)) nil (not no-recursive-edit)))
 
 (defun isearch-backward-regexp (&optional not-regexp no-recursive-edit)
-  "\
-Do incremental search backward for regular expression.
+  "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 the command `isearch-forward' for more information."
@@ -747,14 +797,14 @@ as a regexp.  See the command `isearch-forward' for more information."
 ;;  "List of commands for which isearch-mode does not recursive-edit.")
 
 
-(defun isearch-mode (forward &optional regexp op-fun recursive-edit word-p)
+(defun isearch-mode (forward &optional regexp op-fun recursive-edit word)
   "Start Isearch minor mode.
 It is called by the function `isearch-forward' and other related functions."
 
   ;; Initialize global vars.
   (setq isearch-forward forward
        isearch-regexp regexp
-       isearch-word word-p
+       isearch-word word
        isearch-op-fun op-fun
        isearch-last-case-fold-search isearch-case-fold-search
        isearch-case-fold-search case-fold-search
@@ -872,8 +922,7 @@ The last thing it does is to run `isearch-update-post-hook'."
             (if (< isearch-other-end (point)) ; isearch-forward?
                 (isearch-highlight isearch-other-end (point))
               (isearch-highlight (point) isearch-other-end))
-          (isearch-dehighlight))
-        ))
+          (isearch-dehighlight))))
   (setq ;; quit-flag nil  not for isearch-mode
    isearch-adjusted nil
    isearch-yank-flag nil)
@@ -936,9 +985,10 @@ NOPUSH is t and EDIT is t."
        (before (if (bobp) nil
                  (get-text-property (1- (point)) 'intangible))))
     (when (and before after (eq before after))
-      (if isearch-forward
-         (goto-char (next-single-property-change (point) 'intangible))
-       (goto-char (previous-single-property-change (point) 'intangible)))))
+      (goto-char
+       (if isearch-forward
+           (next-single-property-change (point) 'intangible)
+         (previous-single-property-change (point) 'intangible)))))
 
   (if (and (> (length isearch-string) 0) (not nopush))
       ;; Update the ring data.
@@ -978,73 +1028,58 @@ REGEXP if non-nil says use the regexp search ring."
 \f
 ;; The search status structure and stack.
 
-(defsubst isearch-string-state (frame)
-  "Return the search string in FRAME."
-  (aref frame 0))
-(defsubst isearch-message-state (frame)
-  "Return the search string to display to the user in FRAME."
-  (aref frame 1))
-(defsubst isearch-point-state (frame)
-  "Return the point in FRAME."
-  (aref frame 2))
-(defsubst isearch-success-state (frame)
-  "Return the success flag in FRAME."
-  (aref frame 3))
-(defsubst isearch-forward-state (frame)
-  "Return the searching-forward flag in FRAME."
-  (aref frame 4))
-(defsubst isearch-other-end-state (frame)
-  "Return the other end of the match in FRAME."
-  (aref frame 5))
-(defsubst isearch-word-state (frame)
-  "Return the search-by-word flag in FRAME."
-  (aref frame 6))
-(defsubst isearch-error-state (frame)
-  "Return the regexp error message in FRAME, or nil if its regexp is valid."
-  (aref frame 7))
-(defsubst isearch-wrapped-state (frame)
-  "Return the search-wrapped flag in FRAME."
-  (aref frame 8))
-(defsubst isearch-barrier-state (frame)
-  "Return the barrier value in FRAME."
-  (aref frame 9))
-(defsubst isearch-case-fold-search-state (frame)
-  "Return the case-folding flag in FRAME."
-  (aref frame 10))
-(defsubst isearch-pop-fun-state (frame)
-  "Return the function restoring the mode-specific Isearch state in FRAME."
-  (aref frame 11))
-
-(defun isearch-top-state ()
-  (let ((cmd (car isearch-cmds)))
-    (setq isearch-string (isearch-string-state cmd)
-         isearch-message (isearch-message-state cmd)
-         isearch-success (isearch-success-state cmd)
-         isearch-forward (isearch-forward-state cmd)
-         isearch-other-end (isearch-other-end-state cmd)
-         isearch-word (isearch-word-state cmd)
-         isearch-error (isearch-error-state cmd)
-         isearch-wrapped (isearch-wrapped-state cmd)
-         isearch-barrier (isearch-barrier-state cmd)
-         isearch-case-fold-search (isearch-case-fold-search-state cmd))
-    (if (functionp (isearch-pop-fun-state cmd))
-       (funcall (isearch-pop-fun-state cmd) cmd))
-    (goto-char (isearch-point-state cmd))))
+(cl-defstruct (isearch--state
+               (:constructor nil)
+               (:copier nil)
+               (:constructor isearch--get-state
+                (&aux
+                 (string isearch-string)
+                 (message isearch-message)
+                 (point (point))
+                 (success isearch-success)
+                 (forward isearch-forward)
+                 (other-end isearch-other-end)
+                 (word isearch-word)
+                 (error isearch-error)
+                 (wrapped isearch-wrapped)
+                 (barrier isearch-barrier)
+                 (case-fold-search isearch-case-fold-search)
+                 (pop-fun (if isearch-push-state-function
+                              (funcall isearch-push-state-function))))))
+  (string :read-only t)
+  (message :read-only t)
+  (point :read-only t)
+  (success :read-only t)
+  (forward :read-only t)
+  (other-end :read-only t)
+  (word :read-only t)
+  (error :read-only t)
+  (wrapped :read-only t)
+  (barrier :read-only t)
+  (case-fold-search :read-only t)
+  (pop-fun :read-only t))
+
+(defun isearch--set-state (cmd)
+  (setq isearch-string (isearch--state-string cmd)
+       isearch-message (isearch--state-message cmd)
+       isearch-success (isearch--state-success cmd)
+       isearch-forward (isearch--state-forward cmd)
+       isearch-other-end (isearch--state-other-end cmd)
+       isearch-word (isearch--state-word cmd)
+       isearch-error (isearch--state-error cmd)
+       isearch-wrapped (isearch--state-wrapped cmd)
+       isearch-barrier (isearch--state-barrier cmd)
+       isearch-case-fold-search (isearch--state-case-fold-search cmd))
+  (if (functionp (isearch--state-pop-fun cmd))
+      (funcall (isearch--state-pop-fun cmd) cmd))
+  (goto-char (isearch--state-point cmd)))
 
 (defun isearch-pop-state ()
   (setq isearch-cmds (cdr isearch-cmds))
-  (isearch-top-state))
+  (isearch--set-state (car isearch-cmds)))
 
 (defun isearch-push-state ()
-  (setq isearch-cmds
-       (cons (vector isearch-string isearch-message (point)
-                     isearch-success isearch-forward isearch-other-end
-                     isearch-word
-                     isearch-error isearch-wrapped isearch-barrier
-                     isearch-case-fold-search
-                     (if isearch-push-state-function
-                         (funcall isearch-push-state-function)))
-             isearch-cmds)))
+  (push (isearch--get-state) isearch-cmds))
 
 \f
 ;; Commands active while inside of the isearch minor mode.
@@ -1066,16 +1101,16 @@ nonincremental search instead via `isearch-edit-string'."
 
 (defun isearch-fail-pos (&optional msg)
   "Return position of first mismatch in search string, or nil if none.
-If MSG is non-nil, use `isearch-message', otherwise `isearch-string'."
+If MSG is non-nil, use variable `isearch-message', otherwise `isearch-string'."
   (let ((cmds isearch-cmds)
        (curr-msg (if msg isearch-message isearch-string))
        succ-msg)
     (when (or (not isearch-success) isearch-error)
-      (while (or (not (isearch-success-state (car cmds)))
-                 (isearch-error-state (car cmds)))
+      (while (or (not (isearch--state-success (car cmds)))
+                 (isearch--state-error (car cmds)))
         (pop cmds))
-      (setq succ-msg (and cmds (if msg (isearch-message-state (car cmds))
-                                (isearch-string-state (car cmds)))))
+      (setq succ-msg (and cmds (if msg (isearch--state-message (car cmds))
+                                (isearch--state-string (car cmds)))))
       (if (and (stringp succ-msg)
               (< (length succ-msg) (length curr-msg))
               (equal succ-msg
@@ -1083,23 +1118,17 @@ If MSG is non-nil, use `isearch-message', otherwise `isearch-string'."
          (length succ-msg)
        0))))
 
-(defun isearch-edit-string ()
-  "Edit the search string in the minibuffer.
-The following additional command keys are active while editing.
-\\<minibuffer-local-isearch-map>
-\\[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-reverse-exit-minibuffer] to resume isearching backward.
-\\[isearch-complete-edit] to complete the search string using the search ring."
-
+(defmacro with-isearch-suspended (&rest body)
+  "Exit Isearch mode, run BODY, and reinvoke the pending search.
+You can update the global isearch variables by setting new values to
+`isearch-new-string', `isearch-new-message', `isearch-new-forward',
+`isearch-new-word', `isearch-new-case-fold'."
   ;; 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
+  `(condition-case nil
       (progn
        (let ((isearch-nonincremental isearch-nonincremental)
 
@@ -1162,29 +1191,7 @@ The following additional command keys are active while editing.
          (setq old-point (point) old-other-end isearch-other-end)
 
          (unwind-protect
-             (let* ((message-log-max nil)
-                    ;; Don't add a new search string to the search ring here
-                    ;; in `read-from-minibuffer'. It should be added only
-                    ;; by `isearch-update-ring' called from `isearch-done'.
-                    (history-add-new-input nil)
-                    ;; Binding minibuffer-history-symbol to nil is a work-around
-                    ;; for some incompatibility with gmhist.
-                    (minibuffer-history-symbol))
-               (setq isearch-new-string
-                      (read-from-minibuffer
-                       (isearch-message-prefix nil nil isearch-nonincremental)
-                      (cons isearch-string (1+ (or (isearch-fail-pos)
-                                                   (length isearch-string))))
-                       minibuffer-local-isearch-map nil
-                       (if isearch-regexp
-                          (cons 'regexp-search-ring
-                                (1+ (or regexp-search-ring-yank-pointer -1)))
-                        (cons 'search-ring
-                              (1+ (or search-ring-yank-pointer -1))))
-                       nil t)
-                     isearch-new-message
-                     (mapconcat 'isearch-text-char-description
-                                isearch-new-string "")))
+             (progn ,@body)
 
            ;; Set point at the start (end) of old match if forward (backward),
            ;; so after exiting minibuffer isearch resumes at the start (end)
@@ -1243,6 +1250,41 @@ The following additional command keys are active while editing.
      (isearch-abort)  ;; outside of let to restore outside global values
      )))
 
+(defun isearch-edit-string ()
+  "Edit the search string in the minibuffer.
+The following additional command keys are active while editing.
+\\<minibuffer-local-isearch-map>
+\\[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-reverse-exit-minibuffer] to resume isearching backward.
+\\[isearch-complete-edit] to complete the search string using the search ring."
+  (interactive)
+  (with-isearch-suspended
+   (let* ((message-log-max nil)
+         ;; Don't add a new search string to the search ring here
+         ;; in `read-from-minibuffer'. It should be added only
+         ;; by `isearch-update-ring' called from `isearch-done'.
+         (history-add-new-input nil)
+         ;; Binding minibuffer-history-symbol to nil is a work-around
+         ;; for some incompatibility with gmhist.
+         (minibuffer-history-symbol))
+     (setq isearch-new-string
+          (read-from-minibuffer
+           (isearch-message-prefix nil isearch-nonincremental)
+           (cons isearch-string (1+ (or (isearch-fail-pos)
+                                        (length isearch-string))))
+           minibuffer-local-isearch-map nil
+           (if isearch-regexp
+               (cons 'regexp-search-ring
+                     (1+ (or regexp-search-ring-yank-pointer -1)))
+             (cons 'search-ring
+                   (1+ (or search-ring-yank-pointer -1))))
+           nil t)
+          isearch-new-message
+          (mapconcat 'isearch-text-char-description
+                     isearch-new-string "")))))
+
 (defun isearch-nonincremental-exit-minibuffer ()
   (interactive)
   (setq isearch-nonincremental t)
@@ -1265,18 +1307,18 @@ The following additional command keys are active while editing.
       ;; For defined push-state function, restore the first state.
       ;; This calls pop-state function and restores original point.
       (let ((isearch-cmds (last isearch-cmds)))
-       (isearch-top-state))
+       (isearch--set-state (car isearch-cmds)))
     (goto-char isearch-opoint))
-  (isearch-done t)                      ; exit isearch
+  (isearch-done t)                      ; Exit isearch..
   (isearch-clean-overlays)
-  (signal 'quit nil))                   ; and pass on quit signal
+  (signal 'quit nil))                   ; ..and pass on quit signal.
 
 (defun isearch-abort ()
   "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 signaling."
   (interactive)
-;;  (ding)  signal instead below, if quitting
+  ;; (ding)  signal instead below, if quitting
   (discard-input)
   (if (and isearch-success (not isearch-error))
       ;; If search is successful and has no incomplete regexp,
@@ -1299,9 +1341,7 @@ Use `isearch-exit' to quit without signaling."
          (if (null (if isearch-regexp regexp-search-ring search-ring))
              (setq isearch-error "No previous search string")
            (setq isearch-string
-                 (if isearch-regexp
-                     (car regexp-search-ring)
-                   (car search-ring))
+                 (car (if isearch-regexp regexp-search-ring search-ring))
                  isearch-message
                  (mapconcat 'isearch-text-char-description
                             isearch-string "")
@@ -1362,11 +1402,44 @@ Use `isearch-exit' to quit without signaling."
 
 (defun isearch-toggle-word ()
   "Toggle word searching on or off."
+  ;; The status stack is left unchanged.
   (interactive)
   (setq isearch-word (not isearch-word))
+  (if isearch-word (setq isearch-regexp nil))
+  (setq isearch-success t isearch-adjusted t)
+  (isearch-update))
+
+(defun isearch-toggle-symbol ()
+  "Toggle symbol searching on or off."
+  (interactive)
+  (setq isearch-word (unless (eq isearch-word 'isearch-symbol-regexp)
+                      'isearch-symbol-regexp))
+  (if isearch-word (setq isearch-regexp nil))
   (setq isearch-success t isearch-adjusted t)
   (isearch-update))
 
+(defun isearch-toggle-lax-whitespace ()
+  "Toggle whitespace matching in searching on or off.
+In ordinary search, toggles the value of the variable
+`isearch-lax-whitespace'.  In regexp search, toggles the
+value of the variable `isearch-regexp-lax-whitespace'."
+  (interactive)
+  (if isearch-regexp
+      (setq isearch-regexp-lax-whitespace (not isearch-regexp-lax-whitespace))
+    (setq isearch-lax-whitespace (not isearch-lax-whitespace)))
+  (let ((message-log-max nil))
+    (message "%s%s [%s]"
+            (isearch-message-prefix nil isearch-nonincremental)
+            isearch-message
+            (if (if isearch-regexp
+                    isearch-regexp-lax-whitespace
+                  isearch-lax-whitespace)
+                "match spaces loosely"
+              "match spaces literally")))
+  (setq isearch-success t isearch-adjusted t)
+  (sit-for 1)
+  (isearch-update))
+
 (defun isearch-toggle-case-fold ()
   "Toggle case folding in searching on or off."
   (interactive)
@@ -1374,7 +1447,7 @@ Use `isearch-exit' to quit without signaling."
        (if isearch-case-fold-search nil 'yes))
   (let ((message-log-max nil))
     (message "%s%s [case %ssensitive]"
-            (isearch-message-prefix nil nil isearch-nonincremental)
+            (isearch-message-prefix nil isearch-nonincremental)
             isearch-message
             (if isearch-case-fold-search "in" "")))
   (setq isearch-success t isearch-adjusted t)
@@ -1468,6 +1541,38 @@ to punctuation."
   (interactive "sWord search: ")
   (re-search-forward (word-search-regexp string t) bound noerror count))
 
+;; Symbol search
+
+(defun isearch-symbol-regexp (string &optional lax)
+  "Return a regexp which matches STRING as a symbol.
+Creates a regexp where STRING is surrounded by symbol delimiters \\_< and \\_>.
+If LAX is non-nil, the end of the string need not match a symbol boundary."
+  (concat "\\_<" (regexp-quote string) (unless lax "\\_>")))
+
+(put 'isearch-symbol-regexp 'isearch-message-prefix "symbol ")
+
+;; Search with lax whitespace
+
+(defun search-forward-lax-whitespace (string &optional bound noerror count)
+  "Search forward for STRING, matching a sequence of whitespace chars."
+  (let ((search-spaces-regexp search-whitespace-regexp))
+    (re-search-forward (regexp-quote string) bound noerror count)))
+
+(defun search-backward-lax-whitespace (string &optional bound noerror count)
+  "Search backward for STRING, matching a sequence of whitespace chars."
+  (let ((search-spaces-regexp search-whitespace-regexp))
+    (re-search-backward (regexp-quote string) bound noerror count)))
+
+(defun re-search-forward-lax-whitespace (regexp &optional bound noerror count)
+  "Search forward for REGEXP, matching a sequence of whitespace chars."
+  (let ((search-spaces-regexp search-whitespace-regexp))
+    (re-search-forward regexp bound noerror count)))
+
+(defun re-search-backward-lax-whitespace (regexp &optional bound noerror count)
+  "Search backward for REGEXP, matching a sequence of whitespace chars."
+  (let ((search-spaces-regexp search-whitespace-regexp))
+    (re-search-backward regexp bound noerror count)))
+
 \f
 (defun isearch-query-replace (&optional delimited regexp-flag)
   "Start `query-replace' with string to replace from last search string.
@@ -1484,6 +1589,10 @@ way to run word replacements from Isearch is `M-s w ... M-%'."
        ;; set `search-upper-case' to nil to not call
        ;; `isearch-no-upper-case-p' in `perform-replace'
        (search-upper-case nil)
+       (replace-lax-whitespace
+        isearch-lax-whitespace)
+       (replace-regexp-lax-whitespace
+        isearch-regexp-lax-whitespace)
        ;; Set `isearch-recursive-edit' to nil to prevent calling
        ;; `exit-recursive-edit' in `isearch-done' that terminates
        ;; the execution of this command when it is non-nil.
@@ -1534,6 +1643,8 @@ characters in that string."
   (interactive
    (let* ((perform-collect (consp current-prefix-arg))
          (regexp (cond
+                  ((functionp isearch-word)
+                   (funcall isearch-word isearch-string))
                   (isearch-word (word-search-regexp isearch-string))
                   (isearch-regexp isearch-string)
                   (t (regexp-quote isearch-string)))))
@@ -1547,9 +1658,9 @@ characters in that string."
                 (isearch-done nil t)
                 (isearch-clean-overlays)
                 (let ((default (car occur-collect-regexp-history)))
-                  (read-string
+                  (read-regexp
                    (format "Regexp to collect (default %s): " default)
-                   nil 'occur-collect-regexp-history default)))
+                   default 'occur-collect-regexp-history)))
             ;; Otherwise normal occur takes numerical prefix argument.
             (when current-prefix-arg
               (prefix-numeric-value current-prefix-arg))))))
@@ -1557,7 +1668,11 @@ characters in that string."
        ;; Set `search-upper-case' to nil to not call
        ;; `isearch-no-upper-case-p' in `occur-1'.
        (search-upper-case nil)
-       (search-spaces-regexp (if isearch-regexp search-whitespace-regexp)))
+       (search-spaces-regexp
+        (if (if isearch-regexp
+                isearch-regexp-lax-whitespace
+              isearch-lax-whitespace)
+            search-whitespace-regexp)))
     (occur regexp nlines)))
 
 (declare-function hi-lock-read-face-name "hi-lock" ())
@@ -1599,6 +1714,9 @@ and reads its face argument using `hi-lock-read-face-name'."
 \f
 (defun isearch-delete-char ()
   "Discard last input item and move point back.
+Last input means the last character or the last isearch command
+that added or deleted characters from the search string,
+moved point, toggled regexp mode or case-sensitivity, etc.
 If no previous match was done, just beep."
   (interactive)
   (if (null (cdr isearch-cmds))
@@ -1608,6 +1726,8 @@ If no previous match was done, just beep."
 
 (defun isearch-del-char (&optional arg)
   "Delete character from end of search string and search again.
+Unlike `isearch-delete-char', it only deletes the last character,
+but doesn't cancel the effect of other isearch command.
 If search string is empty, just beep."
   (interactive "p")
   (if (= 0 (length isearch-string))
@@ -1728,6 +1848,17 @@ Subword is used when `subword-mode' is activated. "
    (lambda () (let ((inhibit-field-text-motion t))
                (line-end-position (if (eolp) 2 1))))))
 
+(defun isearch-insert-char-by-name ()
+  "Read a character by its Unicode name and insert it into search string."
+  (interactive)
+  (with-isearch-suspended
+   (let ((char (read-char-by-name "Insert character (Unicode name or hex): ")))
+     (when char
+       (setq isearch-new-string (concat isearch-string (string char))
+            isearch-new-message (concat isearch-message
+                                        (mapconcat 'isearch-text-char-description
+                                                   (string char) "")))))))
+
 (defun isearch-search-and-update ()
   ;; Do the search and update the display.
   (when (or isearch-success
@@ -1749,6 +1880,8 @@ Subword is used when `subword-mode' is activated. "
                       (setq case-fold-search
                             (isearch-no-upper-case-p isearch-string isearch-regexp)))
                   (looking-at (cond
+                               ((functionp isearch-word)
+                                (funcall isearch-word isearch-string t))
                                (isearch-word (word-search-regexp isearch-string t))
                                (isearch-regexp isearch-string)
                                (t (regexp-quote isearch-string)))))
@@ -1806,7 +1939,7 @@ to the barrier."
             ;; We have to check 2 stack frames because the last might be
             ;; invalid just because of a backslash.
             (or (not isearch-error)
-                (not (isearch-error-state (cadr isearch-cmds)))
+                (not (isearch--state-error (cadr isearch-cmds)))
                 allow-invalid))
     (if to-barrier
        (progn (goto-char isearch-barrier)
@@ -1821,8 +1954,8 @@ to the barrier."
        ;; Also skip over postfix operators -- though horrid,
        ;; 'ab?\{5,6\}+\{1,2\}*' is perfectly valid.
        (while (and previous
-                   (or (isearch-error-state frame)
-                       (let* ((string (isearch-string-state frame))
+                   (or (isearch--state-error frame)
+                       (let* ((string (isearch--state-string frame))
                               (lchar (aref string (1- (length string)))))
                          ;; The operators aren't always operators; check
                          ;; backslashes.  This doesn't handle the case of
@@ -1830,7 +1963,7 @@ to the barrier."
                          ;; being special, but then we should fall back to
                          ;; the barrier anyway because it's all optional.
                          (if (isearch-backslash
-                              (isearch-string-state (car previous)))
+                              (isearch--state-string (car previous)))
                              (eq lchar ?\})
                            (memq lchar '(?* ?? ?+))))))
          (setq stack previous previous (cdr previous) frame (car stack)))
@@ -1840,7 +1973,7 @@ to the barrier."
          ;; what matched before that.
          (let ((last-other-end
                 (or (and (car previous)
-                         (isearch-other-end-state (car previous)))
+                         (isearch--state-other-end (car previous)))
                     isearch-barrier)))
            (goto-char (if isearch-forward
                           (max last-other-end isearch-barrier)
@@ -2041,7 +2174,8 @@ Isearch mode."
                 (setq prefix-arg arg)
                 (apply 'isearch-unread keylist))
             (setq keylist
-                  (listify-key-sequence (lookup-key local-function-key-map key)))
+                  (listify-key-sequence
+                   (lookup-key local-function-key-map key)))
             (while keylist
               (setq key (car keylist))
               ;; If KEY is a printing char, we handle it here
@@ -2050,6 +2184,9 @@ Isearch mode."
               (if (and (integerp key)
                        (>= key ?\s) (/= key 127) (< key 256))
                   (progn
+                    ;; Ensure that the processed char is recorded in
+                    ;; the keyboard macro, if any (Bug#4894)
+                    (store-kbd-macro-event key)
                     (isearch-process-search-char key)
                     (setq keylist (cdr keylist)))
                 ;; As the remaining keys in KEYLIST can't be handled
@@ -2152,7 +2289,7 @@ Isearch mode."
     ;; Assume character codes 0200 - 0377 stand for characters in some
     ;; single-byte character set, and convert them to Emacs
     ;; characters.
-    (if (and isearch-regexp (= char ?\s))
+    (if (and isearch-regexp isearch-regexp-lax-whitespace (= char ?\s))
        (if (subregexp-context-p isearch-string (length isearch-string))
            (isearch-process-search-string "[ ]" " ")
          (isearch-process-search-char char))
@@ -2304,12 +2441,12 @@ If there is no completion possible, say so and continue searching."
        (add-text-properties (match-beginning 0) (match-end 0)
                             '(face trailing-whitespace) m)))
     (setq m (concat
-            (isearch-message-prefix c-q-hack ellipsis isearch-nonincremental)
+            (isearch-message-prefix ellipsis isearch-nonincremental)
             m
-            (isearch-message-suffix c-q-hack ellipsis)))
+            (isearch-message-suffix c-q-hack)))
     (if c-q-hack m (let ((message-log-max nil)) (message "%s" m)))))
 
-(defun isearch-message-prefix (&optional _c-q-hack ellipsis nonincremental)
+(defun isearch-message-prefix (&optional 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.
@@ -2329,7 +2466,11 @@ If there is no completion possible, say so and continue searching."
                              (< (point) isearch-opoint)))
                       "over")
                   (if isearch-wrapped "wrapped ")
-                  (if isearch-word "word " "")
+                  (if isearch-word
+                      (or (and (symbolp isearch-word)
+                               (get isearch-word 'isearch-message-prefix))
+                          "word ")
+                    "")
                   (if isearch-regexp "regexp " "")
                   (if multi-isearch-next-buffer-current-function "multi " "")
                   (or isearch-message-prefix-add "")
@@ -2346,7 +2487,7 @@ If there is no completion possible, say so and continue searching."
     (propertize (concat (upcase (substring m 0 1)) (substring m 1))
                'face 'minibuffer-prompt)))
 
-(defun isearch-message-suffix (&optional c-q-hack _ellipsis)
+(defun isearch-message-suffix (&optional c-q-hack)
   (concat (if c-q-hack "^Q" "")
          (if isearch-error
              (concat " [" isearch-error "]")
@@ -2356,8 +2497,8 @@ If there is no completion possible, say so and continue searching."
 \f
 ;; Searching
 
-(defvar isearch-search-fun-function nil
-  "Overrides the default `isearch-search-fun' behavior.
+(defvar isearch-search-fun-function 'isearch-search-fun-default
+  "Non-default value overrides the behavior of `isearch-search-fun-default'.
 This variable's value should be a function, which will be called
 with no arguments, and should return a function that takes three
 arguments: STRING, BOUND, and NOERROR.
@@ -2368,22 +2509,40 @@ search for the first occurrence of STRING or its translation.")
 (defun isearch-search-fun ()
   "Return the function to use for the search.
 Can be changed via `isearch-search-fun-function' for special needs."
-  (if isearch-search-fun-function
-      (funcall isearch-search-fun-function)
-    (cond
-     (isearch-word
+  (funcall (or isearch-search-fun-function 'isearch-search-fun-default)))
+
+(defun isearch-search-fun-default ()
+  "Return default functions to use for the search."
+  (cond
+   (isearch-word
+    (lambda (string &optional bound noerror count)
       ;; Use lax versions to not fail at the end of the word while
       ;; the user adds and removes characters in the search string
       ;; (or when using nonincremental word isearch)
-      (if (or isearch-nonincremental
-             (eq (length isearch-string)
-                 (length (isearch-string-state (car isearch-cmds)))))
-         (if isearch-forward 'word-search-forward 'word-search-backward)
-       (if isearch-forward 'word-search-forward-lax 'word-search-backward-lax)))
-     (isearch-regexp
-      (if isearch-forward 're-search-forward 're-search-backward))
-     (t
-      (if isearch-forward 'search-forward 'search-backward)))))
+      (let ((lax (not (or isearch-nonincremental
+                         (null (car isearch-cmds))
+                         (eq (length isearch-string)
+                             (length (isearch--state-string
+                                       (car isearch-cmds))))))))
+       (funcall
+        (if isearch-forward #'re-search-forward #'re-search-backward)
+        (if (functionp isearch-word)
+            (funcall isearch-word string lax)
+          (word-search-regexp string lax))
+        bound noerror count))))
+   ((and isearch-regexp isearch-regexp-lax-whitespace
+        search-whitespace-regexp)
+    (if isearch-forward
+       're-search-forward-lax-whitespace
+      're-search-backward-lax-whitespace))
+   (isearch-regexp
+    (if isearch-forward 're-search-forward 're-search-backward))
+   ((and isearch-lax-whitespace search-whitespace-regexp)
+    (if isearch-forward
+       'search-forward-lax-whitespace
+      'search-backward-lax-whitespace))
+   (t
+    (if isearch-forward 'search-forward 'search-backward))))
 
 (defun isearch-search-string (string bound noerror)
   "Search for the first occurrence of STRING or its translation.
@@ -2439,11 +2598,11 @@ update the match data, and return point."
            (isearch-no-upper-case-p isearch-string isearch-regexp)))
   (condition-case lossage
       (let ((inhibit-point-motion-hooks
+             ;; FIXME: equality comparisons on functions is asking for trouble.
             (and (eq isearch-filter-predicate 'isearch-filter-visible)
                  search-invisible))
            (inhibit-quit nil)
            (case-fold-search isearch-case-fold-search)
-           (search-spaces-regexp search-whitespace-regexp)
            (retry t))
        (setq isearch-error nil)
        (while retry
@@ -2483,11 +2642,12 @@ update the match data, and return point."
   (if isearch-success
       nil
     ;; Ding if failed this time after succeeding last time.
-    (and (isearch-success-state (car isearch-cmds))
+    (and (isearch--state-success (car isearch-cmds))
         (ding))
-    (if (functionp (isearch-pop-fun-state (car isearch-cmds)))
-        (funcall (isearch-pop-fun-state (car isearch-cmds)) (car isearch-cmds)))
-    (goto-char (isearch-point-state (car isearch-cmds)))))
+    (if (functionp (isearch--state-pop-fun (car isearch-cmds)))
+        (funcall (isearch--state-pop-fun (car isearch-cmds))
+                 (car isearch-cmds)))
+    (goto-char (isearch--state-point (car isearch-cmds)))))
 
 
 ;; Called when opening an overlay, and we are still in isearch.
@@ -2734,7 +2894,8 @@ since they have special meaning in a regexp."
 (defvar isearch-lazy-highlight-window-end nil)
 (defvar isearch-lazy-highlight-case-fold-search nil)
 (defvar isearch-lazy-highlight-regexp nil)
-(defvar isearch-lazy-highlight-space-regexp nil)
+(defvar isearch-lazy-highlight-lax-whitespace nil)
+(defvar isearch-lazy-highlight-regexp-lax-whitespace nil)
 (defvar isearch-lazy-highlight-word nil)
 (defvar isearch-lazy-highlight-forward nil)
 (defvar isearch-lazy-highlight-error nil)
@@ -2776,6 +2937,10 @@ by other Emacs features."
                          isearch-regexp))
                 (not (eq isearch-lazy-highlight-word
                          isearch-word))
+                (not (eq isearch-lazy-highlight-lax-whitespace
+                         isearch-lax-whitespace))
+                (not (eq isearch-lazy-highlight-regexp-lax-whitespace
+                         isearch-regexp-lax-whitespace))
                  (not (= (window-start)
                          isearch-lazy-highlight-window-start))
                  (not (= (window-end)   ; Window may have been split/joined.
@@ -2802,7 +2967,8 @@ by other Emacs features."
          isearch-lazy-highlight-last-string  isearch-string
          isearch-lazy-highlight-case-fold-search isearch-case-fold-search
          isearch-lazy-highlight-regexp       isearch-regexp
-         isearch-lazy-highlight-space-regexp search-whitespace-regexp
+         isearch-lazy-highlight-lax-whitespace   isearch-lax-whitespace
+         isearch-lazy-highlight-regexp-lax-whitespace isearch-regexp-lax-whitespace
          isearch-lazy-highlight-word         isearch-word
          isearch-lazy-highlight-forward      isearch-forward)
       (unless (equal isearch-string "")
@@ -2816,12 +2982,15 @@ Attempt to do the search exactly the way the pending Isearch would."
   (condition-case nil
       (let ((case-fold-search isearch-lazy-highlight-case-fold-search)
            (isearch-regexp isearch-lazy-highlight-regexp)
-           (search-spaces-regexp isearch-lazy-highlight-space-regexp)
            (isearch-word isearch-lazy-highlight-word)
+           (isearch-lax-whitespace
+            isearch-lazy-highlight-lax-whitespace)
+           (isearch-regexp-lax-whitespace
+            isearch-lazy-highlight-regexp-lax-whitespace)
+           (isearch-forward isearch-lazy-highlight-forward)
            (search-invisible nil)      ; don't match invisible text
            (retry t)
            (success nil)
-           (isearch-forward isearch-lazy-highlight-forward)
            (bound (if isearch-lazy-highlight-forward
                       (min (or isearch-lazy-highlight-end-limit (point-max))
                            (if isearch-lazy-highlight-wrapped