Subject: bug#62505: 29.0.60; Switching between query-replace and query-replace-regexp should be easy, like isearch

From: Spencer Baugh <[email-masked]>
Date: Fri, 12 Sep 2025 17:23:34 -0400

Juri Linkov <[email-masked]> writes:

>>>> isearch-forward and isearch-forward-regexp are bound to C-s and C-M-s.
>>>> After an isearch-forward is started, it can be switched to
>>>> isearch-forward-regexp by typing M-s r.
>>>>
>>>> query-replace and query-replace-regexp are bound to M-% and C-M-%.
>>>> After a query-replace is started, there's no way to switch it to
>>>> query-replace-regexp.
>>>
>>> It's not clear whether you want to switch modes when the replacement
>>> process is already underway, or when reading query-replace arguments
>>> in the minibuffer.  In Isearch mode 'M-s r' switches modes when the
>>> search process is in progress, not when reading the search string
>>> in the minibuffer.
>>
>> I think both should work.
>
> It's straightforward to implement this for the case when replacement
> is already started like in the active Isearch.  However, switching
> the replacement type while reading arguments would be too messy.
> There is such logic in 'query-replace-read-from':
>
>               (if regexp-flag
>                   (read-regexp ...)
>                 (read-from-minibuffer ...))
>
> Changing the value of 'regexp-flag' while the minibuffer is active will require
> canceling the current minibuffer and restarting 'query-replace' from scratch.

Ah.  Right, troubling.

Hmm...

I guess we could give read-regexp a "non-regexp" or "plain text" mode,
which can be toggled on and off by the user, and just always call
read-regexp.  Then read-regexp could return "this string isn't a regex"
using a text property on the string, just as it returns the text
property case-fold right now.

Then after we return from read-regexp we could read that text property
and set regexp-flag based on it.

What do you think?  Too hacky?

> It's easier to do the same for the user manually with just 'C-g C-M-% RET'
> that reuses the last search→replace pair in case when the user mistyped
> M-% instead of C-M-%.
>
>> True that 'M-s r' doesn't work if you first start editing the search
>> string with 'M-e'.  Maybe Isearch would also benefit from having that
>> work?  With C-s and C-M-s bindings, which work both while searching and
>> while editing the search string in the minibuffer?
>
> There is ambiguity in using C-s in the minibuffer since it can mean both:
> - continue the current regexp search like 'C-M-s C-s C-s ...' does;
> - switch to non-regexp mode.
> It's easier for the user to use just 'M-s r' to toggle regexp search
> while in Isearch mode.
>
> Also using M-% and C-M-% is problematic in the minibuffer
> since both keys can be used for replacing text in the minibuffer.

True, I do sometimes do a recursive M-%.  So, I agree, better to just
use M-s r, and not overload M-%/C-M-%/C-s/C-M-s in this way.

> However, how to implement toggling the regexp mode while using query-replace
> is clear and is implemented in this patch:

Nice!  I tried it and it works.

Though, one issue is that if you start a query-replace that was meant to
be a query-replace-regexp, it will probably not have any matches, so it
will immediately fail.  Though I guess in that case one can just
immediately do C-M-%.