From mboxrd@z Thu Jan 1 00:00:00 1970 Path: main.gmane.org!not-for-mail From: David Kastrup Newsgroups: gmane.emacs.devel Subject: Re: query-replace-interactive not documented Date: 17 Jun 2004 14:14:23 +0200 Sender: emacs-devel-bounces+emacs-devel=quimby.gnus.org@gnu.org Message-ID: References: <20040528.181649.25475113.wl@gnu.org> <200405291737.i4THbPJ06689@raven.dms.auburn.edu> <873c5jug73.fsf@mail.jurta.org> <87oenqa4lu.fsf@mail.jurta.org> <873c51w5rq.fsf@mail.jurta.org> <87659snbd8.fsf@mail.jurta.org> <87llin98sc.fsf@mail.jurta.org> <87n033y0yj.fsf@mail.jurta.org> NNTP-Posting-Host: deer.gmane.org Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" X-Trace: sea.gmane.org 1087474516 7289 80.91.224.253 (17 Jun 2004 12:15:16 GMT) X-Complaints-To: usenet@sea.gmane.org NNTP-Posting-Date: Thu, 17 Jun 2004 12:15:16 +0000 (UTC) Cc: Juri Linkov , schwab@suse.de, emacs-devel@gnu.org Original-X-From: emacs-devel-bounces+emacs-devel=quimby.gnus.org@gnu.org Thu Jun 17 14:15:07 2004 Return-path: Original-Received: from quimby.gnus.org ([80.91.224.244]) by deer.gmane.org with esmtp (Exim 3.35 #1 (Debian)) id 1BavnP-0007yX-00 for ; Thu, 17 Jun 2004 14:15:07 +0200 Original-Received: from lists.gnu.org ([199.232.76.165]) by quimby.gnus.org with esmtp (Exim 3.35 #1 (Debian)) id 1BavnO-0003gW-00 for ; Thu, 17 Jun 2004 14:15:07 +0200 Original-Received: from localhost ([127.0.0.1] helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.33) id 1BavoR-0007ng-C8 for emacs-devel@quimby.gnus.org; Thu, 17 Jun 2004 08:16:11 -0400 Original-Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.33) id 1BavoO-0007n4-8x for emacs-devel@gnu.org; Thu, 17 Jun 2004 08:16:08 -0400 Original-Received: from exim by lists.gnu.org with spam-scanned (Exim 4.33) id 1BavoN-0007mr-Ef for emacs-devel@gnu.org; Thu, 17 Jun 2004 08:16:07 -0400 Original-Received: from [199.232.76.173] (helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.33) id 1BavoN-0007mi-An for emacs-devel@gnu.org; Thu, 17 Jun 2004 08:16:07 -0400 Original-Received: from [199.232.76.164] (helo=fencepost.gnu.org) by monty-python.gnu.org with esmtp (Exim 4.34) id 1Bavmk-0005Jo-U4 for emacs-devel@gnu.org; Thu, 17 Jun 2004 08:14:27 -0400 Original-Received: from localhost ([127.0.0.1] helo=lola.goethe.zz) by fencepost.gnu.org with esmtp (Exim 4.34) id 1Bavmj-0001k3-1N; Thu, 17 Jun 2004 08:14:26 -0400 Original-To: storm@cua.dk (Kim F. Storm) In-Reply-To: Original-Lines: 64 User-Agent: Gnus/5.09 (Gnus v5.9.0) Emacs/21.3.50 X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.4 Precedence: list List-Id: "Emacs development discussions." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-devel-bounces+emacs-devel=quimby.gnus.org@gnu.org Xref: main.gmane.org gmane.emacs.devel:25057 X-Report-Spam: http://spam.gmane.org/gmane.emacs.devel:25057 --=-=-= David Kastrup writes: > storm@cua.dk (Kim F. Storm) writes: > > > David Kastrup writes: > > > > > Juri Linkov writes: > > > > > > > It is not important what question it asks. It would work even for > > > > two or three different \? in one replacement string. In rare cases > > > > where the user needs more \?, he can use \,(read-string "My prompt: > > > > "). > > > > > > > > (concat (read-string "Enter string 1: ") (read-string "Enter string > > > > 2: ")) > > > > > > Yes, I might do something like that. > > > > Maybe "Enter string: " for the first one, "Enter string 2:" etc for the > > following strings. > > Ouch, I just realized that the dialog will basically be: > > Enter string: xsxa RET > Query replace gfag with fdsfxsxa* (y/n)? > > That's so weird that I don't really know if it should be a "feature" > with its own shortcut. Entering the strings when they might not even > get used... And without any visual indication what is happening right now. > I'll just post the current untested diff (which might or might not > work) since I'm about to fall asleep. In case it works, people have > something to play with. Ok, I just redid this. I have completely thrown out \\? again: if one wanted to implement it, the right place would not be in preparing the replacement list where \, is implemented, but rather during perform-replace. And then it should be done _after_ the question "replace woozle with heffalump\? (y/n)?" has been answered positively, by using a while loop that removes the first proper \? in the string (using the "\\(\\`\\|[^\\]\\)\\(\\\\\\\\\\)*\\\\\\?" pattern) and calls the replacement string editor with the cursor at the specified location, until there is no longer an unescaped \?. After that, the replacement can be performed. This will give the context (so we don't need prompt strings), it will be flexible and convenient. Any volunteer to put this is? Anyway, I fixed what I posted last night, removed the skip of the optional space (the most prominent use would have been \,replace-count, and there is a shorthand for that by now) and added documentation strings. If nobody protests, I'll check in the following patch. It also removes \n \t warnings where they are nonsensical. --=-=-= Content-Type: text/x-patch Content-Disposition: attachment --- replace.el 10 Jun 2004 09:36:09 +0200 1.172 +++ replace.el 17 Jun 2004 13:58:02 +0200 @@ -81,14 +81,15 @@ query-replace-from-history-variable nil t))) ;; Warn if user types \n or \t, but don't reject the input. - (if (string-match "\\\\[nt]" from) - (let ((match (match-string 0 from))) - (cond - ((string= match "\\n") - (message "Note: `\\n' here doesn't match a newline; to do that, type C-q C-j instead")) - ((string= match "\\t") - (message "Note: `\\t' here doesn't match a tab; to do that, just type TAB"))) - (sit-for 2)))) + (and regexp-flag + (string-match "\\(\\`\\|[^\\]\\)\\(\\\\\\\\\\)*\\(\\\\[nt]\\)" from) + (let ((match (match-string 3 from))) + (cond + ((string= match "\\n") + (message "Note: `\\n' here doesn't match a newline; to do that, type C-q C-j instead")) + ((string= match "\\t") + (message "Note: `\\t' here doesn't match a tab; to do that, just type TAB"))) + (sit-for 2)))) (save-excursion (setq to (read-from-minibuffer (format "%s %s with: " string from) @@ -161,20 +162,62 @@ In TO-STRING, `\\&' stands for whatever matched the whole of REGEXP, and `\\=\\N' (where N is a digit) stands for - whatever what matched the Nth `\\(...\\)' in REGEXP." +whatever what matched the Nth `\\(...\\)' in REGEXP. + +When this function is called interactively, the replacement text +can also contain `\\,' followed by a Lisp expression. The escaped +shorthands for `query-replace-regexp-eval' are also valid +here: within the Lisp expression, you can use `\\&' for the whole +match string, `\\N' for partial matches, `\\#&' and `\\#N' for +the respective numeric values, and `\\#' for `replace-count'. + +If your Lisp expression is an identifier and the next +letter in the replacement string would be interpreted as part of it, +you can wrap it with an expression like `\\,(or \\#)'. Incidentally, +for this particular case you may also enter `\\#' in the replacement +text directly. + +When you use `\\,' or `\\#' in the replacement, TO-STRING actually +becomes a list with expanded shorthands. +Use \\[repeat-complex-command] after this command to see details." (interactive (let ((common (query-replace-read-args "Query replace regexp" t))) - (list (nth 0 common) (nth 1 common) (nth 2 common) - ;; These are done separately here - ;; so that command-history will record these expressions - ;; rather than the values they had this time. - (if (and transient-mark-mode mark-active) - (region-beginning)) - (if (and transient-mark-mode mark-active) - (region-end))))) - + (list + (nth 0 common) + (if (string-match "\\(\\`\\|[^\\]\\)\\(\\\\\\\\\\)*\\\\[,#]" + (nth 1 common)) + (let ((to-string (nth 1 common)) pos to-expr char prompt) + (while (string-match + "\\(\\`\\|[^\\]\\)\\(\\\\\\\\\\)*\\\\[,#]" + to-string) + (setq pos (match-end 0)) + (push (substring to-string 0 (- pos 2)) to-expr) + (setq char (aref to-string (1- pos)) + to-string (substring to-string pos)) + (cond ((eq char ?\#) + (push '(number-to-string replace-count) to-expr)) + ((eq char ?\,) + (setq pos (read-from-string to-string)) + (push `(replace-quote ,(car pos)) to-expr) + (setq to-string (substring to-string (cdr pos)))))) + (setq to-expr (nreverse (delete "" (cons to-string to-expr)))) + (replace-match-string-symbols to-expr) + (cons 'replace-eval-replacement + (if (> (length to-expr) 1) + (cons 'concat to-expr) + (car to-expr)))) + (nth 1 common)) + (nth 2 common) + ;; These are done separately here + ;; so that command-history will record these expressions + ;; rather than the values they had this time. + (if (and transient-mark-mode mark-active) + (region-beginning)) + (if (and transient-mark-mode mark-active) + (region-end))))) (perform-replace regexp to-string t t delimited nil nil start end)) + (define-key esc-map [?\C-%] 'query-replace-regexp) (defun query-replace-regexp-eval (regexp to-expr &optional delimited start end) @@ -191,6 +234,7 @@ `\\0' to stand for whatever matched the whole of REGEXP, and `\\N' (where N is a digit) to stand for whatever matched the Nth `\\(...\\)' in REGEXP. Use `\\#&' or `\\#N' if you want a number instead of a string. +In interactive use, `\\#' in itself stands for `replace-count'. In Transient Mark mode, if the mark is active, operate on the contents of the region. Otherwise, operate from point to the end of the buffer. @@ -1012,6 +1056,7 @@ #N (string-to-number (match-string N)) & (match-string 0) #& (string-to-number (match-string 0)) +# replace-count Note that these symbols must be preceeded by a backslash in order to type them." @@ -1031,7 +1076,9 @@ ((string= "&" name) (setcar n '(match-string 0))) ((string= "#&" name) - (setcar n '(string-to-number (match-string 0)))))))) + (setcar n '(string-to-number (match-string 0)))) + ((string= "#" name) + (setcar n 'replace-count)))))) (setq n (cdr n)))) (defun replace-eval-replacement (expression replace-count) @@ -1040,6 +1087,21 @@ replacement (prin1-to-string replacement t)))) +(defun replace-quote (replacement) + "Quote a replacement string. +This just doubles all backslashes in REPLACEMENT and +returns the resulting string. If REPLACEMENT is not +a string, it is first passed through `prin1-to-string' +with the `noescape' argument set. + +`match-data' is preserved across the call." + (save-match-data + (replace-regexp-in-string "\\\\" "\\\\" + (if (stringp replacement) + replacement + (prin1-to-string replacement t)) + t t))) + (defun replace-loop-through-replacements (data replace-count) ;; DATA is a vector contaning the following values: ;; 0 next-rotate-count --=-=-= -- David Kastrup, Kriemhildstr. 15, 44793 Bochum --=-=-= Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Disposition: inline _______________________________________________ Emacs-devel mailing list Emacs-devel@gnu.org http://lists.gnu.org/mailman/listinfo/emacs-devel --=-=-=--