;;; isearch.el --- incremental search minor mode
-;; Copyright (C) 1992-1997, 1999-2013 Free Software Foundation, Inc.
+;; Copyright (C) 1992-1997, 1999-2014 Free Software Foundation, Inc.
;; Author: Daniel LaLiberte <liberte@cs.uiuc.edu>
-;; Maintainer: FSF
+;; Maintainer: emacs-devel@gnu.org
;; Keywords: matching
;; Package: emacs
:version "24.3")
(defcustom search-invisible 'open
- "If t incremental search can match hidden text.
+ "If t incremental search/query-replace can match hidden text.
A nil value means don't match invisible text.
When the value is `open', if the text matched is made invisible by
an overlay having an `invisible' property and that overlay has a property
`isearch-open-invisible', then incremental search will show the contents.
\(This applies when using `outline.el' and `hideshow.el'.)
+
+To temporarily change the value for an active incremental search,
+use \\<isearch-mode-map>\\[isearch-toggle-invisible].
+
+See also the related option `isearch-hide-immediately'.
+
See also `reveal-mode' if you want overlays to automatically be opened
whenever point is in one of them."
:type '(choice (const :tag "Match hidden text" t)
(defcustom isearch-hide-immediately t
"If non-nil, re-hide an invisible match right away.
This variable makes a difference when `search-invisible' is set to `open'.
-It means that after search makes some invisible text visible
-to show the match, it makes the text invisible again when the match moves.
-Ordinarily the text becomes invisible again at the end of the search."
+If nil then do not re-hide opened invisible text when the match moves.
+Whatever the value, all opened invisible text is hidden again after exiting
+the search."
:type 'boolean
:group 'isearch)
to the search status stack.")
(defvar isearch-filter-predicate #'isearch-filter-visible
- "Predicate that filter the search hits that would normally be available.
+ "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
(defvar isearch-help-map
(let ((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)
;; Make all multibyte characters search for themselves.
(set-char-table-range (nth 1 map) (cons #x100 (max-char))
'isearch-printing-char)
- ;; Make function keys, etc, which aren't bound to a scrolling-function
- ;; exit the search.
- (define-key map [t] 'isearch-other-control-char)
;; Single-byte printing chars extend the search string by default.
(setq i ?\s)
;; would be simpler to disable the global keymap, and/or have a
;; default local key binding for any key not otherwise bound.
(let ((meta-map (make-sparse-keymap)))
- (define-key map (char-to-string meta-prefix-char) meta-map)
- (define-key map [escape] meta-map)
- (define-key meta-map [t] 'isearch-other-meta-char))
+ (define-key map (char-to-string meta-prefix-char) meta-map))
;; Several non-printing chars change the searching behavior.
(define-key map "\C-s" 'isearch-repeat-forward)
(define-key map "\M-\C-s" 'isearch-repeat-forward)
(define-key map "\M-\C-r" 'isearch-repeat-backward)
(define-key map "\177" 'isearch-delete-char)
+ (define-key map [backspace] 'isearch-delete-char)
(define-key map "\C-g" 'isearch-abort)
;; This assumes \e is the meta-prefix-char.
(or (= ?\e meta-prefix-char)
(error "Inconsistency in isearch.el"))
(define-key map "\e\e\e" 'isearch-cancel)
- (define-key map [escape escape escape] 'isearch-cancel)
(define-key map "\C-q" 'isearch-quote-char)
(define-key map "\r" 'isearch-exit)
+ (define-key map [return] 'isearch-exit)
(define-key map "\C-j" 'isearch-printing-char)
(define-key map "\t" 'isearch-printing-char)
(define-key map [?\S-\ ] 'isearch-printing-char)
(define-key map "\M-r" 'isearch-toggle-regexp)
(define-key map "\M-e" 'isearch-edit-string)
+ (put 'isearch-toggle-case-fold :advertised-binding "\M-sc")
+ (put 'isearch-toggle-regexp :advertised-binding "\M-sr")
+ (put 'isearch-edit-string :advertised-binding "\M-se")
+
+ (define-key map "\M-se" 'isearch-edit-string)
(define-key map "\M-sc" 'isearch-toggle-case-fold)
(define-key map "\M-si" 'isearch-toggle-invisible)
(define-key map "\M-sr" 'isearch-toggle-regexp)
;; The key translations defined in the C-x 8 prefix should add
;; characters to the search string. See iso-transl.el.
- (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-char-by-name)
map)
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:
+ "Stack of search status elements.
+Each element is an `isearch--state' struct where the slots are
[STRING MESSAGE POINT SUCCESS FORWARD OTHER-END WORD
INVALID-REGEXP WRAPPED BARRIER WITHIN-BRACKETS CASE-FOLD-SEARCH]")
;; isearch is invoked.
(defvar isearch-input-method-local-p nil)
+(defvar isearch--saved-overriding-local-map nil)
+
;; Minor-mode-alist changes - kind of redundant with the
;; echo area, but if isearching in multiple windows, it can be useful.
(setq overriding-terminal-local-map isearch-mode-map)
(run-hooks 'isearch-mode-hook)
+ ;; Remember the initial map possibly modified
+ ;; by external packages in isearch-mode-hook. (Bug#16035)
+ (setq isearch--saved-overriding-local-map overriding-terminal-local-map)
;; Pushing the initial state used to be before running isearch-mode-hook,
;; but a hook might set `isearch-push-state-function' used in
(isearch-update)
+ (add-hook 'pre-command-hook 'isearch-pre-command-hook)
+ (add-hook 'post-command-hook 'isearch-post-command-hook)
(add-hook 'mouse-leave-buffer-hook 'isearch-done)
(add-hook 'kbd-macro-termination-hook 'isearch-done)
(unless (equal (car command-history) command)
(setq command-history (cons command command-history)))))
+ (remove-hook 'pre-command-hook 'isearch-pre-command-hook)
+ (remove-hook 'post-command-hook 'isearch-post-command-hook)
(remove-hook 'mouse-leave-buffer-hook 'isearch-done)
(remove-hook 'kbd-macro-termination-hook 'isearch-done)
(setq isearch-lazy-highlight-start nil)
(isearch-done)
(isearch-clean-overlays))
-(defvar minibuffer-history-symbol) ;; from external package gmhist.el
-
(defun isearch-fail-pos (&optional msg)
"Return position of first mismatch in search string, or nil if none.
If MSG is non-nil, use variable `isearch-message', otherwise `isearch-string'."
(isearch-abort) ;; outside of let to restore outside global values
)))
+(defvar minibuffer-history-symbol) ;; from external package gmhist.el
+
(defun isearch-edit-string ()
"Edit the search string in the minibuffer.
The following additional command keys are active while editing.
(re-search-backward regexp bound noerror count)))
\f
-(defun isearch-query-replace (&optional delimited regexp-flag)
+(defun isearch-query-replace (&optional arg 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
+The ARG (prefix arg if interactive), if non-nil, means replace
+only matches surrounded by word boundaries. A negative prefix
+arg means replace backward. Note that using the prefix arg
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
isearch-lax-whitespace)
(replace-regexp-lax-whitespace
isearch-regexp-lax-whitespace)
+ (delimited (and arg (not (eq arg '-))))
+ (backward (and arg (eq arg '-)))
;; 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)
(if (and isearch-other-end
- (< isearch-other-end (point))
+ (if backward
+ (> isearch-other-end (point))
+ (< isearch-other-end (point)))
(not (and transient-mark-mode mark-active
- (< (mark) (point)))))
+ (if backward
+ (> (mark) (point))
+ (< (mark) (point))))))
(goto-char isearch-other-end))
(set query-replace-from-history-variable
(cons isearch-string
" word"))
"")
(if isearch-regexp " regexp" "")
+ (if backward " backward" "")
(if (and transient-mark-mode mark-active) " in region" ""))
isearch-regexp)
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))
+ backward))
(and isearch-recursive-edit (exit-recursive-edit)))
-(defun isearch-query-replace-regexp (&optional delimited)
+(defun isearch-query-replace-regexp (&optional arg)
"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))
+ (isearch-query-replace arg t))
(defun isearch-occur (regexp &optional nlines)
"Run `occur' using the last search string as the regexp.
(forward-char arg)))
(defun isearch-yank-char (&optional arg)
- "Pull next character from buffer into search string."
+ "Pull next character from buffer into search string.
+If optional ARG is non-nil, pull in the next ARG characters."
(interactive "p")
(isearch-yank-internal (lambda () (forward-char arg) (point))))
(forward-char 1)) (point))))
(defun isearch-yank-word (&optional arg)
- "Pull next word from buffer into search string."
+ "Pull next word from buffer into search string.
+If optional ARG is non-nil, pull in the next ARG words."
(interactive "p")
(isearch-yank-internal (lambda () (forward-word arg) (point))))
(defun isearch-yank-line (&optional arg)
- "Pull rest of line from buffer into search string."
+ "Pull rest of line from buffer into search string.
+If optional ARG is non-nil, yank the next ARG lines."
(interactive "p")
(isearch-yank-internal
(lambda () (let ((inhibit-field-text-motion t))
(min last-other-end isearch-barrier)))
(setq isearch-adjusted t)))))))
-(defun isearch-unread-key-sequence (keylist)
- "Unread the given key-sequence KEYLIST.
-Scroll-bar or mode-line events are processed appropriately."
- (cancel-kbd-macro-events)
- (apply 'isearch-unread keylist)
- ;; If the event was a scroll-bar or mode-line click, the event will have
- ;; been prefixed by a symbol such as vertical-scroll-bar. We must remove
- ;; it here, because this symbol will be attached to the event again next
- ;; time it gets read by read-key-sequence.
- ;;
- ;; (Old comment from isearch-other-meta-char: "Note that we don't have to
- ;; modify the event anymore in 21 because read_key_sequence no longer
- ;; modifies events to produce fake prefix keys.")
- (if (and (> (length keylist) 1)
- (symbolp (car keylist))
- (listp (cadr keylist))
- (not (numberp (posn-point
- (event-start (cadr keylist) )))))
- (pop unread-command-events)))
-
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; scrolling within Isearch mode. Alan Mackenzie (acm@muc.de), 2003/2/24
;;
(recenter 0))))
(goto-char isearch-point))
-(defun isearch-reread-key-sequence-naturally (keylist)
- "Reread key sequence KEYLIST with an inactive Isearch-mode keymap.
-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.
-
-(defun isearch-lookup-scroll-key (key-seq)
- "If KEY-SEQ is bound to a scrolling command, return it as a symbol.
-Otherwise return nil."
- (let* ((overriding-terminal-local-map nil)
- (binding (key-binding key-seq)))
- (and binding (symbolp binding) (commandp binding)
- (or (eq (get binding 'isearch-scroll) t)
- (eq (get binding 'scroll-command) t))
- binding)))
-
-(defalias 'isearch-other-control-char 'isearch-other-meta-char)
-
-(defun isearch-other-meta-char (&optional arg)
- "Process a miscellaneous key sequence in Isearch mode.
-
-Try to convert the current key-sequence to something usable in Isearch
-mode, either by converting it with `function-key-map', downcasing a
-key with C-<upper case>, or finding a \"scrolling command\" bound to
-it. \(In the last case, we may have to read more events.) If so,
-either unread the converted sequence or execute the command.
-
-Otherwise, if `search-exit-option' is non-nil (the default) unread the
-key-sequence and exit the search normally. If it is the symbol
-`edit', the search string is edited in the minibuffer and the meta
-character is unread so that it applies to editing the string.
-
-ARG is the prefix argument. It will be transmitted through to the
-scrolling command or to the command whose key-sequence exits
-Isearch mode."
- (interactive "P")
- (let* ((key (if current-prefix-arg ; not nec the same as ARG
- (substring (this-command-keys) universal-argument-num-events)
- (this-command-keys)))
- (main-event (aref key 0))
- (keylist (listify-key-sequence key))
- scroll-command isearch-point)
- (cond ((and (= (length key) 1)
- (let ((lookup (lookup-key local-function-key-map key)))
- (not (or (null lookup) (integerp lookup)
- (keymapp lookup)))))
- ;; Handle a function key that translates into something else.
- ;; If the key has a global definition too,
- ;; exit and unread the key itself, so its global definition runs.
- ;; Otherwise, unread the translation,
- ;; so that the translated key takes effect within isearch.
- (cancel-kbd-macro-events)
- (if (lookup-key global-map key)
- (progn
- (isearch-done)
- (setq prefix-arg arg)
- (apply 'isearch-unread keylist))
- (setq keylist
- (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
- ;; directly to avoid the input method and keyboard
- ;; coding system translating it.
- (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
- ;; here, we must reread them.
- (setq prefix-arg arg)
- (apply 'isearch-unread keylist)
- (setq keylist nil)))))
- (
- ;; Handle an undefined shifted control character
- ;; by downshifting it if that makes it defined.
- ;; (As read-key-sequence would normally do,
- ;; if we didn't have a default definition.)
- (let ((mods (event-modifiers main-event)))
- (and (integerp main-event)
- (memq 'shift mods)
- (memq 'control mods)
- (not (memq (lookup-key isearch-mode-map
- (let ((copy (copy-sequence key)))
- (aset copy 0
- (- main-event
- (- ?\C-\S-a ?\C-a)))
- copy)
- nil)
- '(nil
- isearch-other-control-char)))))
- (setcar keylist (- main-event (- ?\C-\S-a ?\C-a)))
- (cancel-kbd-macro-events)
- (setq prefix-arg arg)
- (apply 'isearch-unread keylist))
- ((eq search-exit-option 'edit)
- (setq prefix-arg arg)
- (apply 'isearch-unread keylist)
- (isearch-edit-string))
- ;; 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
- ;; isearch-other-meta-char in the first place.
- (setq isearch-point (point))
- (setq prefix-arg arg)
- (command-execute scroll-command)
- (let ((ab-bel (isearch-string-out-of-window isearch-point)))
- (if ab-bel
- (isearch-back-into-window (eq ab-bel 'above) isearch-point)
- (goto-char isearch-point)))
- (isearch-update))
- ;; A mouse click on the isearch message starts editing the search string
- ((and (eq (car-safe main-event) 'down-mouse-1)
- (window-minibuffer-p (posn-window (event-start main-event))))
- ;; Swallow the up-event.
- (read-event)
- (isearch-edit-string))
- (search-exit-option
- (let (window)
- (setq prefix-arg arg)
- (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,
- ;; is in isearch mode. So end the search in that buffer.
-
- ;; ??? I have no idea what this if checks for, but it's
- ;; obviously wrong for the case that a down-mouse event
- ;; on another window invokes this function. The event
- ;; will contain the window clicked on and that window's
- ;; buffer is certainly not always in Isearch mode.
- ;;
- ;; Leave the code in, but check for current buffer not
- ;; being in Isearch mode for now, until someone tells
- ;; what it's really supposed to do.
- ;;
- ;; --gerd 2001-08-10.
-
- (if (and (not isearch-mode)
- (listp main-event)
- (setq window (posn-window (event-start main-event)))
- (windowp window)
- (or (> (minibuffer-depth) 0)
- (not (window-minibuffer-p window))))
- (with-current-buffer (window-buffer window)
- (isearch-done)
- (isearch-clean-overlays))
- (isearch-done)
- (isearch-clean-overlays)
- (setq prefix-arg arg))))
- (t;; otherwise nil
- (isearch-process-search-string key key)))))
+(defvar isearch-pre-scroll-point nil)
+
+(defun isearch-pre-command-hook ()
+ "Decide whether to exit Isearch mode before executing the command.
+Don't exit Isearch if the key sequence that invoked this command
+is bound in `isearch-mode-map', or if the invoked command is
+a prefix argument command (when `isearch-allow-prefix' is non-nil),
+or it is a scrolling command (when `isearch-allow-scroll' is non-nil).
+Otherwise, exit Isearch (when `search-exit-option' is non-nil)
+before the command is executed globally with terminated Isearch."
+ (let* ((key (this-single-command-keys))
+ (main-event (aref key 0)))
+ (cond
+ ;; Don't exit Isearch if we're in the middle of some
+ ;; `set-transient-map' thingy like `universal-argument--mode'.
+ ((not (eq overriding-terminal-local-map isearch--saved-overriding-local-map)))
+ ;; Don't exit Isearch for isearch key bindings.
+ ((commandp (lookup-key isearch-mode-map key nil)))
+ ;; Optionally edit the search string instead of exiting.
+ ((eq search-exit-option 'edit)
+ (setq this-command 'isearch-edit-string))
+ ;; Handle a scrolling function or prefix argument.
+ ((or (and isearch-allow-prefix
+ (memq this-command '(universal-argument
+ digit-argument negative-argument)))
+ (and isearch-allow-scroll
+ (symbolp this-command)
+ (or (eq (get this-command 'isearch-scroll) t)
+ (eq (get this-command 'scroll-command) t))))
+ (when isearch-allow-scroll
+ (setq isearch-pre-scroll-point (point))))
+ ;; A mouse click on the isearch message starts editing the search string.
+ ((and (eq (car-safe main-event) 'down-mouse-1)
+ (window-minibuffer-p (posn-window (event-start main-event))))
+ ;; Swallow the up-event.
+ (read-event)
+ (setq this-command 'isearch-edit-string))
+ ;; Other characters terminate the search and are then executed normally.
+ (search-exit-option
+ (isearch-done)
+ (isearch-clean-overlays))
+ ;; If search-exit-option is nil, run the command without exiting Isearch.
+ (t
+ (isearch-process-search-string key key)))))
+
+(defun isearch-post-command-hook ()
+ (when isearch-pre-scroll-point
+ (let ((ab-bel (isearch-string-out-of-window isearch-pre-scroll-point)))
+ (if ab-bel
+ (isearch-back-into-window (eq ab-bel 'above) isearch-pre-scroll-point)
+ (goto-char isearch-pre-scroll-point)))
+ (setq isearch-pre-scroll-point nil)
+ (isearch-update)))
(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))))
+ (unless (characterp char)
+ (user-error "%s is not a valid character"
+ (key-description (vector char))))
;; Assume character codes 0200 - 0377 stand for characters in some
;; single-byte character set, and convert them to Emacs
;; characters.