Kurt Hesselbart
Kurt Hesselbart

Reputation: 31

manipulate regexp-search matches using `query-regexp-replace` in a defun

Since version 22 of Emacs, we can use \,(function) for manipualting (parts of) the regex-search result before replacing it. But – this is mentioned often, but nonetheless still the truth – we can use this construct only in the standard interactive way. (Interactive like: By pressing C-M-% or calling query-replace-regexp with M-x.)

As an example:

If we have

and want to get

we can use:

M-x query-replace-regexp <return>
  \[\([A-Za-z-]+\)\([^0-9]*\) \([0-9]\{4\}\)\]    
  [\1\2 \\function{\,(downcase \1)\3}{\3}] 

to get it done. So this can be done pretty easy.

In my own defun, I can use query only by replacing without freely modifying the match, or modify the prepared replaced string without any querying. The only way I see, is to serialize it in such a way:

(defun form-to-function () 
  (interactive)
  (goto-char (point-min))
  (while (query-replace-regexp 
    "\\[\\([A-Za-z-]+\\)\\([^0-9]*\\) \\([0-9]\\{4\\}\\)\\]" 
    "[\\1\\2 \\\\function{\\1\\3}{\\3}]" ))
  (goto-char (point-min))
  (while (search-forward-regexp "\\([a-z0-9]\\)" nil t) 
    (replace-match (downcase (match-string 1)) t nil)
 )
)

For me the query is important, because I can't be sure, what the buffer offers me (= I can't be sure, the author used this kind of string always in the same manner).

I want to use an elisp function, because it is not the only recurring replacement (and also not only one buffer (I know about dired-do-query-replace-regexp but I prefer working buffer-by-buffer with replace-defuns)).

At first I thought I only miss something like a query-replace-match to use instead of replace-match. But I fear, I am also missing the easy and flexible way of rearrange the string the the query-replace-regexp.

So I think, I need a \, for use in an defun. And I really wonder, if I am the only one, who is missing this feature.

Upvotes: 2

Views: 379

Answers (2)

Kurt Hesselbart
Kurt Hesselbart

Reputation: 31

The query-replace-function can handle replacement not only as a string, but as a list including the manipulating elements. The use of concat archives building an string from various elements.

So one who wants to manipulate the search match by a function before inserting the replacement can use query-replace-regexp also in a defun.

(defun form-to-function () 
  (interactive)
  (goto-char (point-min))
  (query-replace-regexp 
   "\\[\\([A-Za-z-]+\\)\\([^0-9]*\\) \\([0-9]\\{4\\}\\)\\]"
   (quote (replace-eval-replacement concat "[\\1\\2 \\\\function{" 
    (replace-quote (downcase (match-string 1))) "\\3}{\\3}]")) nil ))
  • match-string 1 returns the first expression of our regexp-search.

  • `replace-quote' helps us doublequoting the following expression.

  • concat forms a string from the following elements.

and

  • replace-eval-replacement is not documented.

Why it is in use here nevertheless, is because of emacs seems to use it internally, while performing the first »interactive« query-replace-regexp call. At least is it given by asking emacs with repeat-complex-command.

I came across repeat-complex-command (bound to [C-x M-:].) while searching for an answer in the source code of query-replace-regexp.

So an easy to create defun could be archieved by performing the standard search and replace way as told in the question and after first sucess pressing [C-x M-:] results in an already Lisp formed command, which can be copied and pasted in a defun.

Edit (perform-replace)

As Stefan mentioned, one can use perform-replace to avoid using query-replace-regexp.

Such a function could be:

(defun form-to-function () 
  (interactive)
  (goto-char (point-min))
  (while (perform-replace
      "\\[\\([A-Za-z-]+\\)\\([^0-9]*\\) \\([0-9]\\{4\\}\\)\\]"
      (quote (replace-eval-replacement concat "[\\1\\2 \\\\function{"
      (replace-quote (downcase (match-string 1))) "\\3}{\\3}]"))
       t t nil)))

The first boolean (t) is a query flag, the second is the regexp switch. So it works also perfectly, but it didn't help finding the replacement expression as easy as in using \,.

Upvotes: 1

Stefan
Stefan

Reputation: 28531

If you want your rsearch&replace to prompt the user, that means you want it to be interactive, so it's perfectly OK to call query-replace-regexp (even if the byte-compiler will tell you that this is meant for interactive use only). If the warning bothers you, you can either wrap the call in with-no-warnings or call perform-replace instead.

The docstring of perform-replace sadly doesn't (or rather "didn't" until today) say what is the format of the replacements argument, but you can see it in the function's code:

;; REPLACEMENTS is either a string, a list of strings, or a cons cell
;; containing a function and its first argument.  The function is
;; called to generate each replacement like this:
;;   (funcall (car replacements) (cdr replacements) replace-count)
;; It must return a string.

Upvotes: 2

Related Questions