]> 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 e82627616c30310e806ff7c5245defbea5b50124..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 displyed 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.
 
@@ -667,6 +661,7 @@ Each set is a vector of the form:
 (define-key esc-map "\C-r" 'isearch-backward-regexp)
 (define-key search-map "w" 'isearch-forward-word)
 (define-key search-map "_" 'isearch-forward-symbol)
+(define-key search-map "." 'isearch-forward-symbol-at-point)
 
 ;; Entry points to isearch-mode.
 
@@ -782,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
@@ -806,6 +801,25 @@ 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-forward-symbol-at-point ()
+  "Do incremental search forward for a symbol found near point.
+Like ordinary incremental search except that the symbol found at point
+is added to the search string initially as a regexp surrounded
+by symbol boundary constructs \\_< and \\_>.
+See the command `isearch-forward-symbol' for more information."
+  (interactive)
+  (isearch-forward-symbol nil 1)
+  (let ((bounds (find-tag-default-bounds)))
+    (cond
+     (bounds
+      (when (< (car bounds) (point))
+       (goto-char (car bounds)))
+      (isearch-yank-string
+       (buffer-substring-no-properties (car bounds) (cdr bounds))))
+     (t
+      (setq isearch-error "No symbol at point")
+      (isearch-update)))))
+
 \f
 ;; isearch-mode only sets up incremental search for the minor mode.
 ;; All the work is done by the isearch-mode commands.
@@ -995,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)
@@ -1520,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.
@@ -1605,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 ")
 
@@ -1637,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)
@@ -1673,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)
@@ -1715,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))))))
@@ -1752,7 +1798,10 @@ and reads its face argument using `hi-lock-read-face-name'."
     (isearch-done nil t)
     (isearch-clean-overlays))
   (require 'hi-lock nil t)
-  (let ((string (cond (isearch-regexp isearch-string)
+  (let ((regexp (cond ((functionp isearch-word)
+                      (funcall isearch-word isearch-string))
+                     (isearch-word (word-search-regexp isearch-string))
+                     (isearch-regexp isearch-string)
                      ((if (and (eq isearch-case-fold-search t)
                                search-upper-case)
                           (isearch-no-upper-case-p
@@ -1768,7 +1817,7 @@ and reads its face argument using `hi-lock-read-face-name'."
                              (regexp-quote s))))
                        isearch-string ""))
                      (t (regexp-quote isearch-string)))))
-    (hi-lock-face-buffer string (hi-lock-read-face-name)))
+    (hi-lock-face-buffer regexp (hi-lock-read-face-name)))
   (and isearch-recursive-edit (exit-recursive-edit)))
 
 \f
@@ -1792,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)
@@ -1896,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.
@@ -2129,6 +2188,15 @@ If nil, scrolling commands will first cancel Isearch mode."
   :type 'boolean
   :group 'isearch)
 
+(defcustom isearch-allow-prefix t
+  "Whether prefix arguments are allowed during incremental search.
+If non-nil, entering a prefix argument will not terminate the
+search.  This option is ignored \(presumed t) when
+`isearch-allow-scroll' is set."
+  :version "24.4"
+  :type 'boolean
+  :group 'isearch)
+
 (defun isearch-string-out-of-window (isearch-point)
   "Test whether the search string is currently outside of the window.
 Return nil if it's completely visible, or if point is visible,
@@ -2181,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.
@@ -2239,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.
@@ -2281,12 +2361,19 @@ Isearch mode."
           (setq prefix-arg arg)
           (apply 'isearch-unread keylist)
           (isearch-edit-string))
-          ;; Handle a scrolling function.
-          ((and isearch-allow-scroll
-                (progn (setq key (isearch-reread-key-sequence-naturally keylist))
-                       (setq keylist (listify-key-sequence key))
-                       (setq main-event (aref key 0))
-                       (setq scroll-command (isearch-lookup-scroll-key key))))
+          ;; Handle a scrolling function or prefix argument.
+          ((progn
+            (setq key (isearch-reread-key-sequence-naturally keylist)
+                  keylist (listify-key-sequence key)
+                  main-event (aref key 0))
+            (or (and isearch-allow-scroll
+                     (setq scroll-command (isearch-lookup-scroll-key key)))
+                (and isearch-allow-prefix
+                     (let (overriding-terminal-local-map)
+                       (setq scroll-command (key-binding key))
+                       (memq scroll-command
+                             '(universal-argument
+                               negative-argument digit-argument))))))
            ;; From this point onwards, KEY, KEYLIST and MAIN-EVENT hold a
            ;; complete key sequence, possibly as modified by function-key-map,
            ;; not merely the one or two event fragment which invoked
@@ -2311,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,
@@ -2343,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
@@ -2353,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.
@@ -2379,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)
@@ -2527,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))
@@ -2679,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
@@ -2699,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)
@@ -2864,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
@@ -3090,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)))