diff options
Diffstat (limited to 'lisp/isearch.el')
-rw-r--r-- | lisp/isearch.el | 248 |
1 files changed, 202 insertions, 46 deletions
diff --git a/lisp/isearch.el b/lisp/isearch.el index a68c3a4748c..31fbdf01bf2 100644 --- a/lisp/isearch.el +++ b/lisp/isearch.el @@ -430,13 +430,13 @@ and doesn't remove full-buffer highlighting after a search." (defface lazy-highlight '((((class color) (min-colors 88) (background light)) - (:background "paleturquoise")) + (:background "paleturquoise" :distant-foreground "black")) (((class color) (min-colors 88) (background dark)) - (:background "paleturquoise4")) + (:background "paleturquoise4" :distant-foreground "white")) (((class color) (min-colors 16)) - (:background "turquoise3")) + (:background "turquoise3" :distant-foreground "white")) (((class color) (min-colors 8)) - (:background "turquoise3")) + (:background "turquoise3" :distant-foreground "white")) (t (:underline t))) "Face for lazy highlighting of matches other than the current one." :group 'lazy-highlight @@ -488,9 +488,9 @@ and doesn't remove full-buffer highlighting after a search." "You have typed %THIS-KEY%, the help character. Type a Help option: \(Type \\<isearch-help-map>\\[help-quit] to exit the Help command.) -\\[isearch-describe-bindings] Display all Isearch key bindings. -\\[isearch-describe-key] KEYS Display full documentation of Isearch key sequence. -\\[isearch-describe-mode] Display documentation of Isearch mode. + \\[isearch-describe-bindings] Display all Isearch key bindings. + \\[isearch-describe-key] Display full documentation of Isearch key sequence. + \\[isearch-describe-mode] Display documentation of Isearch mode. You can't type here other help keys available in the global help map, but outside of this help window when you type them in Isearch mode, @@ -668,6 +668,7 @@ This is like `describe-bindings', but displays only Isearch keys." ;; 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) + (define-key map "\C-x8e\r" 'isearch-emoji-by-name) map) "Keymap for `isearch-mode'.") @@ -758,6 +759,8 @@ This is like `describe-bindings', but displays only Isearch keys." :help "Search for literal char"] ["Search for char by name" isearch-char-by-name :help "Search for character by name"] + ["Search for Emoji by name" isearch-emoji-by-name + :help "Search for Emoji by its Unicode name"] "---" ["Toggle input method" isearch-toggle-input-method :help "Toggle input method for search"] @@ -865,14 +868,16 @@ This variable is set and changed during isearch. To change the default behavior used for searches, see `search-default-mode' instead.") -(defvar isearch-lax-whitespace t +(defcustom 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', usually bound to -`M-s SPC' during isearch.") +`M-s SPC' during isearch." + :type 'boolean + :version "25.1") (defvar isearch-regexp-lax-whitespace nil "If non-nil, a space will match a sequence of whitespace chars. @@ -1810,17 +1815,19 @@ The following additional command keys are active while editing. ;; 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) - (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) + (minibuffer-with-setup-hook + (minibuffer-lazy-highlight-setup) + (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 ""))))) @@ -2063,7 +2070,7 @@ The command then executes BODY and updates the isearch prompt." #',function)) (setq isearch-regexp nil))) ,@body - (setq isearch-success t isearch-adjusted t) + (setq isearch-success t isearch-adjusted 'toggle) (isearch-update)) (define-key isearch-mode-map ,key #',command-name) ,@(when (and function (symbolp function)) @@ -2488,8 +2495,8 @@ The arguments passed to `highlight-regexp' are the regexp from the last search and the face from `hi-lock-read-face-name'." (interactive) (isearch--highlight-regexp-or-lines - #'(lambda (regexp face lighter) - (highlight-regexp regexp face nil lighter)))) + (lambda (regexp face lighter) + (highlight-regexp regexp face nil lighter)))) (defun isearch-highlight-lines-matching-regexp () "Exit Isearch mode and call `highlight-lines-matching-regexp'. @@ -2497,8 +2504,8 @@ The arguments passed to `highlight-lines-matching-regexp' are the regexp from the last search and the face from `hi-lock-read-face-name'." (interactive) (isearch--highlight-regexp-or-lines - #'(lambda (regexp face _lighter) - (highlight-lines-matching-regexp regexp face)))) + (lambda (regexp face _lighter) + (highlight-lines-matching-regexp regexp face)))) (defun isearch-delete-char () @@ -2514,6 +2521,11 @@ If no input items have been entered yet, just beep." (if (null (cdr isearch-cmds)) (ding) (isearch-pop-state)) + ;; When going back to the hidden match, reopen it. + (when (and (eq search-invisible 'open) isearch-hide-immediately + isearch-other-end) + (isearch-range-invisible (min (point) isearch-other-end) + (max (point) isearch-other-end))) (isearch-update)) (defun isearch-del-char (&optional arg) @@ -2629,9 +2641,10 @@ is bound to outside of Isearch." ;; Key search depends on mode (bug#47755) (isearch-mode nil)) (key-binding (this-command-keys-vector) t)))) - (if (and (window-minibuffer-p w) - (not (minibuffer-window-active-p w))) ; in echo area - (isearch-yank-x-selection) + (if (or mouse-yank-at-point + (and (window-minibuffer-p w) + (not (minibuffer-window-active-p w)))) ; in echo area + (isearch-yank-x-selection) (when (functionp binding) (call-interactively binding))))) @@ -2670,7 +2683,7 @@ or it might return the position of the end of the line." (interactive "p") (if (eobp) (insert - (with-current-buffer (cadr (buffer-list)) + (with-minibuffer-selected-window (buffer-substring-no-properties (point) (progn (forward-char arg) (point))))) (forward-char arg))) @@ -2752,6 +2765,24 @@ With argument, add COUNT copies of the character." (mapconcat 'isearch-text-char-description string "")))))))) +(defun isearch-emoji-by-name (&optional count) + "Read an Emoji name and add it to the search string COUNT times. +COUNT (interactively, the prefix argument) defaults to 1. +The command accepts Unicode names like \"smiling face\" or +\"heart with arrow\", and completion is available." + (interactive "p") + (with-isearch-suspended + (let ((emoji (with-temp-buffer + (emoji-search) + (if (and (integerp count) (> count 1)) + (apply 'concat (make-list count (buffer-string))) + (buffer-string))))) + (when emoji + (setq isearch-new-string (concat isearch-string emoji) + isearch-new-message (concat isearch-message + (mapconcat 'isearch-text-char-description + emoji ""))))))) + (defun isearch-search-and-update () "Do the search and update the display." (when (or isearch-success @@ -2918,6 +2949,7 @@ to the barrier." (put 'scroll-other-window-down 'isearch-scroll t) (put 'beginning-of-buffer-other-window 'isearch-scroll t) (put 'end-of-buffer-other-window 'isearch-scroll t) +(put 'recenter-other-window 'isearch-scroll t) ;; Commands which change the window layout (put 'delete-other-windows 'isearch-scroll t) @@ -2932,6 +2964,9 @@ to the barrier." (put 'mouse-drag-mode-line 'isearch-scroll t) (put 'mouse-drag-vertical-line 'isearch-scroll t) +;; For context menu with isearch submenu +(put 'context-menu-open 'isearch-scroll t) + ;; Aliases for split-window-* (put 'split-window-vertically 'isearch-scroll t) (put 'split-window-horizontally 'isearch-scroll t) @@ -3422,7 +3457,7 @@ the word mode." ;; If currently failing, display no ellipsis. (or isearch-success (setq ellipsis nil)) (let ((m (concat (if isearch-success "" "failing ") - (if isearch-adjusted "pending " "") + (if (eq isearch-adjusted t) "pending " "") (if (and isearch-wrapped (not isearch-wrap-function) (if isearch-forward @@ -3435,11 +3470,13 @@ the word mode." (if (and (not isearch-success) (not isearch-case-fold-search)) "case-sensitive ") (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) + (dolist (advice-function (list isearch-filter-predicate + isearch-search-fun-function)) + (advice-function-mapc + (lambda (_ props) + (let ((np (cdr (assq 'isearch-message-prefix props)))) + (if np (setq prefix (concat np prefix))))) + advice-function)) prefix) (isearch--describe-regexp-mode isearch-regexp-function) (cond @@ -3526,10 +3563,10 @@ Can be changed via `isearch-search-fun-function' for special needs." ;; (Bug#35802). (regexp (cond (isearch-regexp-function - (let ((lax (and (not bound) + (let ((lax (and (not bound) ; not lazy-highlight (isearch--lax-regexp-function-p)))) (when lax - (setq isearch-adjusted t)) + (setq isearch-adjusted 'lax)) (if (functionp isearch-regexp-function) (funcall isearch-regexp-function string lax) (word-search-regexp string lax)))) @@ -3797,8 +3834,9 @@ Isearch, at least partially, as determined by `isearch-range-invisible'. If `search-invisible' is t, which allows Isearch matches inside invisible text, this function will always return non-nil, regardless of what `isearch-range-invisible' says." - (or (eq search-invisible t) - (not (isearch-range-invisible beg end)))) + (and (not (text-property-not-all beg end 'inhibit-isearch nil)) + (or (eq search-invisible t) + (not (isearch-range-invisible beg end))))) ;; General utilities @@ -3969,6 +4007,8 @@ since they have special meaning in a regexp." (defvar isearch-lazy-count-current nil) (defvar isearch-lazy-count-total nil) (defvar isearch-lazy-count-hash (make-hash-table)) +(defvar lazy-count-update-hook nil + "Hook run after new lazy count results are computed.") (defun lazy-highlight-cleanup (&optional force procrastinate) "Stop lazy highlighting and remove extra highlighting from current buffer. @@ -4027,7 +4067,7 @@ by other Emacs features." isearch-lazy-highlight-window-end)))))) ;; something important did indeed change (lazy-highlight-cleanup t (not (equal isearch-string ""))) ;stop old timer - (when (and isearch-lazy-count isearch-mode (null isearch-message-function)) + (when isearch-lazy-count (when (or (equal isearch-string "") ;; Check if this place was reached by a condition above ;; other than changed window boundaries (that shouldn't @@ -4046,7 +4086,10 @@ by other Emacs features." (setq isearch-lazy-count-current nil isearch-lazy-count-total nil) ;; Delay updating the message if possible, to avoid flicker - (when (string-equal isearch-string "") (isearch-message)))) + (when (string-equal isearch-string "") + (when (and isearch-mode (null isearch-message-function)) + (isearch-message)) + (run-hooks 'lazy-count-update-hook)))) (setq isearch-lazy-highlight-window-start-changed nil) (setq isearch-lazy-highlight-window-end-changed nil) (setq isearch-lazy-highlight-error isearch-error) @@ -4099,13 +4142,15 @@ by other Emacs features." 'isearch-lazy-highlight-start)))) ;; Update the current match number only in isearch-mode and ;; unless isearch-mode is used specially with isearch-message-function - (when (and isearch-lazy-count isearch-mode (null isearch-message-function)) + (when isearch-lazy-count ;; Update isearch-lazy-count-current only when it was already set ;; at the end of isearch-lazy-highlight-buffer-update (when isearch-lazy-count-current (setq isearch-lazy-count-current (gethash (point) isearch-lazy-count-hash 0)) - (isearch-message)))) + (when (and isearch-mode (null isearch-message-function)) + (isearch-message)) + (run-hooks 'lazy-count-update-hook)))) (defun isearch-lazy-highlight-search (string bound) "Search ahead for the next or previous match, for lazy highlighting. @@ -4306,16 +4351,110 @@ Attempt to do the search exactly the way the pending Isearch would." (setq looping nil nomore t)))) (if nomore - (when (and isearch-lazy-count isearch-mode (null isearch-message-function)) + (when isearch-lazy-count (unless isearch-lazy-count-total (setq isearch-lazy-count-total 0)) (setq isearch-lazy-count-current (gethash opoint isearch-lazy-count-hash 0)) - (isearch-message)) + (when (and isearch-mode (null isearch-message-function)) + (isearch-message))) (setq isearch-lazy-highlight-timer (run-at-time lazy-highlight-interval nil - 'isearch-lazy-highlight-buffer-update))))))))) + 'isearch-lazy-highlight-buffer-update))))) + (when (and nomore isearch-lazy-count) + (run-hooks 'lazy-count-update-hook)))))) + +;; Reading from minibuffer with lazy highlight and match count + +(defcustom minibuffer-lazy-count-format "%s " + "Format of the total number of matches for the prompt prefix." + :type '(choice (const :tag "Don't display a count" nil) + (string :tag "Display match count" "%s ")) + :group 'lazy-count + :version "29.1") + +(cl-defun minibuffer-lazy-highlight-setup + (&key (highlight isearch-lazy-highlight) + (cleanup lazy-highlight-cleanup) + (transform #'identity) + (filter nil) + (regexp isearch-regexp) + (regexp-function isearch-regexp-function) + (case-fold isearch-case-fold-search) + (lax-whitespace (if regexp + isearch-regexp-lax-whitespace + isearch-lax-whitespace))) + "Set up minibuffer for lazy highlight of matches in the original window. + +This function return a closure intended to be added to +`minibuffer-setup-hook'. It accepts the following keyword +arguments, all of which have a default based on the current +isearch settings. + +HIGHLIGHT: Whether to perform lazy highlight. +CLEANUP: Whether to clean up the lazy highlight when the minibuffer +exits. +TRANSFORM: A function taking one argument, the minibuffer contents, +and returning the `isearch-string' to use for lazy highlighting. +FILTER: A function to add to `isearch-filter-predicate'. +REGEXP: The value of `isearch-regexp' to use for lazy highlighting. +REGEXP-FUNCTION: The value of `isearch-regexp-function' to use for +lazy highlighting. +CASE-FOLD: The value of `isearch-case-fold' to use for lazy +highlighting. +LAX-WHITESPACE: The value of `isearch-lax-whitespace' and +`isearch-regexp-lax-whitespace' to use for lazy highlighting." + (if (not highlight) + #'ignore + (let ((unwind (make-symbol "minibuffer-lazy-highlight--unwind")) + (after-change (make-symbol "minibuffer-lazy-highlight--after-change")) + (display-count (make-symbol "minibuffer-lazy-highlight--display-count")) + (buffer (current-buffer)) + overlay) + (fset unwind + (lambda () + (when filter + (with-current-buffer buffer + (remove-function (local 'isearch-filter-predicate) filter))) + (remove-hook 'lazy-count-update-hook display-count) + (when overlay (delete-overlay overlay)) + (remove-hook 'after-change-functions after-change t) + (remove-hook 'minibuffer-exit-hook unwind t) + (let ((lazy-highlight-cleanup cleanup)) + (lazy-highlight-cleanup)))) + (fset after-change + (lambda (_beg _end _len) + (let ((inhibit-redisplay t) ;; Avoid cursor flickering + (string (minibuffer-contents))) + (with-minibuffer-selected-window + (let* ((isearch-forward t) + (isearch-regexp regexp) + (isearch-regexp-function regexp-function) + (isearch-case-fold-search case-fold) + (isearch-lax-whitespace lax-whitespace) + (isearch-regexp-lax-whitespace lax-whitespace) + (isearch-string (funcall transform string))) + (isearch-lazy-highlight-new-loop)))))) + (fset display-count + (lambda () + (overlay-put overlay 'before-string + (and isearch-lazy-count-total + (not isearch-error) + (format minibuffer-lazy-count-format + isearch-lazy-count-total))))) + (lambda () + (add-hook 'minibuffer-exit-hook unwind nil t) + (add-hook 'after-change-functions after-change nil t) + (when minibuffer-lazy-count-format + (setq overlay (make-overlay (point-min) (point-min) (current-buffer) t)) + (add-hook 'lazy-count-update-hook display-count)) + (when filter + (with-current-buffer buffer + (add-function :after-while (local 'isearch-filter-predicate) filter))) + (funcall after-change nil nil nil))))) + + (defun isearch-resume (string regexp word forward message case-fold) "Resume an incremental search. STRING is the string or regexp searched for. @@ -4331,6 +4470,23 @@ CASE-FOLD non-nil means the search was case-insensitive." (isearch-search) (isearch-update)) + +(defvar isearch-fold-quotes-mode--state) +(define-minor-mode isearch-fold-quotes-mode + "Minor mode to aid searching for \\=` characters in help modes." + :lighter "" + (if isearch-fold-quotes-mode + (setq-local isearch-fold-quotes-mode--state + (buffer-local-set-state + search-default-mode + (lambda (string &optional _lax) + (thread-last + (regexp-quote string) + (replace-regexp-in-string "`" "[`‘]") + (replace-regexp-in-string "'" "['’]") + (replace-regexp-in-string "\"" "[\"“”]"))))) + (buffer-local-restore-state isearch-fold-quotes-mode--state))) + (provide 'isearch) ;;; isearch.el ends here |