;;; isearch.el --- incremental search minor mode
-;; Copyright (C) 1992-1997, 1999-2012 Free Software Foundation, Inc.
+;; Copyright (C) 1992-1997, 1999-2013 Free Software Foundation, Inc.
;; Author: Daniel LaLiberte <liberte@cs.uiuc.edu>
;; Maintainer: FSF
(defcustom search-whitespace-regexp (purecopy "\\s-+")
"If non-nil, regular expression to match a sequence of whitespace chars.
-This applies to regular expression incremental search.
-When you put a space or spaces in the incremental regexp, it stands for
-this, unless it is inside of a regexp construct such as [...] or *, + or ?.
+When you enter a space or spaces in the incremental search, it
+will match any sequence matched by this regexp. As an exception,
+spaces are treated normally in regexp incremental search if they
+occur in a regexp construct like [...] or *, + or ?.
+
+If the value is a string, it applies to both ordinary and
+regexp incremental search. If the value is nil, or
+`isearch-lax-whitespace' is nil for ordinary incremental search, or
+`isearch-regexp-lax-whitespace' is nil for regexp incremental search,
+then each space you type matches literally, against one space.
+
You might want to use something like \"[ \\t\\r\\n]+\" instead.
In the Customization buffer, that is `[' followed by a space,
-a tab, a carriage return (control-M), a newline, and `]+'.
-
-When this is nil, each space you type matches literally, against one space."
- :type '(choice (const :tag "Find Spaces Literally" nil)
+a tab, a carriage return (control-M), a newline, and `]+'."
+ :type '(choice (const :tag "Match Spaces Literally" nil)
regexp)
- :group 'isearch)
+ :group 'isearch
+ :version "24.3")
(defcustom search-invisible 'open
"If t incremental search can match hidden text.
(defvar isearch-message-function nil
"Function to call to display the search prompt.
-If nil, use `isearch-message'.")
+If nil, use function `isearch-message'.")
(defvar isearch-wrap-function nil
"Function to call to wrap the search when search is failed.
they exit Isearch mode before displaying global help."
isearch-help-map)
+(defvar isearch--display-help-action '(nil (inhibit-same-window . t)))
+
(defun isearch-help-for-help ()
"Display Isearch help menu."
(interactive)
- (let (same-window-buffer-names same-window-regexps)
+ (let ((display-buffer-overriding-action isearch--display-help-action))
(isearch-help-for-help-internal))
(isearch-update))
"Show a list of all keys defined in Isearch mode, and their definitions.
This is like `describe-bindings', but displays only Isearch keys."
(interactive)
- (let (same-window-buffer-names same-window-regexps)
+ (let ((display-buffer-overriding-action isearch--display-help-action))
(with-help-window "*Help*"
(with-current-buffer standard-output
(princ "Isearch Mode Bindings:\n")
(defun isearch-describe-key ()
"Display documentation of the function invoked by isearch key."
(interactive)
- (let (same-window-buffer-names same-window-regexps)
+ (let ((display-buffer-overriding-action isearch--display-help-action))
(call-interactively 'describe-key))
(isearch-update))
(defun isearch-describe-mode ()
"Display documentation of Isearch mode."
(interactive)
- (let (same-window-buffer-names same-window-regexps)
+ (let ((display-buffer-overriding-action isearch--display-help-action))
(describe-function 'isearch-forward))
(isearch-update))
(define-key map "\M-sr" 'isearch-toggle-regexp)
(define-key map "\M-sw" 'isearch-toggle-word)
(define-key map "\M-s_" 'isearch-toggle-symbol)
+ (define-key map "\M-s " 'isearch-toggle-lax-whitespace)
(define-key map [?\M-%] 'isearch-query-replace)
(define-key map [?\C-\M-%] 'isearch-query-replace-regexp)
(define-key map "\C-x" nil)
(define-key map [?\C-x t] 'isearch-other-control-char)
(define-key map "\C-x8" nil)
- (define-key map "\C-x8\r" 'isearch-other-control-char)
+ (define-key map "\C-x8\r" 'isearch-insert-char-by-name)
map)
"Keymap for `isearch-mode'.")
The property `isearch-message-prefix' put on this function specifies the
prefix string displayed in the search message.")
+(defvar isearch-lax-whitespace t
+ "If non-nil, a space will match a sequence of whitespace chars.
+When you enter a space or spaces in ordinary incremental search, it
+will match any sequence matched by the regexp defined by the variable
+`search-whitespace-regexp'. If the value is nil, each space you type
+matches literally, against one space. You can toggle the value of this
+variable by the command `isearch-toggle-lax-whitespace'.")
+
+(defvar isearch-regexp-lax-whitespace nil
+ "If non-nil, a space will match a sequence of whitespace chars.
+When you enter a space or spaces in regexp incremental search, it
+will match any sequence matched by the regexp defined by the variable
+`search-whitespace-regexp'. If the value is nil, each space you type
+matches literally, against one space. You can toggle the value of this
+variable by the command `isearch-toggle-lax-whitespace'.")
+
(defvar isearch-cmds nil
"Stack of search status sets.
Each set is a vector of the form:
Type \\[isearch-toggle-regexp] to toggle regular-expression mode.
Type \\[isearch-toggle-word] to toggle word mode.
Type \\[isearch-toggle-symbol] to toggle symbol mode.
+
+Type \\[isearch-toggle-lax-whitespace] to toggle whitespace matching.
+In incremental searches, a space or spaces normally matches any whitespace
+defined by the variable `search-whitespace-regexp'; see also the variables
+`isearch-lax-whitespace' and `isearch-regexp-lax-whitespace'.
+
Type \\[isearch-edit-string] to edit the search string in the minibuffer.
Also supported is a search ring of the previous 16 search strings.
(isearch-mode t (not (null regexp-p)) nil (not no-recursive-edit)))
(defun isearch-forward-regexp (&optional not-regexp no-recursive-edit)
- "\
-Do incremental search forward for regular expression.
+ "Do incremental search forward for regular expression.
With a prefix argument, do a regular string search instead.
Like ordinary incremental search except that your input is treated
as a regexp. See the command `isearch-forward' for more information.
-In regexp incremental searches, a space or spaces normally matches
-any whitespace (the variable `search-whitespace-regexp' controls
-precisely what that means). If you want to search for a literal space
-and nothing else, enter C-q SPC."
+In incremental searches, a space or spaces normally matches any
+whitespace defined by the variable `search-whitespace-regexp'.
+To search for a literal space and nothing else, enter C-q SPC.
+To toggle whitespace matching, use `isearch-toggle-lax-whitespace'."
(interactive "P\np")
(isearch-mode t (null not-regexp) nil (not no-recursive-edit)))
(defun isearch-forward-word (&optional not-word no-recursive-edit)
- "\
-Do incremental search forward for a sequence of words.
+ "Do incremental search forward for a sequence of words.
With a prefix argument, do a regular string search instead.
Like ordinary incremental search except that your input is treated
as a sequence of words without regard to how the words are separated.
(isearch-mode t nil nil (not no-recursive-edit) (null not-word)))
(defun isearch-forward-symbol (&optional not-symbol no-recursive-edit)
- "\
-Do incremental search forward for a symbol.
+ "Do incremental search forward for a symbol.
The prefix argument is currently unused.
Like ordinary incremental search except that your input is treated
as a symbol surrounded by symbol boundary constructs \\_< and \\_>.
(isearch-mode t nil nil (not no-recursive-edit) 'isearch-symbol-regexp))
(defun isearch-backward (&optional regexp-p no-recursive-edit)
- "\
-Do incremental search backward.
+ "Do incremental search backward.
With a prefix argument, do a regular expression search instead.
See the command `isearch-forward' for more information."
(interactive "P\np")
(isearch-mode nil (not (null regexp-p)) nil (not no-recursive-edit)))
(defun isearch-backward-regexp (&optional not-regexp no-recursive-edit)
- "\
-Do incremental search backward for regular expression.
+ "Do incremental search backward for regular expression.
With a prefix argument, do a regular string search instead.
Like ordinary incremental search except that your input is treated
as a regexp. See the command `isearch-forward' for more information."
(if (< isearch-other-end (point)) ; isearch-forward?
(isearch-highlight isearch-other-end (point))
(isearch-highlight (point) isearch-other-end))
- (isearch-dehighlight))
- ))
+ (isearch-dehighlight))))
(setq ;; quit-flag nil not for isearch-mode
isearch-adjusted nil
isearch-yank-flag nil)
(defun isearch-fail-pos (&optional msg)
"Return position of first mismatch in search string, or nil if none.
-If MSG is non-nil, use `isearch-message', otherwise `isearch-string'."
+If MSG is non-nil, use variable `isearch-message', otherwise `isearch-string'."
(let ((cmds isearch-cmds)
(curr-msg (if msg isearch-message isearch-string))
succ-msg)
(length succ-msg)
0))))
-(defun isearch-edit-string ()
- "Edit the search string in the minibuffer.
-The following additional command keys are active while editing.
-\\<minibuffer-local-isearch-map>
-\\[exit-minibuffer] to resume incremental searching with the edited string.
-\\[isearch-nonincremental-exit-minibuffer] to do one nonincremental search.
-\\[isearch-forward-exit-minibuffer] to resume isearching forward.
-\\[isearch-reverse-exit-minibuffer] to resume isearching backward.
-\\[isearch-complete-edit] to complete the search string using the search ring."
-
+(defmacro with-isearch-suspended (&rest body)
+ "Exit Isearch mode, run BODY, and reinvoke the pending search.
+You can update the global isearch variables by setting new values to
+`isearch-new-string', `isearch-new-message', `isearch-new-forward',
+`isearch-new-word', `isearch-new-case-fold'."
;; This code is very hairy for several reasons, explained in the code.
;; Mainly, isearch-mode must be terminated while editing and then restarted.
;; If there were a way to catch any change of buffer from the minibuffer,
;; this could be simplified greatly.
;; Editing doesn't back up the search point. Should it?
- (interactive)
- (condition-case nil
+ `(condition-case nil
(progn
(let ((isearch-nonincremental isearch-nonincremental)
(setq old-point (point) old-other-end isearch-other-end)
(unwind-protect
- (let* ((message-log-max nil)
- ;; Don't add a new search string to the search ring here
- ;; in `read-from-minibuffer'. It should be added only
- ;; by `isearch-update-ring' called from `isearch-done'.
- (history-add-new-input nil)
- ;; Binding minibuffer-history-symbol to nil is a work-around
- ;; for some incompatibility with gmhist.
- (minibuffer-history-symbol))
- (setq isearch-new-string
- (read-from-minibuffer
- (isearch-message-prefix nil isearch-nonincremental)
- (cons isearch-string (1+ (or (isearch-fail-pos)
- (length isearch-string))))
- minibuffer-local-isearch-map nil
- (if isearch-regexp
- (cons 'regexp-search-ring
- (1+ (or regexp-search-ring-yank-pointer -1)))
- (cons 'search-ring
- (1+ (or search-ring-yank-pointer -1))))
- nil t)
- isearch-new-message
- (mapconcat 'isearch-text-char-description
- isearch-new-string "")))
+ (progn ,@body)
;; Set point at the start (end) of old match if forward (backward),
;; so after exiting minibuffer isearch resumes at the start (end)
(isearch-abort) ;; outside of let to restore outside global values
)))
+(defun isearch-edit-string ()
+ "Edit the search string in the minibuffer.
+The following additional command keys are active while editing.
+\\<minibuffer-local-isearch-map>
+\\[exit-minibuffer] to resume incremental searching with the edited string.
+\\[isearch-nonincremental-exit-minibuffer] to do one nonincremental search.
+\\[isearch-forward-exit-minibuffer] to resume isearching forward.
+\\[isearch-reverse-exit-minibuffer] to resume isearching backward.
+\\[isearch-complete-edit] to complete the search string using the search ring."
+ (interactive)
+ (with-isearch-suspended
+ (let* ((message-log-max nil)
+ ;; Don't add a new search string to the search ring here
+ ;; in `read-from-minibuffer'. It should be added only
+ ;; by `isearch-update-ring' called from `isearch-done'.
+ (history-add-new-input nil)
+ ;; Binding minibuffer-history-symbol to nil is a work-around
+ ;; for some incompatibility with gmhist.
+ (minibuffer-history-symbol))
+ (setq isearch-new-string
+ (read-from-minibuffer
+ (isearch-message-prefix nil isearch-nonincremental)
+ (cons isearch-string (1+ (or (isearch-fail-pos)
+ (length isearch-string))))
+ minibuffer-local-isearch-map nil
+ (if isearch-regexp
+ (cons 'regexp-search-ring
+ (1+ (or regexp-search-ring-yank-pointer -1)))
+ (cons 'search-ring
+ (1+ (or search-ring-yank-pointer -1))))
+ nil t)
+ isearch-new-message
+ (mapconcat 'isearch-text-char-description
+ isearch-new-string "")))))
+
(defun isearch-nonincremental-exit-minibuffer ()
(interactive)
(setq isearch-nonincremental t)
(interactive)
(setq isearch-word (unless (eq isearch-word 'isearch-symbol-regexp)
'isearch-symbol-regexp))
+ (if isearch-word (setq isearch-regexp nil))
+ (setq isearch-success t isearch-adjusted t)
+ (isearch-update))
+
+(defun isearch-toggle-lax-whitespace ()
+ "Toggle whitespace matching in searching on or off.
+In ordinary search, toggles the value of the variable
+`isearch-lax-whitespace'. In regexp search, toggles the
+value of the variable `isearch-regexp-lax-whitespace'."
+ (interactive)
+ (if isearch-regexp
+ (setq isearch-regexp-lax-whitespace (not isearch-regexp-lax-whitespace))
+ (setq isearch-lax-whitespace (not isearch-lax-whitespace)))
+ (let ((message-log-max nil))
+ (message "%s%s [%s]"
+ (isearch-message-prefix nil isearch-nonincremental)
+ isearch-message
+ (if (if isearch-regexp
+ isearch-regexp-lax-whitespace
+ isearch-lax-whitespace)
+ "match spaces loosely"
+ "match spaces literally")))
(setq isearch-success t isearch-adjusted t)
+ (sit-for 1)
(isearch-update))
(defun isearch-toggle-case-fold ()
(put 'isearch-symbol-regexp 'isearch-message-prefix "symbol ")
+;; Search with lax whitespace
+
+(defun search-forward-lax-whitespace (string &optional bound noerror count)
+ "Search forward for STRING, matching a sequence of whitespace chars."
+ (let ((search-spaces-regexp search-whitespace-regexp))
+ (re-search-forward (regexp-quote string) bound noerror count)))
+
+(defun search-backward-lax-whitespace (string &optional bound noerror count)
+ "Search backward for STRING, matching a sequence of whitespace chars."
+ (let ((search-spaces-regexp search-whitespace-regexp))
+ (re-search-backward (regexp-quote string) bound noerror count)))
+
+(defun re-search-forward-lax-whitespace (regexp &optional bound noerror count)
+ "Search forward for REGEXP, matching a sequence of whitespace chars."
+ (let ((search-spaces-regexp search-whitespace-regexp))
+ (re-search-forward regexp bound noerror count)))
+
+(defun re-search-backward-lax-whitespace (regexp &optional bound noerror count)
+ "Search backward for REGEXP, matching a sequence of whitespace chars."
+ (let ((search-spaces-regexp search-whitespace-regexp))
+ (re-search-backward regexp bound noerror count)))
+
\f
(defun isearch-query-replace (&optional delimited regexp-flag)
"Start `query-replace' with string to replace from last search string.
;; set `search-upper-case' to nil to not call
;; `isearch-no-upper-case-p' in `perform-replace'
(search-upper-case nil)
+ (replace-lax-whitespace
+ isearch-lax-whitespace)
+ (replace-regexp-lax-whitespace
+ isearch-regexp-lax-whitespace)
;; Set `isearch-recursive-edit' to nil to prevent calling
;; `exit-recursive-edit' in `isearch-done' that terminates
;; the execution of this command when it is non-nil.
(isearch-done nil t)
(isearch-clean-overlays)
(let ((default (car occur-collect-regexp-history)))
- (read-string
+ (read-regexp
(format "Regexp to collect (default %s): " default)
- nil 'occur-collect-regexp-history default)))
+ default 'occur-collect-regexp-history)))
;; Otherwise normal occur takes numerical prefix argument.
(when current-prefix-arg
(prefix-numeric-value current-prefix-arg))))))
;; Set `search-upper-case' to nil to not call
;; `isearch-no-upper-case-p' in `occur-1'.
(search-upper-case nil)
- (search-spaces-regexp (if isearch-regexp search-whitespace-regexp)))
+ (search-spaces-regexp
+ (if (if isearch-regexp
+ isearch-regexp-lax-whitespace
+ isearch-lax-whitespace)
+ search-whitespace-regexp)))
(occur regexp nlines)))
(declare-function hi-lock-read-face-name "hi-lock" ())
\f
(defun isearch-delete-char ()
"Discard last input item and move point back.
+Last input means the last character or the last isearch command
+that added or deleted characters from the search string,
+moved point, toggled regexp mode or case-sensitivity, etc.
If no previous match was done, just beep."
(interactive)
(if (null (cdr isearch-cmds))
(defun isearch-del-char (&optional arg)
"Delete character from end of search string and search again.
+Unlike `isearch-delete-char', it only deletes the last character,
+but doesn't cancel the effect of other isearch command.
If search string is empty, just beep."
(interactive "p")
(if (= 0 (length isearch-string))
(lambda () (let ((inhibit-field-text-motion t))
(line-end-position (if (eolp) 2 1))))))
+(defun isearch-insert-char-by-name ()
+ "Read a character by its Unicode name and insert it into search string."
+ (interactive)
+ (with-isearch-suspended
+ (let ((char (read-char-by-name "Insert character (Unicode name or hex): ")))
+ (when char
+ (setq isearch-new-string (concat isearch-string (string char))
+ isearch-new-message (concat isearch-message
+ (mapconcat 'isearch-text-char-description
+ (string char) "")))))))
+
(defun isearch-search-and-update ()
;; Do the search and update the display.
(when (or isearch-success
(setq prefix-arg arg)
(apply 'isearch-unread keylist))
(setq keylist
- (listify-key-sequence (lookup-key local-function-key-map key)))
+ (listify-key-sequence
+ (lookup-key local-function-key-map key)))
(while keylist
(setq key (car keylist))
;; If KEY is a printing char, we handle it here
(if (and (integerp key)
(>= key ?\s) (/= key 127) (< key 256))
(progn
+ ;; Ensure that the processed char is recorded in
+ ;; the keyboard macro, if any (Bug#4894)
+ (store-kbd-macro-event key)
(isearch-process-search-char key)
(setq keylist (cdr keylist)))
;; As the remaining keys in KEYLIST can't be handled
;; Assume character codes 0200 - 0377 stand for characters in some
;; single-byte character set, and convert them to Emacs
;; characters.
- (if (and isearch-regexp (= char ?\s))
+ (if (and isearch-regexp isearch-regexp-lax-whitespace (= char ?\s))
(if (subregexp-context-p isearch-string (length isearch-string))
(isearch-process-search-string "[ ]" " ")
(isearch-process-search-char char))
;; the user adds and removes characters in the search string
;; (or when using nonincremental word isearch)
(let ((lax (not (or isearch-nonincremental
+ (null (car isearch-cmds))
(eq (length isearch-string)
(length (isearch--state-string
(car isearch-cmds))))))))
(funcall isearch-word string lax)
(word-search-regexp string lax))
bound noerror count))))
+ ((and isearch-regexp isearch-regexp-lax-whitespace
+ search-whitespace-regexp)
+ (if isearch-forward
+ 're-search-forward-lax-whitespace
+ 're-search-backward-lax-whitespace))
(isearch-regexp
(if isearch-forward 're-search-forward 're-search-backward))
+ ((and isearch-lax-whitespace search-whitespace-regexp)
+ (if isearch-forward
+ 'search-forward-lax-whitespace
+ 'search-backward-lax-whitespace))
(t
(if isearch-forward 'search-forward 'search-backward))))
search-invisible))
(inhibit-quit nil)
(case-fold-search isearch-case-fold-search)
- (search-spaces-regexp search-whitespace-regexp)
(retry t))
(setq isearch-error nil)
(while retry
(defvar isearch-lazy-highlight-window-end nil)
(defvar isearch-lazy-highlight-case-fold-search nil)
(defvar isearch-lazy-highlight-regexp nil)
-(defvar isearch-lazy-highlight-space-regexp nil)
+(defvar isearch-lazy-highlight-lax-whitespace nil)
+(defvar isearch-lazy-highlight-regexp-lax-whitespace nil)
(defvar isearch-lazy-highlight-word nil)
(defvar isearch-lazy-highlight-forward nil)
(defvar isearch-lazy-highlight-error nil)
isearch-regexp))
(not (eq isearch-lazy-highlight-word
isearch-word))
+ (not (eq isearch-lazy-highlight-lax-whitespace
+ isearch-lax-whitespace))
+ (not (eq isearch-lazy-highlight-regexp-lax-whitespace
+ isearch-regexp-lax-whitespace))
(not (= (window-start)
isearch-lazy-highlight-window-start))
(not (= (window-end) ; Window may have been split/joined.
(setq isearch-lazy-highlight-window (selected-window)
isearch-lazy-highlight-window-start (window-start)
isearch-lazy-highlight-window-end (window-end)
- isearch-lazy-highlight-start (point)
- isearch-lazy-highlight-end (point)
+ ;; Start lazy-highlighting at the beginning of the found
+ ;; match (`isearch-other-end'). If no match, use point.
+ ;; One of the next two variables (depending on search direction)
+ ;; is used to define the starting position of lazy-highlighting
+ ;; and also to remember the current position of point between
+ ;; calls of `isearch-lazy-highlight-update', and another variable
+ ;; is used to define where the wrapped search must stop.
+ isearch-lazy-highlight-start (or isearch-other-end (point))
+ isearch-lazy-highlight-end (or isearch-other-end (point))
isearch-lazy-highlight-wrapped nil
isearch-lazy-highlight-last-string isearch-string
isearch-lazy-highlight-case-fold-search isearch-case-fold-search
isearch-lazy-highlight-regexp isearch-regexp
- isearch-lazy-highlight-space-regexp search-whitespace-regexp
+ isearch-lazy-highlight-lax-whitespace isearch-lax-whitespace
+ isearch-lazy-highlight-regexp-lax-whitespace isearch-regexp-lax-whitespace
isearch-lazy-highlight-word isearch-word
isearch-lazy-highlight-forward isearch-forward)
(unless (equal isearch-string "")
(condition-case nil
(let ((case-fold-search isearch-lazy-highlight-case-fold-search)
(isearch-regexp isearch-lazy-highlight-regexp)
- (search-spaces-regexp isearch-lazy-highlight-space-regexp)
(isearch-word isearch-lazy-highlight-word)
+ (isearch-lax-whitespace
+ isearch-lazy-highlight-lax-whitespace)
+ (isearch-regexp-lax-whitespace
+ isearch-lazy-highlight-regexp-lax-whitespace)
+ (isearch-forward isearch-lazy-highlight-forward)
(search-invisible nil) ; don't match invisible text
(retry t)
(success nil)
- (isearch-forward isearch-lazy-highlight-forward)
(bound (if isearch-lazy-highlight-forward
(min (or isearch-lazy-highlight-end-limit (point-max))
(if isearch-lazy-highlight-wrapped
(overlay-put ov 'priority 1000)
(overlay-put ov 'face lazy-highlight-face)
(overlay-put ov 'window (selected-window))))
+ ;; Remember the current position of point for
+ ;; the next call of `isearch-lazy-highlight-update'
+ ;; when `lazy-highlight-max-at-a-time' is too small.
(if isearch-lazy-highlight-forward
(setq isearch-lazy-highlight-end (point))
(setq isearch-lazy-highlight-start (point)))))