]> code.delx.au - gnu-emacs/blobdiff - lisp/isearch.el
* net/network-stream.el (network-stream-open-starttls): Don't add
[gnu-emacs] / lisp / isearch.el
index d9f8b0891e4582d3ede82320145f6f203059ed32..131ab5893d4442245c17e93559cca203255b6552 100644 (file)
@@ -187,21 +187,15 @@ 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-filter-predicates nil
-  "Predicates that filter the search hits that would normally be available.
-Search hits that dissatisfy the list of predicates are skipped.
-Each function in this list has two arguments: the positions of
-start and end of text matched by the search.
-The search loop uses `run-hook-with-args-until-failure' to call
-each predicate in order, and when one of the predicates returns nil,
-skips this match and continues searching for the next match.
-When the list of predicates is empty, `run-hook-with-args-until-failure'
-returns non-nil that means that the found match is accepted.
-The property `isearch-message-prefix' put on the predicate's symbol
-specifies the prefix string displayed in the search message.")
-(define-obsolete-variable-alias 'isearch-filter-predicate
-                                'isearch-filter-predicates
-                                "24.4")
+(defvar isearch-filter-predicate #'isearch-filter-visible
+  "Predicate that filter 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.
+If you use `add-function' to modify this variable, you can use the
+`isearch-message-prefix' advice property to specify the prefix string
+displayed in the search message.")
 
 ;; Search ring.
 
@@ -783,7 +777,7 @@ 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)
+(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
@@ -1015,7 +1009,7 @@ NOPUSH is t and EDIT is t."
   (setq minibuffer-message-timeout isearch-original-minibuffer-message-timeout)
   (isearch-dehighlight)
   (lazy-highlight-cleanup lazy-highlight-cleanup)
-  (let ((found-start (window-start (selected-window)))
+  (let ((found-start (window-start))
        (found-point (point)))
     (when isearch-window-configuration
       (set-window-configuration isearch-window-configuration)
@@ -1540,17 +1534,22 @@ nil and a non-nil value of the option `search-invisible'
   "Return a regexp which matches words, ignoring punctuation.
 Given STRING, a string of words separated by word delimiters,
 compute a regexp that matches those exact words separated by
-arbitrary punctuation.  If LAX is non-nil, the end of the string
-need not match a word boundary unless it ends in whitespace.
+arbitrary punctuation.  If the string begins or ends in whitespace,
+the beginning or the end of the string matches arbitrary whitespace.
+Otherwise if LAX is non-nil, the beginning or the end of the string
+need not match a word boundary.
 
 Used in `word-search-forward', `word-search-backward',
 `word-search-forward-lax', `word-search-backward-lax'."
-  (if (string-match-p "^\\W*$" string)
-      ""
-    (concat
-     "\\b"
-     (mapconcat 'identity (split-string string "\\W+" t) "\\W+")
-     (if (or (not lax) (string-match-p "\\W$" string)) "\\b"))))
+  (cond
+   ((equal string "") "")
+   ((string-match-p "\\`\\W+\\'" string) "\\W+")
+   (t (concat
+       (if (string-match-p "\\`\\W" string) "\\W+"
+        (unless lax "\\<"))
+       (mapconcat 'regexp-quote (split-string string "\\W+" t) "\\W+")
+       (if (string-match-p "\\W\\'" string) "\\W+"
+        (unless lax "\\>"))))))
 
 (defun word-search-backward (string &optional bound noerror count)
   "Search backward from point for STRING, ignoring differences in punctuation.
@@ -1625,8 +1624,24 @@ to punctuation."
 (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 "\\_>")))
+If there are more than one symbol, then compute a regexp that matches
+those exact symbols separated by non-symbol characters.  If the string
+begins or ends in whitespace, the beginning or the end of the string
+matches arbitrary non-symbol whitespace.  Otherwise if LAX is non-nil,
+the beginning or the end of the string need not match a symbol boundary."
+  (let ((not-word-symbol-re
+        ;; This regexp matches all syntaxes except word and symbol syntax.
+        ;; FIXME: Replace it with something shorter if possible (bug#14602).
+        "\\(?:\\s-\\|\\s.\\|\\s(\\|\\s)\\|\\s\"\\|\\s\\\\|\\s/\\|\\s$\\|\\s'\\|\\s<\\|\\s>\\|\\s@\\|\\s!\\|\\s|\\)+"))
+    (cond
+     ((equal string "") "")
+     ((string-match-p (format "\\`%s\\'" not-word-symbol-re) string) not-word-symbol-re)
+     (t (concat
+        (if (string-match-p (format "\\`%s" not-word-symbol-re) string) not-word-symbol-re
+          (unless lax "\\_<"))
+        (mapconcat 'regexp-quote (split-string string not-word-symbol-re t) not-word-symbol-re)
+        (if (string-match-p (format "%s\\'" not-word-symbol-re) string) not-word-symbol-re
+          (unless lax "\\_>")))))))
 
 (put 'isearch-symbol-regexp 'isearch-message-prefix "symbol ")
 
@@ -1657,9 +1672,10 @@ If LAX is non-nil, the end of the string need not match a symbol boundary."
   "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 doesn't
-always provide the correct matches for `query-replace', so the preferred
-way to run word replacements from Isearch is `M-s w ... M-%'."
+is possible only when `isearch-allow-scroll' is non-nil or
+`isearch-allow-prefix' is non-nil, and it doesn't always provide 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)
@@ -1693,7 +1709,15 @@ way to run word replacements from Isearch is `M-s w ... M-%'."
      (query-replace-read-to
       isearch-string
       (concat "Query replace"
-             (if (or delimited isearch-word) " word" "")
+             (if (or delimited isearch-word)
+                 (let* ((symbol (or delimited isearch-word))
+                        (string (and symbol (symbolp symbol)
+                                     (get symbol 'isearch-message-prefix))))
+                   (if (stringp string)
+                       ;; Move space from the end to the beginning.
+                       (replace-regexp-in-string "\\(.*\\) \\'" " \\1" string)
+                     " word"))
+               "")
              (if isearch-regexp " regexp" "")
              (if (and transient-mark-mode mark-active) " in region" ""))
       isearch-regexp)
@@ -1735,12 +1759,14 @@ characters in that string."
                   ;; No subexpression so collect the entire match.
                   "\\&"
                 ;; Get the regexp for collection pattern.
-                (isearch-done nil t)
-                (isearch-clean-overlays)
-                (let ((default (car occur-collect-regexp-history)))
-                  (read-regexp
-                   (format "Regexp to collect (default %s): " default)
-                   default 'occur-collect-regexp-history)))
+                (let ((default (car occur-collect-regexp-history))
+                      regexp-collect)
+                  (with-isearch-suspended
+                   (setq regexp-collect
+                         (read-regexp
+                          (format "Regexp to collect (default %s): " default)
+                          default 'occur-collect-regexp-history)))
+                  regexp-collect))
             ;; Otherwise normal occur takes numerical prefix argument.
             (when current-prefix-arg
               (prefix-numeric-value current-prefix-arg))))))
@@ -1815,11 +1841,17 @@ If search string is empty, just beep."
   (interactive "p")
   (if (= 0 (length isearch-string))
       (ding)
-    (setq isearch-string (substring isearch-string 0 (- (or arg 1)))
+    (setq isearch-string (substring isearch-string 0
+                                   (- (min (or arg 1)
+                                           (length isearch-string))))
           isearch-message (mapconcat 'isearch-text-char-description
                                      isearch-string "")))
   ;; Use the isearch-other-end as new starting point to be able
   ;; to find the remaining part of the search string again.
+  ;; This is like what `isearch-search-and-update' does,
+  ;; but currently it doesn't support deletion of characters
+  ;; for the case where unsuccessful search may become successful
+  ;; by deletion of characters.
   (if isearch-other-end (goto-char isearch-other-end))
   (isearch-search)
   (isearch-push-state)
@@ -1919,29 +1951,33 @@ Subword is used when `subword-mode' is activated. "
           (forward-word 1))
        (forward-char 1)) (point))))
 
-(defun isearch-yank-word ()
+(defun isearch-yank-word (&optional arg)
   "Pull next word from buffer into search string."
-  (interactive)
-  (isearch-yank-internal (lambda () (forward-word 1) (point))))
+  (interactive "p")
+  (isearch-yank-internal (lambda () (forward-word arg) (point))))
 
-(defun isearch-yank-line ()
+(defun isearch-yank-line (&optional arg)
   "Pull rest of line from buffer into search string."
-  (interactive)
+  (interactive "p")
   (isearch-yank-internal
    (lambda () (let ((inhibit-field-text-motion t))
-               (line-end-position (if (eolp) 2 1))))))
+               (line-end-position (if (eolp) (1+ arg) arg))))))
 
-(defun isearch-char-by-name ()
+(defun isearch-char-by-name (&optional count)
   "Read a character by its Unicode name and add it to the search string.
-Completion is available like in `read-char-by-name' used by `insert-char'."
-  (interactive)
+Completion is available like in `read-char-by-name' used by `insert-char'.
+With argument, add COUNT copies of the character."
+  (interactive "p")
   (with-isearch-suspended
    (let ((char (read-char-by-name "Add character to search (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) "")))))))
+       (let ((string (if (and (integerp count) (> count 1))
+                        (make-string count char)
+                      (char-to-string char))))
+        (setq isearch-new-string (concat isearch-string string)
+              isearch-new-message (concat isearch-message
+                                          (mapconcat 'isearch-text-char-description
+                                                     string ""))))))))
 
 (defun isearch-search-and-update ()
   ;; Do the search and update the display.
@@ -2213,7 +2249,9 @@ the bottom."
 Return the key sequence as a string/vector."
   (isearch-unread-key-sequence keylist)
   (let (overriding-terminal-local-map)
-    (read-key-sequence nil)))  ; This will go through function-key-map, if nec.
+    ;; This will go through function-key-map, if nec.
+    ;; The arg DONT-DOWNCASE-LAST prevents premature shift-translation.
+    (read-key-sequence nil nil t)))
 
 (defun isearch-lookup-scroll-key (key-seq)
   "If KEY-SEQ is bound to a scrolling command, return it as a symbol.
@@ -2271,6 +2309,16 @@ Isearch mode."
                    (lookup-key local-function-key-map key)))
             (while keylist
               (setq key (car keylist))
+              ;; Handle an undefined shifted printing character
+              ;; by downshifting it if that makes it printing.
+              ;; (As read-key-sequence would normally do,
+              ;; if we didn't have a default definition.)
+              (if (and (integerp key)
+                       (memq 'shift (event-modifiers key))
+                       (>= key (+ ?\s (- ?\S-a ?a)))
+                       (/= key (+ 127 (- ?\S-a ?a)))
+                       (<  key (+ 256 (- ?\S-a ?a))))
+                  (setq key (- key (- ?\S-a ?a))))
               ;; If KEY is a printing char, we handle it here
               ;; directly to avoid the input method and keyboard
               ;; coding system translating it.
@@ -2350,6 +2398,13 @@ Isearch mode."
              (isearch-unread-key-sequence keylist)
              (setq main-event (car unread-command-events))
 
+            ;; Don't store special commands in the keyboard macro.
+            (let (overriding-terminal-local-map)
+              (when (memq (key-binding key)
+                          '(kmacro-start-macro
+                            kmacro-end-macro kmacro-end-and-call-macro))
+                (cancel-kbd-macro-events)))
+
             ;; If we got a mouse click event, that event contains the
             ;; window clicked on. maybe it was read with the buffer
             ;; it was clicked on.  If so, that buffer, not the current one,
@@ -2382,9 +2437,10 @@ Isearch mode."
           (t;; otherwise nil
           (isearch-process-search-string key key)))))
 
-(defun isearch-quote-char ()
-  "Quote special characters for incremental search."
-  (interactive)
+(defun isearch-quote-char (&optional count)
+  "Quote special characters for incremental search.
+With argument, add COUNT copies of the character."
+  (interactive "p")
   (let ((char (read-quoted-char (isearch-message t))))
     ;; Assume character codes 0200 - 0377 stand for characters in some
     ;; single-byte character set, and convert them to Emacs
@@ -2392,24 +2448,30 @@ Isearch mode."
     (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))
-      (and enable-multibyte-characters
-          (>= char ?\200)
-          (<= char ?\377)
-          (setq char (unibyte-char-to-multibyte char)))
-      (isearch-process-search-char char))))
-
-(defun isearch-printing-char ()
-  "Add this ordinary printing character to the search string and search."
-  (interactive)
-  (let ((char last-command-event))
+         (isearch-process-search-char char count))
+      ;; This used to assume character codes 0240 - 0377 stand for
+      ;; characters in some single-byte character set, and converted them
+      ;; to Emacs characters.  But in 23.1 this feature is deprecated
+      ;; in favor of inserting the corresponding Unicode characters.
+      ;; (and enable-multibyte-characters
+      ;;      (>= char ?\200)
+      ;;      (<= char ?\377)
+      ;;      (setq char (unibyte-char-to-multibyte char)))
+      (isearch-process-search-char char count))))
+
+(defun isearch-printing-char (&optional char count)
+  "Add this ordinary printing CHAR to the search string and search.
+With argument, add COUNT copies of the character."
+  (interactive (list last-command-event
+                    (prefix-numeric-value current-prefix-arg)))
+  (let ((char (or char last-command-event)))
     (if (= char ?\S-\ )
        (setq char ?\s))
     (if current-input-method
-       (isearch-process-search-multibyte-characters char)
-      (isearch-process-search-char char))))
+       (isearch-process-search-multibyte-characters char count)
+      (isearch-process-search-char char count))))
 
-(defun isearch-process-search-char (char)
+(defun isearch-process-search-char (char &optional count)
   ;; * and ? are special in regexps when not preceded by \.
   ;; } and | are special in regexps when preceded by \.
   ;; Nothing special for + because it matches at least once.
@@ -2418,12 +2480,15 @@ Isearch mode."
    ((eq   char ?\})      (isearch-fallback t t))
    ((eq   char ?|)       (isearch-fallback t nil t)))
 
-  ;; Append the char to the search string, update the message and re-search.
-  (isearch-process-search-string
-   (char-to-string char)
-   (if (>= char ?\200)
-       (char-to-string char)
-     (isearch-text-char-description char))))
+  ;; Append the char(s) to the search string,
+  ;; update the message and re-search.
+  (let* ((string (if (and (integerp count) (> count 1))
+                    (make-string count char)
+                  (char-to-string char)))
+        (message (if (>= char ?\200)
+                     string
+                   (mapconcat 'isearch-text-char-description string ""))))
+    (isearch-process-search-string string message)))
 
 (defun isearch-process-search-string (string message)
   (setq isearch-string (concat isearch-string string)
@@ -2566,13 +2631,13 @@ If there is no completion possible, say so and continue searching."
                              (< (point) isearch-opoint)))
                       "over")
                   (if isearch-wrapped "wrapped ")
-                  (mapconcat (lambda (s)
-                               (and (symbolp s)
-                                    (get s 'isearch-message-prefix)))
-                             (if (consp isearch-filter-predicates)
-                                 isearch-filter-predicates
-                               (list isearch-filter-predicates))
-                             "")
+                   (let ((prefix ""))
+                     (advice-function-mapc
+                      (lambda (_ props)
+                        (let ((np (cdr (assq 'isearch-message-prefix props))))
+                          (if np (setq prefix (concat np prefix)))))
+                      isearch-filter-predicate)
+                    prefix)
                   (if isearch-word
                       (or (and (symbolp isearch-word)
                                (get isearch-word 'isearch-message-prefix))
@@ -2718,15 +2783,8 @@ update the match data, and return point."
          (if (or (not isearch-success)
                  (bobp) (eobp)
                  (= (match-beginning 0) (match-end 0))
-                 ;; When one of filter predicates returns nil,
-                 ;; retry the search.  Otherwise, act according
-                 ;; to search-invisible (open overlays, etc.)
-                 (and (run-hook-with-args-until-failure
-                       'isearch-filter-predicates
-                       (match-beginning 0) (match-end 0))
-                      (or (eq search-invisible t)
-                          (not (isearch-range-invisible
-                                (match-beginning 0) (match-end 0))))))
+                 (funcall isearch-filter-predicate
+                          (match-beginning 0) (match-end 0)))
              (setq retry nil)))
        (setq isearch-just-started nil)
        (if isearch-success
@@ -2738,10 +2796,18 @@ update the match data, and return point."
 
     (invalid-regexp
      (setq isearch-error (car (cdr lossage)))
-     (if (string-match
-         "\\`Premature \\|\\`Unmatched \\|\\`Invalid "
-         isearch-error)
-        (setq isearch-error "incomplete input")))
+     (cond
+      ((string-match
+       "\\`Premature \\|\\`Unmatched \\|\\`Invalid "
+       isearch-error)
+       (setq isearch-error "incomplete input"))
+      ((and (not isearch-regexp)
+           (string-match "\\`Regular expression too big" isearch-error))
+       (cond
+       (isearch-word
+        (setq isearch-error "Too many words"))
+       ((and isearch-lax-whitespace search-whitespace-regexp)
+        (setq isearch-error "Too many spaces for whitespace matching"))))))
 
     (search-failed
      (setq isearch-success nil)
@@ -2903,7 +2969,6 @@ determined by `isearch-range-invisible' unless invisible text can be
 searched too when `search-invisible' is t."
   (or (eq search-invisible t)
       (not (isearch-range-invisible beg end))))
-(make-obsolete 'isearch-filter-visible 'isearch-invisible "24.4")
 
 \f
 ;; General utilities
@@ -3129,11 +3194,8 @@ Attempt to do the search exactly the way the pending Isearch would."
          (if (or (not success)
                  (= (point) bound) ; like (bobp) (eobp) in `isearch-search'.
                  (= (match-beginning 0) (match-end 0))
-                 (and (run-hook-with-args-until-failure
-                       'isearch-filter-predicates
-                       (match-beginning 0) (match-end 0))
-                      (not (isearch-range-invisible
-                            (match-beginning 0) (match-end 0)))))
+                 (funcall isearch-filter-predicate
+                          (match-beginning 0) (match-end 0)))
              (setq retry nil)))
        success)
     (error nil)))