(isearch-search-string): Simplify and convert docstring.
[bpt/emacs.git] / lisp / isearch.el
index 42c213a..0a86834 100644 (file)
@@ -176,11 +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.")
+(defvar isearch-filter-predicate 'isearch-filter-invisible
+  "Predicate that filters the search hits that would normally be available.
+Search hits that dissatisfy the predicate are skipped.  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.
 
@@ -533,6 +534,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).
@@ -1333,16 +1337,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)
        ;; set `search-upper-case' to nil to not call
        ;; `isearch-no-upper-case-p' in `perform-replace'
-       (search-upper-case nil))
-    (isearch-done)
+       (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))
@@ -1356,16 +1371,22 @@ 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)))))
+     (if (and transient-mark-mode mark-active) (region-end))))
+  (and isearch-recursive-edit (exit-recursive-edit)))
 
-(defun isearch-query-replace-regexp ()
-  "Start `query-replace-regexp' with string to replace from last search string."
-  (interactive)
-  (isearch-query-replace t))
+(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.
@@ -1373,7 +1394,10 @@ Interactively, REGEXP is the current search regexp or a quoted search
 string.  NLINES has the same meaning as in `occur'."
   (interactive
    (list
-    (if isearch-regexp isearch-string (regexp-quote isearch-string))
+    (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
@@ -1390,17 +1414,33 @@ 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-done)
-  (isearch-clean-overlays)
+  (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)
-  ;; (add-to-history 'hi-lock-regexp-history regexp)
-  (let ((case-fold-search isearch-case-fold-search)
-       ;; TODO: add `search-upper-case' as in `isearch-occur'
-       )
-    (hi-lock-face-buffer
-     (hi-lock-regexp-okay
-      (if isearch-regexp isearch-string (regexp-quote isearch-string)))
-     (hi-lock-read-face-name))))
+  (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 ()
@@ -2127,6 +2167,7 @@ If there is no completion possible, say so and continue searching."
                   (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
@@ -2140,7 +2181,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
@@ -2158,48 +2200,52 @@ 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
       (if isearch-forward 'search-forward 'search-backward)))))
 
 (defun isearch-search-string (string bound noerror)
-  ;; Search for the first occurance of STRING or its translation.  If
-  ;; found, move point to the end of the occurance, update
-  ;; isearch-match-beg and isearch-match-end, and return point.
-  (let ((func (isearch-search-fun))
-       (len (length string))
-       pos1 pos2)
-    (setq pos1 (save-excursion (funcall func string bound noerror)))
-    (if (and (char-table-p translation-table-for-input)
-             (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
-               (if (setq pos2 (funcall func translated bound noerror))
-                   (setq match-data (match-data t)))))
-           (when (and pos2
-                      (or (not pos1)
-                          (if isearch-forward (< pos2 pos1) (> pos2 pos1))))
-             (setq pos1 pos2)
-             (set-match-data match-data)))))
+  "Search for the first occurance of STRING or its translation.  If
+found, move point to the end of the occurance, update
+isearch-match-beg and isearch-match-end, and return point."
+  (let* ((func (isearch-search-fun))
+         (pos1 (save-excursion (funcall func string bound noerror)))
+         pos2)
+    (when (and (char-table-p translation-table-for-input)
+               (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
+              (if (setq pos2 (funcall func translated bound noerror))
+                  (setq match-data (match-data t)))))
+          (when (and pos2
+                     (or (not pos1)
+                         (if isearch-forward (< pos2 pos1) (> pos2 pos1))))
+            (setq pos1 pos2)
+            (set-match-data match-data)))))
     (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 multi-isearch-next-buffer-current-function
               (buffer-live-p multi-isearch-current-buffer))
          (switch-to-buffer multi-isearch-current-buffer))
-      (goto-char pos1))
-    pos1))
+      (goto-char pos1)
+      pos1)))
 
 (defun isearch-search ()
   ;; Do the search with the current search string.
@@ -2211,7 +2257,7 @@ Can be changed via `isearch-search-fun-function' for special needs."
            (isearch-no-upper-case-p isearch-string isearch-regexp)))
   (condition-case lossage
       (let ((inhibit-point-motion-hooks
-            (and (eq isearch-success-function 'isearch-success-function-default)
+            (and (eq isearch-filter-predicate 'isearch-filter-invisible)
                  search-invisible))
            (inhibit-quit nil)
            (case-fold-search isearch-case-fold-search)
@@ -2221,12 +2267,12 @@ Can be changed via `isearch-search-fun-function' for special needs."
        (while retry
          (setq isearch-success
                (isearch-search-string isearch-string nil t))
-         ;; Clear RETRY unless we matched some invisible text
-         ;; and we aren't supposed to do that.
+         ;; Clear RETRY unless the search predicate says
+         ;; to skip this search hit.
          (if (or (not isearch-success)
                  (bobp) (eobp)
                  (= (match-beginning 0) (match-end 0))
-                 (funcall isearch-success-function
+                 (funcall isearch-filter-predicate
                           (match-beginning 0) (match-end 0)))
              (setq retry nil)))
        (setq isearch-just-started nil)
@@ -2405,10 +2451,10 @@ 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."
+(defun isearch-filter-invisible (beg end)
+  "Default predicate to filter out invisible text.
+It filters search hits to those that are visible (at least partially),
+unless invisible text too can be searched."
   (or (eq search-invisible t)
       (not (isearch-range-invisible beg end))))
 
@@ -2578,23 +2624,34 @@ 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))
+         ;; Clear RETRY unless the search predicate says
+         ;; to skip this search hit.
+         (if (or (not success)
+                 (funcall isearch-filter-predicate
+                          (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."