From: Augusto Stoffel <arstoffel@gmail.com>
To: Juri Linkov <juri@linkov.net>
Cc: 53126@debbugs.gnu.org
Subject: bug#53126: 29.0.50; [PATCH] Lazy highlight/count when reading query-replace string, etc.
Date: Sun, 03 Apr 2022 10:32:12 +0200 [thread overview]
Message-ID: <87o81i5zzn.fsf@gmail.com> (raw)
In-Reply-To: <86r16fz6bw.fsf@mail.linkov.net> (Juri Linkov's message of "Sat, 02 Apr 2022 21:23:10 +0300")
[-- Attachment #1: Type: text/plain, Size: 275 bytes --]
I've attached a sketch of a macro to help activating the minibuffer lazy
highlight. It doesn't make query-replace-read-args exactly short, but
if you like this, I can prepare the patch.
Note that I haven't tested this code at all. It's for impressionistic
purposes only.
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Display-lazy-highlight-and-match-count-in-query-repl.patch --]
[-- Type: text/x-patch, Size: 8664 bytes --]
From 92394967d27bb2750a88a19951182d1465b57040 Mon Sep 17 00:00:00 2001
From: Augusto Stoffel <arstoffel@gmail.com>
Date: Thu, 17 Mar 2022 20:17:26 +0100
Subject: [PATCH] Display lazy highlight and match count in query-replace
* lisp/isearch.el (isearch-query-replace): Don't clean up lazy
highlight if applicable.
* lisp/replace.el (query-replace-read-args, query-replace-read-to):
Add lazy highlighting and count.
(replace--region-filter): New function, extracted from
'perform-replace'.
(perform-replace): Use 'replace--region-filter'.
---
lisp/isearch.el | 65 +++++++++++++++++++++++++++++++++++++++++++------
lisp/replace.el | 49 +++++++++++++++++++++++--------------
2 files changed, 88 insertions(+), 26 deletions(-)
diff --git a/lisp/isearch.el b/lisp/isearch.el
index 956b115ce4..b28a7b88a8 100644
--- a/lisp/isearch.el
+++ b/lisp/isearch.el
@@ -2352,7 +2352,9 @@ isearch-query-replace
(isearch-recursive-edit nil)
(isearch-string-propertized
(isearch-string-propertize isearch-string)))
- (isearch-done nil t)
+ (let ((lazy-highlight-cleanup (and lazy-highlight-cleanup
+ (not query-replace-lazy-highlight))))
+ (isearch-done nil t))
(isearch-clean-overlays)
(if (and isearch-other-end
(if backward
@@ -2368,13 +2370,16 @@ isearch-query-replace
(symbol-value query-replace-from-history-variable)))
(perform-replace
isearch-string-propertized
- (query-replace-read-to
- isearch-string-propertized
- (concat "Query replace"
- (isearch--describe-regexp-mode (or delimited isearch-regexp-function) t)
- (if backward " backward" "")
- (if (use-region-p) " in region" ""))
- isearch-regexp)
+ (condition-case error
+ (query-replace-read-to
+ isearch-string-propertized
+ (concat "Query replace"
+ (isearch--describe-regexp-mode (or delimited isearch-regexp-function) t)
+ (if backward " backward" "")
+ (if (use-region-p) " in region" ""))
+ isearch-regexp)
+ (t (lazy-highlight-cleanup lazy-highlight-cleanup)
+ (signal (car error) (cdr error))))
t isearch-regexp (or delimited isearch-regexp-function) nil nil
(if (use-region-p) (region-beginning))
(if (use-region-p) (region-end))
@@ -4413,6 +4418,50 @@ minibuffer-lazy-highlight-setup
(make-overlay (point-min) (point-min) (current-buffer) t)))
(minibuffer-lazy-highlight--after-change nil nil nil))
+(cl-defmacro with-minibuffer-lazy-highlight (lazy-highlight &rest body &keys (regexp 'unset) (regexp-function 'unset) (case-fold 'unset) (cleanup 'unset) (region 'unset) (transform 'unset))
+ (while (keywordp (car body) (setq body (cddr body))))
+ `(let ((isearch-lazy-highlight ,lazy-highlight)
+ ,@(unless (eq 'unset ,case-fold) `(isearch-case-fold-search ,case-fold))
+ ,@(unless (eq 'unset ,regexp) `(isearch-regexp ,regexp))
+ ,@(unless (eq 'unset ,regexp-function) `(isearch-regexp-function ,regexp-function))
+ ,@(unless (eq 'unset ,cleanup) `(lazy-highlight-cleanup ,cleanup))
+ ,@(unless (eq 'unset ,transform )`(minibuffer-lazy-highlight-transform ,transform)))
+ (when isearch-lazy-highlight
+ (add-hook 'minibuffer-setup-hook #'minibuffer-lazy-highlight-setup)
+ (when ,region
+ (letrec ((region-filter (isearch-region-filter
+ (funcall region-extract-function ,region)))
+ (cleanup (lambda ()
+ (remove-function isearch-filter-predicate region-filter)
+ (remove-hook 'minibuffer-exit-hook cleanup))))
+ (add-function :after-while isearch-filter-predicate region-filter)
+ (add-hook 'minibuffer-exit-hook cleanup))))
+ (condition-case error
+ (progn ,@body)
+ (t (lazy-highlight-cleanup)
+ (signal (car error) (cdr error))))))
+
+(defun isearch-region-filter (bounds)
+ "Return a function that decides if a region is inside BOUNDS.
+BOUNDS is a list of cons cells of the form (START . END). The
+returned function takes as argument two buffer positions, START
+and END."
+ (let ((region-bounds
+ (mapcar (lambda (position)
+ (cons (copy-marker (car position))
+ (copy-marker (cdr position))))
+ bounds)))
+ (lambda (start end)
+ (delq nil (mapcar
+ (lambda (bounds)
+ (and
+ (>= start (car bounds))
+ (<= start (cdr bounds))
+ (>= end (car bounds))
+ (<= end (cdr bounds))))
+ region-bounds)))))
+
+
\f
(defun isearch-resume (string regexp word forward message case-fold)
"Resume an incremental search.
diff --git a/lisp/replace.el b/lisp/replace.el
index e6f565d802..bed4a2824d 100644
--- a/lisp/replace.el
+++ b/lisp/replace.el
@@ -352,8 +352,15 @@ query-replace-read-to
(query-replace-compile-replacement
(save-excursion
(let* ((history-add-new-input nil)
+ (count (if (and query-replace-lazy-highlight
+ minibuffer-lazy-count-format
+ isearch-lazy-count
+ isearch-lazy-count-total)
+ (format minibuffer-lazy-count-format
+ isearch-lazy-count-total)
+ ""))
(to (read-from-minibuffer
- (format "%s %s with: " prompt (query-replace-descr from))
+ (format "%s%s %s with: " count prompt (query-replace-descr from))
nil nil nil
query-replace-to-history-variable from t)))
(add-to-history query-replace-to-history-variable to nil t)
@@ -365,7 +372,26 @@ query-replace-read-args
(unless noerror
(barf-if-buffer-read-only))
(save-mark-and-excursion
- (let* ((from (query-replace-read-from prompt regexp-flag))
+ (let* ((from (with-minibuffer-lazy-highlight
+ query-replace-lazy-highlight
+ :case-fold case-fold-search
+ :regexp regexp-flag
+ :regexp-function (or replace-regexp-function
+ (and current-prefix-arg
+ (not (eq current-prefix-arg '-)))
+ (and replace-char-fold
+ (not regexp-flag)
+ #'char-fold-to-regexp)))
+ :cleanup nil
+ :transform (minibuffer-lazy-highlight-transform
+ (lambda (string)
+ (let* ((split (query-replace--split-string string))
+ (from-string (if (consp split) (car split) split)))
+ (when (and case-fold-search search-upper-case)
+ (setq isearch-case-fold-search
+ (isearch-no-upper-case-p from-string regexp-flag)))
+ from-string)))
+ (query-replace-read-from prompt regexp-flag))
(to (if (consp from) (prog1 (cdr from) (setq from (car from)))
(query-replace-read-to from prompt regexp-flag))))
(list from to
@@ -2862,22 +2888,9 @@ perform-replace
;; Unless a single contiguous chunk is selected, operate on multiple chunks.
(when region-noncontiguous-p
- (let ((region-bounds
- (mapcar (lambda (position)
- (cons (copy-marker (car position))
- (copy-marker (cdr position))))
- (funcall region-extract-function 'bounds))))
- (setq region-filter
- (lambda (start end)
- (delq nil (mapcar
- (lambda (bounds)
- (and
- (>= start (car bounds))
- (<= start (cdr bounds))
- (>= end (car bounds))
- (<= end (cdr bounds))))
- region-bounds))))
- (add-function :after-while isearch-filter-predicate region-filter)))
+ (setq region-filter (isearch-region-filter
+ (funcall region-extract-function 'bounds)))
+ (add-function :after-while isearch-filter-predicate region-filter))
;; If region is active, in Transient Mark mode, operate on region.
(if backward
--
2.35.1
[-- Attachment #3: Type: text/plain, Size: 1227 bytes --]
Below a response to your last message.
On Sat, 2 Apr 2022 at 21:23, Juri Linkov <juri@linkov.net> wrote:
>> - The value of case-fold-search can change on the fly
>
> Please clarify when it can be changed on the fly.
I refer to the following lines of my patch, which are necessary.
(when (and case-fold-search search-upper-case)
(setq isearch-case-fold-search
(isearch-no-upper-case-p from-string regexp-flag)))
(Incidentally, I like better the ripgrep smart case, where an uppercase
character only matches itself, and lowercase characters match both upper
and lower case.)
>> I'm referring to the anzu feature whereby the replacement text is shown
>> next to each match, like this:
>>
>> This will not be totally trivial to implement, right? And it will add
>> some extra stuff to query-replace-read-args that is very much
>> query-replace specific.
>
> I very much doubt in usefulness of such a feature. I thought it could
> simply highlight the replacement text to show the places in the buffer
> that already contain such replacement text.
The anzu thing is only really meaningful if you are doing some complex
replacements with \, and whatnot. But in that case, I guess it could
help a lot?
next prev parent reply other threads:[~2022-04-03 8:32 UTC|newest]
Thread overview: 57+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-01-08 13:24 bug#53126: 29.0.50; [PATCH] Lazy highlight/count when reading query-replace string, etc Augusto Stoffel
2022-01-08 18:59 ` Juri Linkov
2022-01-08 19:35 ` Augusto Stoffel
2022-01-09 9:10 ` Juri Linkov
2022-01-09 10:02 ` Augusto Stoffel
2022-01-09 10:30 ` Augusto Stoffel
2022-01-09 18:58 ` Juri Linkov
2022-01-10 17:34 ` Augusto Stoffel
2022-01-10 19:09 ` Juri Linkov
2022-02-26 16:13 ` Augusto Stoffel
2022-03-15 17:09 ` Juri Linkov
2022-03-15 21:33 ` Augusto Stoffel
2022-03-16 18:56 ` Juri Linkov
2022-03-16 20:09 ` Augusto Stoffel
2022-03-17 17:09 ` Juri Linkov
2022-03-17 19:10 ` Augusto Stoffel
2022-03-17 20:40 ` Juri Linkov
2022-03-17 21:42 ` Augusto Stoffel
2022-03-20 9:38 ` Augusto Stoffel
2022-03-20 18:51 ` Juri Linkov
2022-03-24 19:03 ` Augusto Stoffel
2022-03-25 8:39 ` Juri Linkov
2022-03-25 9:43 ` Augusto Stoffel
2022-03-27 7:46 ` Juri Linkov
2022-04-01 9:06 ` Augusto Stoffel
2022-04-01 16:35 ` Juri Linkov
2022-04-01 18:12 ` Augusto Stoffel
2022-04-02 18:23 ` Juri Linkov
2022-04-03 8:32 ` Augusto Stoffel [this message]
2022-04-03 17:06 ` Juri Linkov
2022-04-04 16:37 ` Juri Linkov
2022-04-05 16:38 ` Augusto Stoffel
2022-04-05 17:12 ` Juri Linkov
2022-04-07 19:32 ` Augusto Stoffel
2022-04-08 7:32 ` Juri Linkov
2022-04-08 7:53 ` Augusto Stoffel
2022-04-09 11:06 ` Augusto Stoffel
2022-04-10 19:38 ` Juri Linkov
2022-03-15 17:24 ` Juri Linkov
2022-03-15 21:21 ` Augusto Stoffel
2022-03-16 19:02 ` Juri Linkov
2022-03-16 20:25 ` Augusto Stoffel
2022-03-17 17:05 ` Juri Linkov
2022-03-17 19:06 ` Augusto Stoffel
2022-03-20 19:24 ` Juri Linkov
2022-03-20 19:59 ` Augusto Stoffel
2022-03-20 20:29 ` Juri Linkov
2022-03-20 20:56 ` Augusto Stoffel
2022-03-23 18:20 ` Juri Linkov
2022-03-23 18:54 ` Augusto Stoffel
2022-03-23 19:17 ` Eli Zaretskii
2022-03-23 19:53 ` Juri Linkov
2022-03-23 20:06 ` Juri Linkov
2022-03-23 20:30 ` Augusto Stoffel
2022-03-23 20:43 ` Juri Linkov
2022-03-17 19:45 ` Augusto Stoffel
2022-03-17 20:43 ` Juri Linkov
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
List information: https://www.gnu.org/software/emacs/
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=87o81i5zzn.fsf@gmail.com \
--to=arstoffel@gmail.com \
--cc=53126@debbugs.gnu.org \
--cc=juri@linkov.net \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this public inbox
https://git.savannah.gnu.org/cgit/emacs.git
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).