2

It seems that replace-regexp has a different behavior if called interactively and not, but I do not understand why, and how to get the wanted behavior.

What I want is the non-interactive equivalent of:

M-x replace-regexp RET foo1 RET foo\#

which, if called on buffer

foo1
foo1
foo1

creates

foo0
foo1
foo2

Now, non-interactively, the naive

(replace-regexp "foo1" "foo\\#")

yields

"Invalid use of '\' in replacement text"

The same happens with

(funcall-interactive "foo1" "foo\\#")

And singling or doubling the backslashes give the expected (unwanted) results:

(replace-regexp "foo1" "foo\#")
;; foo#
;; foo#
;; foo#

(replace-regexp "foo1" "foo\\\\#")
;; foo\#
;; foo\#
;; foo\#

Of course, running call-interactively works, but in my use-case the regexp and replacement string will be crafted by elisp code.

Running a loop searching and replacing would probably work, but is it really the only solution? It seems hard to believe.

3
  • 1
    The conversion of the replacement text into lisp code is contained in the interactive specification of replace-regexp. It is essentially (query-replace-compile-replacement "foo\\#" t) which returns (replace-eval-replacement concat "foo" (number-to-string replace-count)) (resolved over several function calls). The documentation of replace-regexp says quite clearly that \# only works in interactive calls and that you should use re-search-forward and replace-match instead in emacs-lisp code.
    – Tobias
    Apr 1, 2017 at 19:33
  • (query-replace-compile-replacement to-string t) seems to suit my purposes perfectly. Are there any pitfalls that I don't see yet? And in any case, would you mind turning your comment with its great explanation into an answer?
    – T. Verron
    Apr 1, 2017 at 19:49
  • 1
    You can/should write up an answer yourself and accept it afterwards. query-replace-compile-replacement is a documented function from replace.el. There are no double-slashes in the name and the code is not marked as internal. Therefore, usage of the function should be safe. Have fun!
    – Tobias
    Apr 1, 2017 at 20:04

1 Answer 1

3

(Credits go to Tobias)

replace-regexp (and query-replace-regexp) have their second read argument malaxed into a suitable form for the elisp call. In particular, in case the replacement string contains elisp forms, it is turned into a quoted elisp expression by the function query-replace-compile-replacement.

It is actually written explicitely in the documentation for query-replace-regexp... once you know what to look for:

When using those Lisp features interactively in the replacement text, TO-STRING is actually made a list instead of a string. Use C-x M-: after this command for details.

Anyway, it turns out that replacing "foo\\#" with `(query-replace-compile-replacement "foo\#" t) works for my purpose. And the entire call, with elisp-supplied strings and just enough interactivity,

(let ((from-string "foo1")
      (to-string "foo\\#"))
   (funcall-interactively #'query-replace-regexp
                          from-string
                          (query-replace-compile-replacement to-string t)))

works as intended.

Thanks!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.