]> code.delx.au - gnu-emacs/blobdiff - lisp/isearch.el
* w32-fns.el (w32-shell-dos-semantics):
[gnu-emacs] / lisp / isearch.el
index 22fa28e70efd0789c81f686faa043c6ebc467972..c343472767768f511bf6622f087a1a13a342ce5c 100644 (file)
@@ -1,7 +1,7 @@
 ;;; isearch.el --- incremental search minor mode
 
 ;; Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1999, 2000,
-;;   2001, 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
+;;   2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
 
 ;; Author: Daniel LaLiberte <liberte@cs.uiuc.edu>
 ;; Maintainer: FSF
@@ -9,10 +9,10 @@
 
 ;; This file is part of GNU Emacs.
 
-;; GNU Emacs is free software; you can redistribute it and/or modify
+;; GNU Emacs is free software: you can redistribute it and/or modify
 ;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation; either version 3, or (at your option)
-;; any later version.
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
 
 ;; GNU Emacs is distributed in the hope that it will be useful,
 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -20,9 +20,7 @@
 ;; GNU General Public License for more details.
 
 ;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs; see the file COPYING.  If not, write to the
-;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
-;; Boston, MA 02110-1301, USA.
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
 
 ;;; Commentary:
 
@@ -96,7 +94,8 @@ that the search has reached."
 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."
+If this value is `not-yanks', text yanked into the search string
+in Isearch mode is always downcased."
   :type '(choice (const :tag "off" nil)
                 (const not-yanks)
                 (other :tag "on" t))
@@ -177,6 +176,12 @@ or to the end of the buffer for a backward search.")
   "Function to save a function restoring the mode-specific isearch state
 to the search status stack.")
 
+(defvar isearch-success-function 'isearch-success-function-default
+  "Function to report whether the new search match is considered successful.
+The function has two arguments: the positions of start and end of text
+matched by the search.  If this function returns nil, continue
+searching without stopping at this match.")
+
 ;; Search ring.
 
 (defvar search-ring nil
@@ -230,6 +235,22 @@ Default value, nil, means edit the string instead."
   :group 'basic-faces)
 (defvar isearch 'isearch)
 
+(defface isearch-fail
+  '((((class color) (min-colors 88) (background light))
+     (:background "RosyBrown1"))
+    (((class color) (min-colors 88) (background dark))
+     (:background "red4"))
+    (((class color) (min-colors 16))
+     (:background "red"))
+    (((class color) (min-colors 8))
+     (:background "red"))
+    (((class color grayscale))
+     :foreground "grey")
+    (t (:inverse-video t)))
+  "Face for highlighting failed part in Isearch echo-area message."
+  :version "23.1"
+  :group 'isearch)
+
 (defcustom isearch-lazy-highlight t
   "*Controls the lazy-highlighting during incremental search.
 When non-nil, all text in the buffer matching the current search
@@ -304,6 +325,73 @@ A value of nil means highlight all matches."
 (define-obsolete-variable-alias 'isearch-lazy-highlight-face
                                 'lazy-highlight-face
                                 "22.1")
+\f
+;; Define isearch help map.
+
+(defvar isearch-help-map
+  (let ((i 0)
+       (map (make-sparse-keymap)))
+    (define-key map [t] 'isearch-other-control-char)
+    (define-key map (char-to-string help-char) 'isearch-help-for-help)
+    (define-key map [help] 'isearch-help-for-help)
+    (define-key map [f1] 'isearch-help-for-help)
+    (define-key map "?" 'isearch-help-for-help)
+    (define-key map "b" 'isearch-describe-bindings)
+    (define-key map "k" 'isearch-describe-key)
+    (define-key map "m" 'isearch-describe-mode)
+    (define-key map "q" 'help-quit)
+    map)
+  "Keymap for characters following the Help key for isearch mode.")
+
+(eval-when-compile (require 'help-macro))
+
+(make-help-screen isearch-help-for-help-internal
+  "Type a help option: [bkm] or ?"
+  "You have typed %THIS-KEY%, the help character.  Type a Help option:
+\(Type \\<help-map>\\[help-quit] to exit the Help command.)
+
+b           Display all isearch key bindings.
+k KEYS      Display full documentation of isearch key sequence.
+m           Display documentation of isearch mode.
+
+You can't type here other help keys available in the global help map,
+but outise of this help window when you type them in isearch mode,
+they exit isearch mode before displaying global help."
+  isearch-help-map)
+
+(defun isearch-help-for-help ()
+  "Display isearch help menu."
+  (interactive)
+  (let (same-window-buffer-names same-window-regexps)
+    (isearch-help-for-help-internal))
+  (isearch-update))
+
+(defun isearch-describe-bindings ()
+  "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)
+    (with-help-window "*Help*"
+      (with-current-buffer standard-output
+       (princ "Isearch Mode Bindings:\n")
+       (princ (substitute-command-keys "\\{isearch-mode-map}"))))))
+
+(defun isearch-describe-key ()
+  "Display documentation of the function invoked by isearch key."
+  (interactive)
+  (let (same-window-buffer-names same-window-regexps)
+    (call-interactively 'describe-key))
+  (isearch-update))
+
+(defun isearch-describe-mode ()
+  "Display documentation of isearch mode."
+  (interactive)
+  (let (same-window-buffer-names same-window-regexps)
+    (describe-function 'isearch-forward))
+  (isearch-update))
+
+(defalias 'isearch-mode-help 'isearch-describe-mode)
+
 \f
 ;; Define isearch-mode keymap.
 
@@ -371,9 +459,7 @@ A value of nil means highlight all matches."
     (define-key map "\M-\C-y" 'isearch-yank-char)
     (define-key map    "\C-y" 'isearch-yank-line)
 
-    ;; Turned off because I find I expect to get the global definition--rms.
-    ;; ;; Instead bind C-h to special help command for isearch-mode.
-    ;; (define-key map "\C-h" 'isearch-mode-help)
+    (define-key map "\C-h" isearch-help-map)
 
     (define-key map "\M-n" 'isearch-ring-advance)
     (define-key map "\M-p" 'isearch-ring-retreat)
@@ -405,8 +491,13 @@ A value of nil means highlight all matches."
     (define-key map "\M-r" 'isearch-toggle-regexp)
     (define-key map "\M-e" 'isearch-edit-string)
 
+    (define-key map "\M-sr" 'isearch-toggle-regexp)
+    (define-key map "\M-sw" 'isearch-toggle-word)
+
     (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)
 
     map)
   "Keymap for `isearch-mode'.")
@@ -418,6 +509,7 @@ A value of nil means highlight all matches."
     (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)
+    (define-key map "\C-w"  'isearch-edit-string-set-word)
     (define-key map "\C-f"  'isearch-yank-char-in-minibuffer)
     (define-key map [right] 'isearch-yank-char-in-minibuffer)
     map)
@@ -441,6 +533,9 @@ Each set is a vector of the form:
 (defvar isearch-string "")  ; The current search string.
 (defvar isearch-message "") ; text-char-description version of isearch-string
 
+(defvar isearch-message-prefix-add nil) ; Additonal text for the message prefix
+(defvar isearch-message-suffix-add nil) ; Additonal text for the message suffix
+
 (defvar isearch-success t)     ; Searching is currently successful.
 (defvar isearch-error nil)     ; Error message for failed search.
 (defvar isearch-other-end nil) ; Start (end) of match if forward (backward).
@@ -508,6 +603,7 @@ Each set is a vector of the form:
 (define-key esc-map "\C-s" 'isearch-forward-regexp)
 (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)
 
 ;; Entry points to isearch-mode.
 
@@ -542,13 +638,9 @@ Type \\[isearch-quote-char] to quote control character to search for it.
 If you try to exit with the search string still empty, it invokes
  nonincremental search.
 
-Type \\[isearch-query-replace] to start `query-replace' with string to\
- replace from last search string.
-Type \\[isearch-query-replace-regexp] to start `query-replace-regexp'\
- with string to replace from last search string.
-
 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-edit-string] to edit the search string in the minibuffer.
 
 Also supported is a search ring of the previous 16 search strings.
@@ -557,6 +649,19 @@ Type \\[isearch-ring-retreat] to search for the previous item in the search\
  ring.
 Type \\[isearch-complete] to complete the search string using the search ring.
 
+Type \\[isearch-query-replace] to run `query-replace' with string to\
+ replace from last search string.
+Type \\[isearch-query-replace-regexp] to run `query-replace-regexp'\
+ with the last search string.
+Type \\[isearch-occur] to run `occur' that shows\
+ the last search string.
+Type \\[isearch-highlight-regexp] to run `highlight-regexp'\
+ that highlights the last search string.
+
+Type \\[isearch-describe-bindings] to display all isearch key bindings.
+Type \\[isearch-describe-key] to display documentation of isearch key.
+Type \\[isearch-describe-mode] to display documentation of isearch mode.
+
 If an input method is turned on in the current buffer, that input
 method is also active while you are typing characters to search.  To
 toggle the input method, type \\[isearch-toggle-input-method].  It
@@ -582,8 +687,8 @@ the calling function until the search is done."
   "\
 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.
+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
@@ -592,11 +697,21 @@ and nothing else, enter C-q SPC."
   (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.
+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.
+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-backward (&optional regexp-p no-recursive-edit)
   "\
 Do incremental search backward.
 With a prefix argument, do a regular expression search instead.
-See \\[isearch-forward] for more information."
+See the command `isearch-forward' for more information."
   (interactive "P\np")
   (isearch-mode nil (not (null regexp-p)) nil (not no-recursive-edit)))
 
@@ -604,17 +719,11 @@ See \\[isearch-forward] for more information."
   "\
 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."
+Like ordinary incremental search except that your input is treated
+as a regexp.  See the command `isearch-forward' for more information."
   (interactive "P\np")
   (isearch-mode nil (null not-regexp) nil (not no-recursive-edit)))
 
-
-(defun isearch-mode-help ()
-  (interactive)
-  (describe-function 'isearch-forward)
-  (isearch-update))
-
 \f
 ;; isearch-mode only sets up incremental search for the minor mode.
 ;; All the work is done by the isearch-mode commands.
@@ -626,9 +735,8 @@ is treated as a regexp.  See \\[isearch-forward] for more info."
 
 
 (defun isearch-mode (forward &optional regexp op-fun recursive-edit word-p)
-  "Start isearch minor mode.  Called by `isearch-forward', etc.
-
-\\{isearch-mode-map}"
+  "Start isearch minor mode.
+It is called by the function `isearch-forward' and other related functions."
 
   ;; Initialize global vars.
   (setq isearch-forward forward
@@ -1007,37 +1115,21 @@ If first char entered is \\[isearch-yank-word-or-char], then do word search inst
          ;; that can change their values.
          (setq old-point (point) old-other-end isearch-other-end)
 
-         (isearch-message) ;; for read-char
          (unwind-protect
-             (let* (;; Why does following read-char echo?
-                    ;;(echo-keystrokes 0) ;; not needed with above message
-                    (e (let ((cursor-in-echo-area t))
-                         (read-event)))
+             (let* ((message-log-max nil)
                     ;; Binding minibuffer-history-symbol to nil is a work-around
                     ;; for some incompatibility with gmhist.
-                    (minibuffer-history-symbol)
-                    (message-log-max nil))
-               ;; If the first character the user types when we prompt them
-               ;; for a string is the yank-word character, then go into
-               ;; word-search mode.  Otherwise unread that character and
-               ;; read a key the normal way.
-               ;; Word search does not apply (yet) to regexp searches,
-               ;; no check is made here.
-               (message "%s" (isearch-message-prefix nil nil t))
-               (if (memq (lookup-key isearch-mode-map (vector e))
-                         '(isearch-yank-word
-                           isearch-yank-word-or-char))
-                   (setq isearch-word t;; so message-prefix is right
-                         isearch-new-word t)
-                 (cancel-kbd-macro-events)
-                 (isearch-unread e))
-               (setq cursor-in-echo-area nil)
+                    (minibuffer-history-symbol))
                (setq isearch-new-string
                       (read-from-minibuffer
                        (isearch-message-prefix nil nil isearch-nonincremental)
                        isearch-string
                        minibuffer-local-isearch-map nil
-                       (if isearch-regexp 'regexp-search-ring 'search-ring)
+                       (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
@@ -1078,12 +1170,15 @@ If first char entered is \\[isearch-yank-word-or-char], then do word search inst
            ;; Only the string actually used should be saved.
            ))
 
-       ;; Push the state as of before this C-s.
-       (isearch-push-state)
+       ;; This used to push the state as of before this C-s, but it adds
+       ;; an inconsistent state where part of variables are from the
+       ;; previous search (e.g. `isearch-success'), and part of variables
+       ;; are just entered from the minibuffer (e.g. `isearch-string').
+       ;; (isearch-push-state)
 
        ;; Reinvoke the pending search.
        (isearch-search)
-       (isearch-push-state)
+       (isearch-push-state)            ; this pushes the correct state
        (isearch-update)
        (if isearch-nonincremental
            (progn
@@ -1098,6 +1193,15 @@ If first char entered is \\[isearch-yank-word-or-char], then do word search inst
      (isearch-abort)  ;; outside of let to restore outside global values
      )))
 
+;; Obsolete usage of `C-s M-e C-w'.  Remove after 23.1.
+(defvar isearch-new-word)
+(defun isearch-edit-string-set-word ()
+  "Do word search after exiting `isearch-edit-string'."
+  (interactive)
+  (message "This feature is obsolete since 23.1; use `M-s w' instead.")
+  (setq isearch-word t isearch-new-word t))
+
+
 (defun isearch-nonincremental-exit-minibuffer ()
   (interactive)
   (setq isearch-nonincremental t)
@@ -1211,6 +1315,13 @@ Use `isearch-exit' to quit without signaling."
   (setq isearch-success t isearch-adjusted t)
   (isearch-update))
 
+(defun isearch-toggle-word ()
+  "Toggle word searching on or off."
+  (interactive)
+  (setq isearch-word (not isearch-word))
+  (setq isearch-success t isearch-adjusted t)
+  (isearch-update))
+
 (defun isearch-toggle-case-fold ()
   "Toggle case folding in searching on or off."
   (interactive)
@@ -1225,13 +1336,27 @@ Use `isearch-exit' to quit without signaling."
   (sit-for 1)
   (isearch-update))
 
-(defun isearch-query-replace (&optional regexp-flag)
-  "Start query-replace with string to replace from last search string."
-  (interactive)
+(defun isearch-query-replace (&optional delimited regexp-flag)
+  "Start `query-replace' with string to replace from last search string.
+The arg DELIMITED (prefix arg if interactive), if non-nil, means replace
+only matches surrounded by word boundaries.  Note that using the prefix arg
+is possible only when `isearch-allow-scroll' is non-nil, and it don't
+always provides the correct matches for `query-replace', so the preferred
+way to run word replacements from Isearch is `M-s w ... M-%'."
+  (interactive
+   (list current-prefix-arg))
   (barf-if-buffer-read-only)
   (if regexp-flag (setq isearch-regexp t))
-  (let ((case-fold-search isearch-case-fold-search))
-    (isearch-done)
+  (let ((case-fold-search isearch-case-fold-search)
+       ;; set `search-upper-case' to nil to not call
+       ;; `isearch-no-upper-case-p' in `perform-replace'
+       (search-upper-case nil)
+       ;; 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.
+       ;; We call `exit-recursive-edit' explicitly at the end below.
+       (isearch-recursive-edit nil))
+    (isearch-done nil t)
     (isearch-clean-overlays)
     (if (and isearch-other-end
             (< isearch-other-end (point))
@@ -1245,16 +1370,76 @@ Use `isearch-exit' to quit without signaling."
      isearch-string
      (query-replace-read-to
       isearch-string
-      (if isearch-regexp "Query replace regexp" "Query replace")
+      (concat "Query replace"
+             (if (or delimited isearch-word) " word" "")
+             (if isearch-regexp " regexp" "")
+             (if (and transient-mark-mode mark-active) " in region" ""))
       isearch-regexp)
-     t isearch-regexp isearch-word nil nil
+     t isearch-regexp (or delimited isearch-word) nil nil
      (if (and transient-mark-mode mark-active) (region-beginning))
-     (if (and transient-mark-mode mark-active) (region-end)))))
-
-(defun isearch-query-replace-regexp ()
-  "Start query-replace-regexp with string to replace from last search string."
+     (if (and transient-mark-mode mark-active) (region-end))))
+  (and isearch-recursive-edit (exit-recursive-edit)))
+
+(defun isearch-query-replace-regexp (&optional delimited)
+  "Start `query-replace-regexp' with string to replace from last search string.
+See `isearch-query-replace' for more information."
+  (interactive
+   (list current-prefix-arg))
+  (isearch-query-replace delimited t))
+
+(defun isearch-occur (regexp &optional nlines)
+  "Run `occur' with regexp to search from the current search string.
+Interactively, REGEXP is the current search regexp or a quoted search
+string.  NLINES has the same meaning as in `occur'."
+  (interactive
+   (list
+    (cond
+     (isearch-word (concat "\\b" (regexp-quote isearch-string) "\\b"))
+     (isearch-regexp isearch-string)
+     (t (regexp-quote isearch-string)))
+    (if current-prefix-arg (prefix-numeric-value current-prefix-arg))))
+  (let ((case-fold-search isearch-case-fold-search)
+       ;; set `search-upper-case' to nil to not call
+       ;; `isearch-no-upper-case-p' in `occur-1'
+       (search-upper-case nil))
+    (occur regexp nlines)))
+
+(declare-function hi-lock-regexp-okay "hi-lock" (regexp))
+(declare-function hi-lock-read-face-name "hi-lock" ())
+
+(defun isearch-highlight-regexp ()
+  "Run `highlight-regexp' with regexp from the current search string.
+It exits Isearch mode and calls `hi-lock-face-buffer' with its regexp
+argument from the last search regexp or a quoted search string,
+and reads its face argument using `hi-lock-read-face-name'."
   (interactive)
-  (isearch-query-replace t))
+  (let (
+       ;; 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.
+       ;; We call `exit-recursive-edit' explicitly at the end below.
+       (isearch-recursive-edit nil))
+    (isearch-done nil t)
+    (isearch-clean-overlays))
+  (require 'hi-lock nil t)
+  (let ((string (cond (isearch-regexp isearch-string)
+                     ((if (and (eq isearch-case-fold-search t)
+                               search-upper-case)
+                          (isearch-no-upper-case-p
+                           isearch-string isearch-regexp)
+                        isearch-case-fold-search)
+                      ;; Turn isearch-string into a case-insensitive
+                      ;; regexp.
+                      (mapconcat
+                       (lambda (c)
+                         (let ((s (string c)))
+                           (if (string-match "[[:alpha:]]" s)
+                               (format "[%s%s]" (upcase s) (downcase s))
+                             (regexp-quote s))))
+                       isearch-string ""))
+                     (t (regexp-quote isearch-string)))))
+    (hi-lock-face-buffer string (hi-lock-read-face-name)))
+  (and isearch-recursive-edit (exit-recursive-edit)))
 
 \f
 (defun isearch-delete-char ()
@@ -1463,7 +1648,7 @@ to the barrier."
        ;; removes all bracket-sets and groups that might be in the way, as
        ;; well as partial \{\} constructs that the code below leaves behind.
        ;; Also skip over postfix operators -- though horrid,
-       ;; 'ab?\{5,6\}+\{1,2\}*' is perfectly legal.
+       ;; 'ab?\{5,6\}+\{1,2\}*' is perfectly valid.
        (while (and previous
                    (or (isearch-error-state frame)
                        (let* ((string (isearch-string-state frame))
@@ -1538,13 +1723,12 @@ Scroll-bar or mode-line events are processed appropriately."
 ;; Scroll-bar functions:
 (if (fboundp 'scroll-bar-toolkit-scroll)
     (put 'scroll-bar-toolkit-scroll 'isearch-scroll t))
-(if (fboundp 'mac-handle-scroll-bar-event)
-    (put 'mac-handle-scroll-bar-event 'isearch-scroll t))
 (if (fboundp 'w32-handle-scroll-bar-event)
     (put 'w32-handle-scroll-bar-event 'isearch-scroll t))
 
 ;; Commands which scroll the window:
 (put 'recenter 'isearch-scroll t)
+(put 'recenter-top-bottom 'isearch-scroll t)
 (put 'reposition-window 'isearch-scroll t)
 (put 'scroll-up 'isearch-scroll t)
 (put 'scroll-down 'isearch-scroll t)
@@ -1799,7 +1983,7 @@ Isearch mode."
   "Convert return into newline for incremental search."
   (interactive)
   (isearch-process-search-char ?\n))
-(make-obsolete 'isearch-return-char 'isearch-printing-char)
+(make-obsolete 'isearch-return-char 'isearch-printing-char "19.7")
 
 (defun isearch-printing-char ()
   "Add this ordinary printing character to the search string and search."
@@ -1807,15 +1991,9 @@ Isearch mode."
   (let ((char last-command-char))
     (if (= char ?\S-\ )
        (setq char ?\s))
-    (if (and enable-multibyte-characters
-            (>= char ?\200)
-            (<= char ?\377))
-       (if (keyboard-coding-system)
-           (isearch-process-search-multibyte-characters char)
-         (isearch-process-search-char (unibyte-char-to-multibyte char)))
-      (if current-input-method
-         (isearch-process-search-multibyte-characters char)
-       (isearch-process-search-char char)))))
+    (if current-input-method
+       (isearch-process-search-multibyte-characters char)
+      (isearch-process-search-char char))))
 
 (defun isearch-process-search-char (char)
   ;; * and ? are special in regexps when not preceded by \.
@@ -1866,10 +2044,12 @@ Isearch mode."
   (if search-ring-update
       (progn
        (isearch-search)
+       (isearch-push-state)
        (isearch-update))
-    (isearch-edit-string)
-    )
-  (isearch-push-state))
+    ;; Otherwise, edit the search string instead.  Note that there is
+    ;; no need to push the search state after isearch-edit-string here
+    ;; since isearch-edit-string already pushes its state
+    (isearch-edit-string)))
 
 (defun isearch-ring-advance ()
   "Advance to the next search string in the ring."
@@ -1936,21 +2116,32 @@ If there is no completion possible, say so and continue searching."
 (defun isearch-message (&optional c-q-hack ellipsis)
   ;; Generate and print the message string.
   (let ((cursor-in-echo-area ellipsis)
-       (m (concat
-           (isearch-message-prefix c-q-hack ellipsis isearch-nonincremental)
-           (if (and (not isearch-success)
-                     (string-match " +$" isearch-message))
-                (concat
-                 (substring isearch-message 0 (match-beginning 0))
-                 (propertize (substring isearch-message (match-beginning 0))
-                             'face 'trailing-whitespace))
-              isearch-message)
-           (isearch-message-suffix c-q-hack ellipsis)
-           )))
-    (if c-q-hack
-       m
-      (let ((message-log-max nil))
-       (message "%s" m)))))
+       (m isearch-message)
+       (cmds isearch-cmds)
+       succ-msg)
+    (when (or (not isearch-success) isearch-error)
+      ;; Highlight failed part
+      (while (or (not (isearch-success-state (car cmds)))
+                (isearch-error-state (car cmds)))
+       (pop cmds))
+      (setq succ-msg (and cmds (isearch-message-state (car cmds)))
+           m (copy-sequence m))
+      (add-text-properties
+       (if (and (stringp succ-msg)
+               (< (length succ-msg) (length m))
+               (equal succ-msg (substring m 0 (length succ-msg))))
+          (length succ-msg)
+        0)
+       (length m) '(face isearch-fail) m)
+      ;; Highlight failed trailing whitespace
+      (when (string-match " +$" m)
+       (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)
+            m
+            (isearch-message-suffix c-q-hack ellipsis)))
+    (if c-q-hack m (let ((message-log-max nil)) (message "%s" m)))))
 
 (defun isearch-message-prefix (&optional c-q-hack ellipsis nonincremental)
   ;; If about to search, and previous search regexp was invalid,
@@ -1974,6 +2165,8 @@ If there is no completion possible, say so and continue searching."
                   (if isearch-wrapped "wrapped ")
                   (if isearch-word "word " "")
                   (if isearch-regexp "regexp " "")
+                  (if multi-isearch-next-buffer-current-function "multi " "")
+                  (or isearch-message-prefix-add "")
                   (if nonincremental "search" "I-search")
                   (if isearch-forward "" " backward")
                   (if current-input-method
@@ -1987,7 +2180,8 @@ If there is no completion possible, say so and continue searching."
   (concat (if c-q-hack "^Q" "")
          (if isearch-error
              (concat " [" isearch-error "]")
-           "")))
+           "")
+         (or isearch-message-suffix-add "")))
 
 \f
 ;; Searching
@@ -2005,7 +2199,12 @@ Can be changed via `isearch-search-fun-function' for special needs."
       (funcall isearch-search-fun-function)
     (cond
      (isearch-word
-      (if isearch-forward 'word-search-forward 'word-search-backward))
+      ;; Use lax versions to not fail at the end of the word while the user
+      ;; adds and removes characters in the search string
+      (if (not (eq (length isearch-string)
+                  (length (isearch-string-state (car isearch-cmds)))))
+         (if isearch-forward 'word-search-forward-lax 'word-search-backward-lax)
+       (if isearch-forward 'word-search-forward 'word-search-backward)))
      (isearch-regexp
       (if isearch-forward 're-search-forward 're-search-backward))
      (t
@@ -2020,13 +2219,15 @@ Can be changed via `isearch-search-fun-function' for special needs."
        pos1 pos2)
     (setq pos1 (save-excursion (funcall func string bound noerror)))
     (if (and (char-table-p translation-table-for-input)
-            (> (string-bytes string) len))
-       (let (translated match-data)
-         (dotimes (i len)
-           (let ((x (aref translation-table-for-input (aref string i))))
-             (when x
-               (or translated (setq translated (copy-sequence string)))
-               (aset translated i x))))
+             (multibyte-string-p string)
+             ;; Minor optimization.
+             (string-match-p "[^[:ascii:]]" string))
+       (let ((translated
+               (apply 'string
+                      (mapcar (lambda (c)
+                                (or (aref translation-table-for-input c) c))
+                              string)))
+              match-data)
          (when translated
            (save-match-data
              (save-excursion
@@ -2040,9 +2241,9 @@ Can be changed via `isearch-search-fun-function' for special needs."
     (when pos1
       ;; When using multiple buffers isearch, switch to the new buffer here,
       ;; because `save-excursion' above doesn't allow doing it inside funcall.
-      (if (and isearch-buffers-next-buffer-function
-              (buffer-live-p isearch-buffers-current-buffer))
-         (switch-to-buffer isearch-buffers-current-buffer))
+      (if (and multi-isearch-next-buffer-current-function
+              (buffer-live-p multi-isearch-current-buffer))
+         (switch-to-buffer multi-isearch-current-buffer))
       (goto-char pos1))
     pos1))
 
@@ -2055,7 +2256,9 @@ Can be changed via `isearch-search-fun-function' for special needs."
       (setq isearch-case-fold-search
            (isearch-no-upper-case-p isearch-string isearch-regexp)))
   (condition-case lossage
-      (let ((inhibit-point-motion-hooks search-invisible)
+      (let ((inhibit-point-motion-hooks
+            (and (eq isearch-success-function 'isearch-success-function-default)
+                 search-invisible))
            (inhibit-quit nil)
            (case-fold-search isearch-case-fold-search)
            (search-spaces-regexp search-whitespace-regexp)
@@ -2066,12 +2269,11 @@ Can be changed via `isearch-search-fun-function' for special needs."
                (isearch-search-string 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)
+         (if (or (not isearch-success)
                  (bobp) (eobp)
                  (= (match-beginning 0) (match-end 0))
-                 (not (isearch-range-invisible
-                       (match-beginning 0) (match-end 0))))
+                 (funcall isearch-success-function
+                          (match-beginning 0) (match-end 0)))
              (setq retry nil)))
        (setq isearch-just-started nil)
        (if isearch-success
@@ -2249,6 +2451,13 @@ Can be changed via `isearch-search-fun-function' for special needs."
                  nil)
              (setq isearch-hidden t)))))))
 
+(defun isearch-success-function-default (beg end)
+  "Default function to report if the new search match is successful.
+Returns t if search can match hidden text, or otherwise checks if some
+text from BEG to END is visible."
+  (or (eq search-invisible t)
+      (not (isearch-range-invisible beg end))))
+
 \f
 ;; General utilities
 
@@ -2415,23 +2624,32 @@ by other Emacs features."
 (defun isearch-lazy-highlight-search ()
   "Search ahead for the next or previous match, for lazy highlighting.
 Attempt to do the search exactly the way the pending isearch would."
-  (let ((case-fold-search isearch-lazy-highlight-case-fold-search)
-       (isearch-regexp isearch-lazy-highlight-regexp)
-       (search-spaces-regexp isearch-lazy-highlight-space-regexp))
-    (condition-case nil
-       (isearch-search-string
-                isearch-lazy-highlight-last-string
-                (if isearch-forward
-                    (min (or isearch-lazy-highlight-end-limit (point-max))
+  (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)
+           (search-invisible nil)      ; don't match invisible text
+           (retry t)
+           (success nil)
+           (bound (if isearch-forward
+                      (min (or isearch-lazy-highlight-end-limit (point-max))
+                           (if isearch-lazy-highlight-wrapped
+                               isearch-lazy-highlight-start
+                             (window-end)))
+                    (max (or isearch-lazy-highlight-start-limit (point-min))
                          (if isearch-lazy-highlight-wrapped
-                             isearch-lazy-highlight-start
-                           (window-end)))
-                  (max (or isearch-lazy-highlight-start-limit (point-min))
-                       (if isearch-lazy-highlight-wrapped
-                           isearch-lazy-highlight-end
-                         (window-start))))
-                t)
-      (error nil))))
+                             isearch-lazy-highlight-end
+                           (window-start))))))
+       ;; Use a loop like in `isearch-search'
+       (while retry
+         (setq success (isearch-search-string
+                        isearch-lazy-highlight-last-string bound t))
+         (if (or (not success)
+                 (funcall isearch-success-function
+                          (match-beginning 0) (match-end 0)))
+             (setq retry nil)))
+       success)
+    (error nil)))
 
 (defun isearch-lazy-highlight-update ()
   "Update highlighting of other matches for current search."