X-Git-Url: https://git.hcoop.net/bpt/emacs.git/blobdiff_plain/a4b0cca119b01dc55bad802ef696c857fe014482..ceac12b73183b3f90804373d5ac5c45c0165e4f9:/lisp/isearch.el diff --git a/lisp/isearch.el b/lisp/isearch.el index df922edf45..b411396cc7 100644 --- a/lisp/isearch.el +++ b/lisp/isearch.el @@ -1,6 +1,6 @@ ;;; 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 ;; Maintainer: FSF @@ -47,7 +47,7 @@ ;; modify the search string before executing the search. There are ;; three commands to terminate the editing: C-s and C-r exit the ;; minibuffer and search forward and reverse respectively, while C-m -;; exits and does a nonincremental search. +;; exits and searches in the last search direction. ;; Exiting immediately from isearch uses isearch-edit-string instead ;; of nonincremental-search, if search-nonincremental-instead is non-nil. @@ -176,7 +176,7 @@ is non-nil if the user quits the search.") (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. @@ -187,12 +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-predicate 'isearch-filter-visible - "Predicate that filters the search hits that would normally be available. +(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.") +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. @@ -346,7 +349,6 @@ A value of nil means highlight all matches." (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) @@ -420,9 +422,6 @@ This is like `describe-bindings', but displays only Isearch keys." ;; 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) @@ -437,8 +436,7 @@ This is like `describe-bindings', but displays only Isearch keys." ;; 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 [escape] meta-map)) ;; Several non-printing chars change the searching behavior. (define-key map "\C-s" 'isearch-repeat-forward) @@ -505,6 +503,7 @@ This is like `describe-bindings', but displays only Isearch keys." (define-key map "\M-e" '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) (define-key map "\M-sw" 'isearch-toggle-word) (define-key map "\M-s_" 'isearch-toggle-symbol) @@ -515,12 +514,9 @@ This is like `describe-bindings', but displays only Isearch keys." (define-key map "\M-so" 'isearch-occur) (define-key map "\M-shr" 'isearch-highlight-regexp) - ;; The key translations defined in the C-x 8 prefix should insert - ;; characters into 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-insert-char-by-name) + ;; 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-x8\r" 'isearch-char-by-name) map) "Keymap for `isearch-mode'.") @@ -528,7 +524,7 @@ This is like `describe-bindings', but displays only Isearch keys." (defvar minibuffer-local-isearch-map (let ((map (make-sparse-keymap))) (set-keymap-parent map minibuffer-local-map) - (define-key map "\r" 'isearch-nonincremental-exit-minibuffer) + (define-key map "\r" 'exit-minibuffer) (define-key map "\M-\t" 'isearch-complete-edit) (define-key map "\C-s" 'isearch-forward-exit-minibuffer) (define-key map "\C-r" 'isearch-reverse-exit-minibuffer) @@ -593,6 +589,11 @@ Each set is a vector of the form: ;; case in the search string is ignored. (defvar isearch-case-fold-search nil) +;; search-invisible while searching. +;; either nil, t, or 'open. 'open means the same as t except that +;; opens hidden overlays. +(defvar isearch-invisible search-invisible) + (defvar isearch-last-case-fold-search nil) ;; Used to save default value while isearch is active @@ -636,6 +637,8 @@ Each set is a vector of the form: ;; 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. @@ -652,6 +655,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. @@ -679,6 +683,8 @@ Type \\[isearch-yank-kill] to yank the last string of killed text. Type \\[isearch-yank-pop] to replace string just yanked into search prompt with string killed before it. Type \\[isearch-quote-char] to quote control character to search for it. +Type \\[isearch-char-by-name] to add a character to search by Unicode name,\ + with completion. \\[isearch-abort] while searching or when search has failed cancels input\ back to what has been found successfully. @@ -689,6 +695,7 @@ If you try to exit with the search string still empty, it invokes nonincremental search. Type \\[isearch-toggle-case-fold] to toggle search case-sensitivity. +Type \\[isearch-toggle-invisible] to toggle search in invisible text. 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. @@ -735,8 +742,9 @@ Other control and meta characters terminate the search and are then executed normally (depending on `search-exit-option'). Likewise for function keys and mouse button events. -If this function is called non-interactively, it does not return to -the calling function until the search is done." +If this function is called non-interactively with a nil NO-RECURSIVE-EDIT, +it does not return to the calling function until the search is done. +See the function `isearch-mode' for more information." (interactive "P\np") (isearch-mode t (not (null regexp-p)) nil (not no-recursive-edit))) @@ -763,7 +771,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 @@ -787,6 +795,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))))) + ;; isearch-mode only sets up incremental search for the minor mode. ;; All the work is done by the isearch-mode commands. @@ -799,7 +826,23 @@ as a regexp. See the command `isearch-forward' for more information." (defun isearch-mode (forward &optional regexp op-fun recursive-edit word) "Start Isearch minor mode. -It is called by the function `isearch-forward' and other related functions." +It is called by the function `isearch-forward' and other related functions. + +The non-nil arg FORWARD means searching in the forward direction. + +The non-nil arg REGEXP does an incremental regular expression search. + +The arg OP-FUN is a function to be called after each input character +is processed. (It is not called after characters that exit the search.) + +When the arg RECURSIVE-EDIT is non-nil, this function behaves modally and +does not return to the calling function until the search is completed. +To behave this way it enters a recursive-edit and exits it when done +isearching. + +The arg WORD, if t, does incremental search for a sequence of words, +ignoring punctuation. If the value is a function, it is called to +convert the search string to a regexp used by regexp search functions." ;; Initialize global vars. (setq isearch-forward forward @@ -808,6 +851,7 @@ It is called by the function `isearch-forward' and other related functions." isearch-op-fun op-fun isearch-last-case-fold-search isearch-case-fold-search isearch-case-fold-search case-fold-search + isearch-invisible search-invisible isearch-string "" isearch-message "" isearch-cmds nil @@ -862,6 +906,9 @@ It is called by the function `isearch-forward' and other related functions." (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 @@ -870,6 +917,8 @@ It is called by the function `isearch-forward' and other related functions." (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) @@ -948,6 +997,8 @@ NOPUSH is t and EDIT is t." (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) @@ -959,7 +1010,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) @@ -1101,13 +1152,14 @@ nonincremental search instead via `isearch-edit-string'." (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) (when (or (not isearch-success) isearch-error) - (while (or (not (isearch--state-success (car cmds))) - (isearch--state-error (car cmds))) + (while (and cmds + (or (not (isearch--state-success (car cmds))) + (isearch--state-error (car cmds)))) (pop cmds)) (setq succ-msg (and cmds (if msg (isearch--state-message (car cmds)) (isearch--state-string (car cmds))))) @@ -1255,7 +1307,6 @@ You can update the global isearch variables by setting new values to The following additional command keys are active while editing. \\ \\[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." @@ -1289,13 +1340,18 @@ The following additional command keys are active while editing. (interactive) (setq isearch-nonincremental t) (exit-minibuffer)) +;; Changing the value of `isearch-nonincremental' has no effect here, +;; because `isearch-edit-string' ignores this change. Thus marked as obsolete. +(make-obsolete 'isearch-nonincremental-exit-minibuffer 'exit-minibuffer "24.4") (defun isearch-forward-exit-minibuffer () + "Resume isearching forward from the minibuffer that edits the search string." (interactive) (setq isearch-new-forward t) (exit-minibuffer)) (defun isearch-reverse-exit-minibuffer () + "Resume isearching backward from the minibuffer that edits the search string." (interactive) (setq isearch-new-forward nil) (exit-minibuffer)) @@ -1441,7 +1497,8 @@ value of the variable `isearch-regexp-lax-whitespace'." (isearch-update)) (defun isearch-toggle-case-fold () - "Toggle case folding in searching on or off." + "Toggle case folding in searching on or off. +Toggles the value of the variable `isearch-case-fold-search'." (interactive) (setq isearch-case-fold-search (if isearch-case-fold-search nil 'yes)) @@ -1454,6 +1511,23 @@ value of the variable `isearch-regexp-lax-whitespace'." (sit-for 1) (isearch-update)) +(defun isearch-toggle-invisible () + "Toggle searching in invisible text on or off. +Toggles the variable `isearch-invisible' between values +nil and a non-nil value of the option `search-invisible' +\(or `open' if `search-invisible' is nil)." + (interactive) + (setq isearch-invisible + (if isearch-invisible nil (or search-invisible 'open))) + (let ((message-log-max nil)) + (message "%s%s [match %svisible text]" + (isearch-message-prefix nil isearch-nonincremental) + isearch-message + (if isearch-invisible "in" ""))) + (setq isearch-success t isearch-adjusted t) + (sit-for 1) + (isearch-update)) + ;; Word search @@ -1461,17 +1535,22 @@ value of the variable `isearch-regexp-lax-whitespace'." "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. @@ -1546,8 +1625,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 ") @@ -1578,9 +1673,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) @@ -1589,6 +1685,7 @@ way to run word replacements from Isearch is `M-s w ... M-%'." ;; set `search-upper-case' to nil to not call ;; `isearch-no-upper-case-p' in `perform-replace' (search-upper-case nil) + (search-invisible isearch-invisible) (replace-lax-whitespace isearch-lax-whitespace) (replace-regexp-lax-whitespace @@ -1613,7 +1710,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) @@ -1655,12 +1760,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)))))) @@ -1692,7 +1799,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 @@ -1708,7 +1818,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))) @@ -1732,11 +1842,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) @@ -1836,28 +1952,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-insert-char-by-name () - "Read a character by its Unicode name and insert it into search string." - (interactive) +(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'. +With argument, add COUNT copies of the character." + (interactive "p") (with-isearch-suspended - (let ((char (read-char-by-name "Insert character (Unicode name or hex): "))) + (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. @@ -1980,26 +2101,6 @@ to the barrier." (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 ;; @@ -2068,6 +2169,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, @@ -2115,176 +2225,63 @@ the bottom." (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-, 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. - ((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)))) - ;; 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)) - - ;; 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))))) - -(defun isearch-quote-char () - "Quote special characters for incremental search." - (interactive) +(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-temporary-overlay-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 + (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)))) ;; Assume character codes 0200 - 0377 stand for characters in some ;; single-byte character set, and convert them to Emacs @@ -2292,24 +2289,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. @@ -2318,12 +2321,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) @@ -2466,6 +2472,13 @@ If there is no completion possible, say so and continue searching." (< (point) isearch-opoint))) "over") (if isearch-wrapped "wrapped ") + (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)) @@ -2520,6 +2533,7 @@ Can be changed via `isearch-search-fun-function' for special needs." ;; 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)))))))) @@ -2596,12 +2610,10 @@ update the match data, and return point." (setq isearch-case-fold-search (isearch-no-upper-case-p isearch-string isearch-regexp))) (condition-case lossage - (let ((inhibit-point-motion-hooks - ;; FIXME: equality comparisons on functions is asking for trouble. - (and (eq isearch-filter-predicate 'isearch-filter-visible) - search-invisible)) + (let ((inhibit-point-motion-hooks isearch-invisible) (inhibit-quit nil) (case-fold-search isearch-case-fold-search) + (search-invisible isearch-invisible) (retry t)) (setq isearch-error nil) (while retry @@ -2625,10 +2637,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) @@ -2960,8 +2980,15 @@ by other Emacs features." (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 @@ -3059,6 +3086,9 @@ Attempt to do the search exactly the way the pending Isearch would." (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)))))