diff options
Diffstat (limited to 'lisp/isearch.el')
-rw-r--r-- | lisp/isearch.el | 300 |
1 files changed, 178 insertions, 122 deletions
diff --git a/lisp/isearch.el b/lisp/isearch.el index b180e63d8e8..38110d09984 100644 --- a/lisp/isearch.el +++ b/lisp/isearch.el @@ -67,8 +67,28 @@ (defcustom search-exit-option t - "Non-nil means random control characters terminate incremental search." - :type 'boolean) + "Defines what control characters do in incremental search. +If t, random control and meta characters terminate the search +and are then executed normally. +If `edit', edit the search string instead of exiting. +If `move', extend the search string by motion commands +that have the `isearch-move' property on their symbols +equal to `enabled', or the shift-translated command is +not disabled by the value `disabled' of the same property. +If `shift-move', extend the search string by motion commands +while holding down the shift key. +Both `move' and `shift-move' extend the search string by yanking text +that ends at the new position after moving point in the current buffer. +If `append', the characters which you type that are not interpreted by +the incremental search are simply appended to the search string. +If nil, run the command without exiting Isearch." + :type '(choice (const :tag "Terminate incremental search" t) + (const :tag "Edit the search string" edit) + (const :tag "Extend the search string by motion commands" move) + (const :tag "Extend the search string by shifted motion keys" shift-move) + (const :tag "Append control characters to the search string" append) + (const :tag "Don't terminate incremental search" nil)) + :version "27.1") (defcustom search-slow-window-lines 1 "Number of lines in slow search display windows. @@ -305,10 +325,6 @@ this variable is set to the symbol `all-windows'." :group 'isearch :group 'matching) -(define-obsolete-variable-alias 'isearch-lazy-highlight-cleanup - 'lazy-highlight-cleanup - "22.1") - (defcustom lazy-highlight-cleanup t "Controls whether to remove extra highlighting after a search. If this is nil, extra highlighting can be \"manually\" removed with @@ -316,28 +332,16 @@ If this is nil, extra highlighting can be \"manually\" removed with :type 'boolean :group 'lazy-highlight) -(define-obsolete-variable-alias 'isearch-lazy-highlight-initial-delay - 'lazy-highlight-initial-delay - "22.1") - (defcustom lazy-highlight-initial-delay 0.25 "Seconds to wait before beginning to lazily highlight all matches." :type 'number :group 'lazy-highlight) -(define-obsolete-variable-alias 'isearch-lazy-highlight-interval - 'lazy-highlight-interval - "22.1") - (defcustom lazy-highlight-interval 0 ; 0.0625 "Seconds between lazily highlighting successive matches." :type 'number :group 'lazy-highlight) -(define-obsolete-variable-alias 'isearch-lazy-highlight-max-at-a-time - 'lazy-highlight-max-at-a-time - "22.1") - (defcustom lazy-highlight-max-at-a-time nil ; 20 (bug#25751) "Maximum matches to highlight at a time (for `lazy-highlight'). Larger values may reduce Isearch's responsiveness to user input; @@ -480,7 +484,8 @@ This is like `describe-bindings', but displays only Isearch keys." (define-key map [?\S-\ ] 'isearch-printing-char) (define-key map "\C-w" 'isearch-yank-word-or-char) - (define-key map "\M-\C-w" 'isearch-del-char) + (define-key map "\M-\C-w" 'isearch-yank-symbol-or-char) + (define-key map "\M-\C-d" 'isearch-del-char) (define-key map "\M-\C-y" 'isearch-yank-char) (define-key map "\C-y" 'isearch-yank-kill) (define-key map "\M-s\C-e" 'isearch-yank-line) @@ -520,6 +525,8 @@ This is like `describe-bindings', but displays only Isearch keys." (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) @@ -555,6 +562,9 @@ This is like `describe-bindings', but displays only Isearch keys." (defvar isearch-forward nil) ; Searching in the forward direction. (defvar isearch-regexp nil) ; Searching for a regexp. +;; We still support setting this to t for backwards compatibility. +(define-obsolete-variable-alias 'isearch-word + 'isearch-regexp-function "25.1") (defvar isearch-regexp-function nil "Regexp-based search mode for words/symbols. If the value is a function (e.g. `isearch-symbol-regexp'), it is @@ -566,9 +576,6 @@ specifies the prefix string displayed in the search message. This variable is set and changed during isearch. To change the default behavior used for searches, see `search-default-mode' instead.") -;; We still support setting this to t for backwards compatibility. -(define-obsolete-variable-alias 'isearch-word - 'isearch-regexp-function "25.1") (defvar isearch-lax-whitespace t "If non-nil, a space will match a sequence of whitespace chars. @@ -589,7 +596,7 @@ variable by the command `isearch-toggle-lax-whitespace'.") (defvar isearch-cmds nil "Stack of search status elements. Each element is an `isearch--state' struct where the slots are - [STRING MESSAGE POINT SUCCESS FORWARD OTHER-END WORD + [STRING MESSAGE POINT SUCCESS FORWARD OTHER-END WORD/REGEXP-FUNCTION ERROR WRAPPED BARRIER CASE-FOLD-SEARCH POP-FUN]") (defvar isearch-string "") ; The current search string. @@ -1042,13 +1049,12 @@ For a failing search, NOPUSH is t. For going to the minibuffer to edit the search string, NOPUSH is t and EDIT is t." - (if isearch-resume-in-command-history - (let ((command `(isearch-resume ,isearch-string ,isearch-regexp - ,isearch-regexp-function ,isearch-forward - ,isearch-message - ',isearch-case-fold-search))) - (unless (equal (car command-history) command) - (setq command-history (cons command command-history))))) + (when isearch-resume-in-command-history + (add-to-history 'command-history + `(isearch-resume ,isearch-string ,isearch-regexp + ,isearch-regexp-function ,isearch-forward + ,isearch-message + ',isearch-case-fold-search))) (remove-hook 'pre-command-hook 'isearch-pre-command-hook) (remove-hook 'post-command-hook 'isearch-post-command-hook) @@ -1119,19 +1125,29 @@ NOPUSH is t and EDIT is t." (defun isearch-update-ring (string &optional regexp) "Add STRING to the beginning of the search ring. REGEXP if non-nil says use the regexp search ring." - (add-to-history - (if regexp 'regexp-search-ring 'search-ring) - string - (if regexp regexp-search-ring-max search-ring-max))) - -;; Switching buffers should first terminate isearch-mode. -;; ;; For Emacs 19, the frame switch event is handled. -;; (defun isearch-switch-frame-handler () -;; (interactive) ;; Is this necessary? -;; ;; First terminate isearch-mode. -;; (isearch-done) -;; (isearch-clean-overlays) -;; (handle-switch-frame (car (cdr last-command-event)))) + (let ((history-delete-duplicates t)) + (add-to-history + (if regexp 'regexp-search-ring 'search-ring) + (isearch-string-propertize string) + (if regexp regexp-search-ring-max search-ring-max) + t))) + +(defun isearch-string-propertize (string &optional properties) + "Add isearch properties to the isearch string." + (unless properties + (setq properties `(isearch-case-fold-search ,isearch-case-fold-search)) + (unless isearch-regexp + (setq properties (append properties `(isearch-regexp-function ,isearch-regexp-function))))) + (apply 'propertize string properties)) + +(defun isearch-update-from-string-properties (string) + "Update isearch properties from the isearch string" + (when (plist-member (text-properties-at 0 string) 'isearch-case-fold-search) + (setq isearch-case-fold-search + (get-text-property 0 'isearch-case-fold-search string))) + (when (plist-member (text-properties-at 0 string) 'isearch-regexp-function) + (setq isearch-regexp-function + (get-text-property 0 'isearch-regexp-function string)))) ;; The search status structure and stack. @@ -1225,13 +1241,16 @@ If MSG is non-nil, use variable `isearch-message', otherwise `isearch-string'." (length succ-msg) 0)))) +(define-obsolete-variable-alias 'isearch-new-word + 'isearch-new-regexp-function "25.1") + (defvar isearch-new-regexp-function nil "Holds the next `isearch-regexp-function' inside `with-isearch-suspended'. If this is set inside code wrapped by the macro `with-isearch-suspended', then the value set will be used as the `isearch-regexp-function' once isearch resumes.") -(define-obsolete-variable-alias 'isearch-new-word - 'isearch-new-regexp-function "25.1") + +(defvar isearch-suspended nil) (defmacro with-isearch-suspended (&rest body) "Exit Isearch mode, run BODY, and reinvoke the pending search. @@ -1299,6 +1318,8 @@ You can update the global isearch variables by setting new values to isearch-original-minibuffer-message-timeout) old-point old-other-end) + (setq isearch-suspended t) + ;; Actually terminate isearching until editing is done. ;; This is so that the user can do anything without failure, ;; like switch buffers and start another isearch, and return. @@ -1313,6 +1334,8 @@ You can update the global isearch variables by setting new values to (unwind-protect (progn ,@body) + (setq isearch-suspended nil) + ;; Always resume isearching by restarting it. (isearch-mode isearch-forward isearch-regexp @@ -1331,6 +1354,8 @@ You can update the global isearch variables by setting new values to multi-isearch-file-list multi-isearch-file-list-new multi-isearch-buffer-list multi-isearch-buffer-list-new) + (isearch-update-from-string-properties isearch-string) + ;; Restore the minibuffer message before moving point. (funcall (or isearch-message-function #'isearch-message) nil t) @@ -1374,6 +1399,7 @@ You can update the global isearch variables by setting new values to (message ""))))) (quit ; handle abort-recursive-edit + (setq isearch-suspended nil) (isearch-abort) ;; outside of let to restore outside global values ))) @@ -1396,7 +1422,9 @@ The following additional command keys are active while editing. (history-add-new-input nil) ;; Binding minibuffer-history-symbol to nil is a work-around ;; for some incompatibility with gmhist. - (minibuffer-history-symbol)) + (minibuffer-history-symbol) + ;; Search string might have meta information on text properties. + (minibuffer-allow-text-properties t)) (setq isearch-new-string (read-from-minibuffer (isearch-message-prefix nil isearch-nonincremental) @@ -1565,7 +1593,6 @@ Turning on word search turns off regexp mode.") Turning on symbol search turns off regexp mode.") (isearch-define-mode-toggle char-fold "'" char-fold-to-regexp "\ Turning on character-folding turns off regexp mode.") -(put 'char-fold-to-regexp 'isearch-message-prefix "char-fold ") (isearch-define-mode-toggle regexp "r" nil nil (setq isearch-regexp (not isearch-regexp)) @@ -1574,10 +1601,10 @@ Turning on character-folding turns off regexp mode.") (defun isearch--momentary-message (string) "Print STRING at the end of the isearch prompt for 1 second" (let ((message-log-max nil)) - (message "%s%s [%s]" + (message "%s%s%s" (isearch-message-prefix nil isearch-nonincremental) isearch-message - string)) + (propertize (format " [%s]" string) 'face 'minibuffer-prompt))) (sit-for 1)) (isearch-define-mode-toggle lax-whitespace " " nil @@ -1764,8 +1791,6 @@ the beginning or the end of the string need not match a symbol boundary." (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 ") - ;; Search with lax whitespace (defun search-forward-lax-whitespace (string &optional bound noerror count) @@ -1824,7 +1849,9 @@ replacements from Isearch is `M-s w ... M-%'." ;; `exit-recursive-edit' in `isearch-done' that terminates ;; the execution of this command when it is non-nil. ;; We call `exit-recursive-edit' explicitly at the end below. - (isearch-recursive-edit nil)) + (isearch-recursive-edit nil) + (isearch-string-propertized + (isearch-string-propertize isearch-string))) (isearch-done nil t) (isearch-clean-overlays) (if (and isearch-other-end @@ -1837,20 +1864,20 @@ replacements from Isearch is `M-s w ... M-%'." (< (mark) (point)))))) (goto-char isearch-other-end)) (set query-replace-from-history-variable - (cons isearch-string + (cons isearch-string-propertized (symbol-value query-replace-from-history-variable))) (perform-replace - isearch-string + isearch-string-propertized (query-replace-read-to - isearch-string + isearch-string-propertized (concat "Query replace" (isearch--describe-regexp-mode (or delimited isearch-regexp-function) t) (if backward " backward" "") - (if (and transient-mark-mode mark-active) " in region" "")) + (if (use-region-p) " in region" "")) isearch-regexp) t isearch-regexp (or delimited isearch-regexp-function) nil nil - (if (and transient-mark-mode mark-active) (region-beginning)) - (if (and transient-mark-mode mark-active) (region-end)) + (if (use-region-p) (region-beginning)) + (if (use-region-p) (region-end)) backward)) (and isearch-recursive-edit (exit-recursive-edit))) @@ -1913,7 +1940,8 @@ characters in that string." 'isearch-regexp-function-descr (isearch--describe-regexp-mode isearch-regexp-function)) regexp) - nlines))) + nlines + (if (use-region-p) (region-bounds))))) (declare-function hi-lock-read-face-name "hi-lock" ()) @@ -2011,6 +2039,7 @@ If search string is empty, just beep." (defun isearch-yank-kill () "Pull string from kill ring into search string." (interactive) + (unless isearch-mode (isearch-mode t)) (isearch-yank-string (current-kill 0))) (defun isearch-yank-pop () @@ -2084,22 +2113,26 @@ If optional ARG is non-nil, pull in the next ARG characters." (interactive "p") (isearch-yank-internal (lambda () (forward-char arg) (point)))) -(declare-function subword-forward "subword" (&optional arg)) -(defun isearch-yank-word-or-char () - "Pull next character, subword or word from buffer into search string. -Subword is used when `subword-mode' is activated. " - (interactive) +(defun isearch--yank-char-or-syntax (syntax-list fn) (isearch-yank-internal (lambda () - (if (or (= (char-syntax (or (char-after) 0)) ?w) - (= (char-syntax (or (char-after (1+ (point))) 0)) ?w)) - (if (or (and (boundp 'subword-mode) subword-mode) - (and (boundp 'superword-mode) superword-mode)) - (subword-forward 1) - (forward-word 1)) + (if (or (memq (char-syntax (or (char-after) 0)) syntax-list) + (memq (char-syntax (or (char-after (1+ (point))) 0)) + syntax-list)) + (funcall fn 1) (forward-char 1)) (point)))) +(defun isearch-yank-word-or-char () + "Pull next character or word from buffer into search string." + (interactive) + (isearch--yank-char-or-syntax '(?w) 'forward-word)) + +(defun isearch-yank-symbol-or-char () + "Pull next character or symbol from buffer into search string." + (interactive) + (isearch--yank-char-or-syntax '(?w ?_) 'forward-symbol)) + (defun isearch-yank-word (&optional arg) "Pull next word from buffer into search string. If optional ARG is non-nil, pull in the next ARG words." @@ -2381,6 +2414,7 @@ the bottom." (goto-char isearch-point)) (defvar isearch-pre-scroll-point nil) +(defvar isearch-pre-move-point nil) (defun isearch-pre-command-hook () "Decide whether to exit Isearch mode before executing the command. @@ -2388,8 +2422,9 @@ 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." +Otherwise, exit Isearch (when `search-exit-option' is t) +before the command is executed globally with terminated Isearch. +See more for options in `search-exit-option'." (let* ((key (this-single-command-keys)) (main-event (aref key 0))) (cond @@ -2417,22 +2452,51 @@ before the command is executed globally with terminated Isearch." ;; Swallow the up-event. (read-event) (setq this-command 'isearch-edit-string)) + ;; Don't terminate the search for motion commands. + ((or (and (eq search-exit-option 'move) + (symbolp this-command) + (or (eq (get this-command 'isearch-move) 'enabled) + (and (not (eq (get this-command 'isearch-move) 'disabled)) + (stringp (nth 1 (interactive-form this-command))) + (string-match-p "^^" (nth 1 (interactive-form this-command)))))) + (and (eq search-exit-option 'shift-move) + this-command-keys-shift-translated)) + (setq this-command-keys-shift-translated nil) + (setq isearch-pre-move-point (point))) + ;; Append control characters to the search string + ((eq search-exit-option 'append) + (unless (memq nil (mapcar (lambda (k) (characterp k)) key)) + (isearch-process-search-string key key)) + (setq this-command 'ignore)) ;; 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))))) + (isearch-clean-overlays))))) (defun isearch-post-command-hook () - (when isearch-pre-scroll-point + (cond + (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))) + (isearch-update)) + ((memq search-exit-option '(move shift-move)) + (when (and isearch-pre-move-point + (not (eq isearch-pre-move-point (point)))) + (let ((string (buffer-substring-no-properties + (or isearch-other-end isearch-opoint) (point)))) + (if isearch-regexp (setq string (regexp-quote string))) + (setq isearch-string string) + (setq isearch-message (mapconcat 'isearch-text-char-description + string "")) + (setq isearch-yank-flag t) + (setq isearch-forward (<= (or isearch-other-end isearch-opoint) (point))) + (when isearch-forward + (goto-char isearch-pre-move-point)) + (isearch-search-and-update))) + (setq isearch-pre-move-point nil)))) (defun isearch-quote-char (&optional count) "Quote special characters for incremental search. @@ -2517,7 +2581,8 @@ Search is updated accordingly." length))) (setq isearch-string (nth yank-pointer ring) isearch-message (mapconcat 'isearch-text-char-description - isearch-string ""))))) + isearch-string "")) + (isearch-update-from-string-properties isearch-string)))) (defun isearch-ring-adjust (advance) ;; Helper for isearch-ring-advance and isearch-ring-retreat @@ -2631,12 +2696,16 @@ the word mode." (cond ;; 1. Do not use a description on the default search mode, ;; but only if the default search mode is non-nil. - ((or (and search-default-mode - (equal search-default-mode regexp-function)) - ;; Special case where `search-default-mode' is t - ;; (defaults to regexp searches). - (and (eq search-default-mode t) - (eq search-default-mode isearch-regexp))) "") + ((and (or (and search-default-mode + (equal search-default-mode regexp-function)) + ;; Special case where `search-default-mode' is t + ;; (defaults to regexp searches). + (and (eq search-default-mode t) + (eq search-default-mode isearch-regexp))) + ;; Also do not omit description in case of error + ;; in default non-literal search. + (or isearch-success (not (or regexp-function isearch-regexp)))) + "") ;; 2. Use the `isearch-message-prefix' set for ;; `regexp-function' if available. (regexp-function @@ -2679,6 +2748,8 @@ the word mode." (< (point) isearch-opoint))) "over") (if isearch-wrapped "wrapped ") + (if (and (not isearch-success) (not isearch-case-fold-search)) + "case-sensitive ") (let ((prefix "")) (advice-function-mapc (lambda (_ props) @@ -2706,11 +2777,12 @@ the word mode." 'face 'minibuffer-prompt))) (defun isearch-message-suffix (&optional c-q-hack) - (concat (if c-q-hack "^Q" "") - (if isearch-error - (concat " [" isearch-error "]") - "") - (or isearch-message-suffix-add ""))) + (propertize (concat (if c-q-hack "^Q" "") + (if isearch-error + (concat " [" isearch-error "]") + "") + (or isearch-message-suffix-add "")) + 'face 'minibuffer-prompt)) ;; Searching @@ -2733,11 +2805,8 @@ Can be changed via `isearch-search-fun-function' for special needs." (defun isearch--lax-regexp-function-p () "Non-nil if next regexp-function call should be lax." - (not (or isearch-nonincremental - (null (car isearch-cmds)) - (eq (length isearch-string) - (length (isearch--state-string - (car isearch-cmds))))))) + (or (memq this-command '(isearch-printing-char isearch-del-char)) + isearch-yank-flag)) (defun isearch-search-fun-default () "Return default functions to use for the search." @@ -2749,25 +2818,18 @@ Can be changed via `isearch-search-fun-function' for special needs." (isearch-regexp isearch-regexp-lax-whitespace) (t isearch-lax-whitespace)) search-whitespace-regexp))) - (condition-case er - (funcall - (if isearch-forward #'re-search-forward #'re-search-backward) - (cond (isearch-regexp-function - (let ((lax (and (not bound) (isearch--lax-regexp-function-p)))) - (when lax - (setq isearch-adjusted t)) - (if (functionp isearch-regexp-function) - (funcall isearch-regexp-function string lax) - (word-search-regexp string lax)))) - (isearch-regexp string) - (t (regexp-quote string))) - bound noerror count) - (search-failed - (signal (car er) - (let ((prefix (get isearch-regexp-function 'isearch-message-prefix))) - (if (and isearch-regexp-function (stringp prefix)) - (list (format "%s [using %ssearch]" string prefix)) - (cdr er))))))))) + (funcall + (if isearch-forward #'re-search-forward #'re-search-backward) + (cond (isearch-regexp-function + (let ((lax (and (not bound) (isearch--lax-regexp-function-p)))) + (when lax + (setq isearch-adjusted t)) + (if (functionp isearch-regexp-function) + (funcall isearch-regexp-function string lax) + (word-search-regexp string lax)))) + (isearch-regexp string) + (t (regexp-quote string))) + bound noerror count)))) (defun isearch-search-string (string bound noerror) "Search for the first occurrence of STRING or its translation. @@ -2854,7 +2916,7 @@ Optional third argument, if t, means if fail just return nil (no error). (setq isearch-error (car (cdr lossage))) (cond ((string-match - "\\`Premature \\|\\`Unmatched \\|\\`Invalid " + "\\`Premature \\|\\`Unmatched " isearch-error) (setq isearch-error "incomplete input")) ((and (not isearch-regexp) @@ -2893,8 +2955,6 @@ Optional third argument, if t, means if fail just return nil (no error). (funcall (overlay-get ov 'isearch-open-invisible-temporary) ov nil) ;; Store the values for the `invisible' property, and then set it to nil. ;; This way the text hidden by this overlay becomes visible. - - ;; In 19.34 this does not exist so I cannot test it. (overlay-put ov 'isearch-invisible (overlay-get ov 'invisible)) (overlay-put ov 'invisible nil))) @@ -3125,9 +3185,9 @@ since they have special meaning in a regexp." (defvar isearch-lazy-highlight-regexp nil) (defvar isearch-lazy-highlight-lax-whitespace nil) (defvar isearch-lazy-highlight-regexp-lax-whitespace nil) -(defvar isearch-lazy-highlight-regexp-function nil) (define-obsolete-variable-alias 'isearch-lazy-highlight-word 'isearch-lazy-highlight-regexp-function "25.1") +(defvar isearch-lazy-highlight-regexp-function nil) (defvar isearch-lazy-highlight-forward nil) (defvar isearch-lazy-highlight-error nil) @@ -3147,10 +3207,6 @@ This function is called when exiting an incremental search if (cancel-timer isearch-lazy-highlight-timer) (setq isearch-lazy-highlight-timer nil))) -(define-obsolete-function-alias 'isearch-lazy-highlight-cleanup - 'lazy-highlight-cleanup - "22.1") - (defun isearch-lazy-highlight-new-loop (&optional beg end) "Cleanup any previous `lazy-highlight' loop and begin a new one. BEG and END specify the bounds within which highlighting should occur. |