Subject: bug#52356: 27.2; Isearch prompt changes unexpectedly with char folding enabled

From: Andrea Greselin <[email-masked]>
Date: Fri, 17 Dec 2021 20:35:50 +0100

Hi Juri, thanks for your help.
I’ve tested your latest patch in two ways and in both tests the prompt
didn’t flicker on adding letters to the search string. So it looks
fine to me.

> A possible variant would be not to flicker too much by
> transitioning from the message:
>
>   Pending char-fold I-search: string
>
> to
>
>   1/20 Pending char-fold I-search: string

If I understand it correctly the jittering there is all due to
‘lazy-count-prefix-format’ being non-nil. If you set it to nil (and
use ‘lazy-count-suffix-format’ to display the counter) then you have
no jittering at all.

> But maybe better not to display "Pending" at all?

I personally don’t like delayed changes to graphical elements, so I’d
say yes.

Now I'm curious though, what‘s the meaning of “pending” in the context
of Isearch?

Anyway, these were my tests:

1) Run ‘emacs -Q’, evaluate functions with your patch. At each step on
   the left I get the prompt on the right:

   C-s       → I-search:
   M-s '     → Pending char-fold I-search:
   Type smth → Char-fold I-search: Type smth

2) Run ‘emacs -Q’, evaluate my settings and the functions with your
   patch. Result:

   C-s       → I-search:

   (At this point the search is already being done with char-folding
   enabled because of my settings.)

   Type smth → I-search: Type smth [0/0]

Here are my settings and the patched functions, for convenience.

    (setq isearch-lazy-count t)
    (setq lazy-count-prefix-format nil
          lazy-count-suffix-format " [%s/%s]")
    (setq-local search-default-mode 'char-fold-to-regexp)
    (electric-quote-local-mode)

    (defun isearch-message-prefix (&optional ellipsis nonincremental)
      ;; If about to search, and previous search regexp was invalid,
      ;; check that it still is.  If it is valid now,
      ;; let the message we display while searching say that it is valid.
      (and isearch-error ellipsis
           (condition-case ()
      (progn (re-search-forward isearch-string (point) t)
     (setq isearch-error nil))
    (error nil)))
      ;; If currently failing, display no ellipsis.
      (or isearch-success (setq ellipsis nil))
      (let ((m (concat (if isearch-success "" "failing ")
      (if (eq isearch-adjusted t) "pending " "")
    (if (and isearch-wrapped
       (not isearch-wrap-function)
       (if isearch-forward
    (> (point) isearch-opoint)
         (< (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)
                            (let ((np (cdr (assq 'isearch-message-prefix
props))))
                              (if np (setq prefix (concat np prefix)))))
                          isearch-filter-predicate)
                         prefix)
                       (isearch--describe-regexp-mode
isearch-regexp-function)
      (cond
       (multi-isearch-file-list "multi-file ")
       (multi-isearch-buffer-list "multi-buffer ")
       (t ""))
      (or isearch-message-prefix-add "")
      (if nonincremental "search" "I-search")
      (if isearch-forward "" " backward")
      (if current-input-method
          ;; Input methods for RTL languages use RTL
          ;; characters for their title, and that messes
          ;; up the display of search text after the prompt.
          (bidi-string-mark-left-to-right
    (concat " [" current-input-method-title "]: "))
        ": ")
      )))
        (apply #'propertize (concat (isearch-lazy-count-format)
                            (upcase (substring m 0 1)) (substring m 1))
      isearch-message-properties)))

    (defun isearch-search-fun-default ()
      "Return default functions to use for the search."
      (lambda (string &optional bound noerror count)
        (let (;; Evaluate this before binding `search-spaces-regexp' which
              ;; can break all sorts of regexp searches.  In particular,
              ;; calling `isearch-regexp-function' can trigger autoloading
              ;; (Bug#35802).
              (regexp
               (cond (isearch-regexp-function
                      (let ((lax (and (not bound)
                                      (isearch--lax-regexp-function-p))))
                        (when lax
                          (setq isearch-adjusted 'lax))
                        (if (functionp isearch-regexp-function)
                            (funcall isearch-regexp-function string lax)
                          (word-search-regexp string lax))))
                     (isearch-regexp string)
                     (t (regexp-quote string))))
              ;; Use lax versions to not fail at the end of the word while
              ;; the user adds and removes characters in the search string
              ;; (or when using nonincremental word isearch)
              (search-spaces-regexp (when (if isearch-regexp
                                              isearch-regexp-lax-whitespace
                                            isearch-lax-whitespace)
                                      search-whitespace-regexp)))
          (funcall
           (if isearch-forward #'re-search-forward #'re-search-backward)
           regexp bound noerror count))))