8000 Home · astoff/isearch-mb Wiki · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content
Augusto Stoffel edited this page Feb 26, 2022 · 10 revisions
8000

This page collects assorted tips and tricks for isearch and related facilities of Emacs. These are interesting ideas that don't fit into isearch-mb, or are too small to distribute as a package, or just a little too hacky (or a combination of these).

Feel free to contribute to this wiki!

Table of contents

Indicate wrap status by colorizing the lazy count

isearch-mb doesn't display the wrapped/overwrapped status of the search in the echo area. This is by design.

However, the following applies some colors to the match count in the echo area, depending on whether the search is failed, wrapped around, or neither.

(advice-add 'isearch-lazy-count-format :filter-return
            (lambda (s) "Fontification of the lazy count."
              (propertize s 'face (cond
                                   ((not isearch-success) 'error)
                                   (isearch-wrapped 'warning)
                                   (t 'success)))))

Add search string to history when canceling

Normally, quitting the minibuffer or isearch with C-g doesn't update the history. This is usually a good idea, but in the particular case of isearch I like to be able to continue an interrupted search.

(advice-add 'isearch-cancel :before
            (lambda () "Add search string to history also when canceling."
              (unless (string-equal "" isearch-string)
                (isearch-update-ring isearch-string isearch-regexp))))

Case fold info in isearch-mb prompt

This advice makes isearch-mb prompt show [Case Fold] when case fold is on:

(define-advice isearch-mb--update-prompt (:around (fn &rest args) show-case-fold-info)
    "Show case fold info in the prompt."
    (cl-letf* ((isearch--describe-regexp-mode-orig
                (symbol-function 'isearch--describe-regexp-mode))
               ((symbol-function 'isearch--describe-regexp-mode)
                (lambda (regexp-function &optional space-before)
                  (concat (if isearch-case-fold-search "[Case Fold] " "")
                          (funcall isearch--describe-regexp-mode-orig
                                   regexp-function space-before)))))
      (apply fn args)))

See also https://github.com/astoff/isearch-mb/pull/19

Fringe wrap indicator (search compass)

The following snipped defines a minor mode to display the relative position of the search starting point in the right fringe of the buffer.

The arrow points towards the starting point of the search, and turns from green to red after the search wraps around.

(defvar isearch--compass-point-ov nil)
(defvar isearch--compass-opoint-ov nil)

(defun isearch--compass-update ()
  (move-overlay isearch--compass-point-ov
                (line-beginning-position) (point) (current-buffer))
  (overlay-put isearch--compass-point-ov
               'before-string
               (propertize "."
                           'display
                           `(right-fringe
                             ,(if (> (point) isearch-opoint) 'up-arrow 'down-arrow)
                             ,(if isearch-wrapped 'error 'success))))
  (move-overlay isearch--compass-opoint-ov
                isearch-opoint isearch-opoint (current-buffer))
  (overlay-put isearch--compass-opoint-ov
               'before-string
               (propertize "." 'display '(right-fringe hollow-square))))

(defun isearch--compass-cleanup ()
  (delete-overlay isearch--compass-point-ov)
  (delete-overlay isearch--compass-opoint-ov))

(define-minor-mode isearch-compass-mode
  "Show search start and wrap status in the fringe."
  :global t
  (if isearch-compass-mode
      (progn
        (delete-overlay
         (setq isearch--compass-point-ov (make-overlay 1 1)))
        (delete-overlay
         (setq isearch--compass-opoint-ov (make-overlay 1 1)))
        (advice-add 'isearch-post-command-hook :after 'isearch--compass-update)
        (add-hook 'isearch-mode-end-hook 'isearch--compass-cleanup))
    (remove-hook 'isearch-mode-end-hook #'isearch--compass-cleanup)
    (advice-remove 'isearch-post-command-hook 'isearch--compass-update)
    (isearch--compass-cleanup)))

A robust query replace UI

The problem of the default query-replace UI is when you accidently press a key that's not in query-replace-map, the session is terminated. This makes it feel fragile.

Here's an advice fixing it. When you press a non query-replace-map key, it opens the help info.

(define-advice perform-replace (:around (fn &rest args) dont-exit-on-anykey)
  "Don't exit replace for anykey that's not in `query-replace-map'."
  (cl-letf* ((lookup-key-orig
              (symbol-function 'lookup-key))
             ((symbol-function 'lookup-key)
              (lambda (map key &optional accept-default)
                (or (apply lookup-key-orig map key accept-default)
                    (when (eq map query-replace-map) 'help)))))
    (apply fn args)))

Also, here's a prettier help info than the default one:

(setq query-replace-help
      "<Replace>
[y/SPC] replace                         [!] replace all remaining
[.] replace only one                    [,] replace and not move to next
<Exit>
[q/RET] exit
<Move>
[n/DEL] next          [^] prev          [C-l] recenter
<Undo>
[u] undo previous                       [U] undo all
<Edit>
[C-r] recursive edit                    [\\[exit-recursive-edit]]: exit recursive edit
[C-w]: delete match and recursive edit  [E]: edit the replacement string.
<Multi-file replace>
[Y]: replace all in remaining buffers   [N]: skip to the next buffer")

Entries relevant for Emacs ≤ 27

Auto-wrap

Note: Starting from Emacs 28, there is a customization variable isearch-wrap-pause to control this behavior.

In isearch, if you search forward, and there's no match, you have to hit C-s again to let it search from the beginning (called "wrap"). This advice makes it wrap automatically:

;; Ref: https://stackoverflow.com/questions/285660/automatically-wrapping-i-search/36707038#36707038
(define-advice isearch-search (:around (fn) auto-wrap)
  "Auto-wrap for isearch commands."
  (funcall fn)
  (unless isearch-success
    ;; `isearch-repeat' calls `isearch-search'.  We need to use the unadvised
    ;; version, or when the wrapped search also fails, it will call
    ;; `isearch-repeat' in turn and reach max callstack depth.
    (cl-letf (((symbol-function 'isearch-search)
               (ad-get-orig-definition #'isearch-search)))
      (isearch-repeat (if isearch-forward 'forward 'backward)))))

Fix lazy-count info

For Emacs 27 or earlier, when starting a search, the lazy count info shows 0/total-count. After isearch-repeat-forward/backward, the current count becomes non-zero.

This is a dirty hack that fixes it. Use it at your own risk!

(when (version< emacs-version "28")
  (define-advice isearch-lazy-highlight-buffer-update (:around (fn &rest args) fix)
  "Fix the problem of current count being 0 when starting the search."
  (cl-letf* ((my-opoint nil)
             (window-group-start-orig (symbol-function 'window-group-start))
             ((symbol-function 'window-group-start)
              (lambda (&optional window)
                (setf my-opoint (point))
                (funcall window-group-start-orig window)))
             (gethash-orig (symbol-function 'gethash))
             ((symbol-function 'gethash)
              (lambda (key &rest args)
                (if (integerp key)
                    (apply gethash-orig my-opoint args)
                  (apply gethash-orig key args)))))
    (apply fn args))))

See also https://github.com/astoff/isearch-mb/issues/20

0