Emacs Lisp: Call Function in Replacement String

Master emacs+lisp, benefit for life. Testimonials. Thank you for support.
, , …,

This page shows you how to use a elisp function in your regex replacement. This lets you do transformation of the matched text. For example, replacing “_” to space, or insert timestamp, current file name, in your replacement text.

Problem Description

Suppose you have thousands of links to Wikipedia in the following form, spread over 5 thousand HTML pages:

① <a href="…/Emacs_Lisp">Emacs Lisp</a>
② <a href="…/Emacs_Lisp">Emacs_Lisp</a>
③ <a href="…/Emacs_Lisp">http://en.wikipedia.org/wiki/Emacs_Lisp</a>

You want format 2 and 3 to be replaced with format 1, but on case-by-case basis. (some of them you want the URL in the link text, depending on the article.)

For simplicity, let's say i just want to replace format 2 to format 1 on a case-by-case basis.

Call Function for Replacement String

In emacs 22 (released in ), there's a new feature that allows you use a function as your replacement string.

In the replace string prompt, give \,(function name), where function name is your elisp function.

The function needs no argument. Its return value is used as the replacement string.

The task here is to write the replacement function.

Solution

Let's say our function will be named ff. ff will take 1 input that's the matched text, then replace _ by space, then return the new text.

Here's our function template:

(defun ff ()
  "temp function. Returns a string based on current regex match."
; 1. get the matched text
; 2. transform the matched text
; 3. returns the transformed text
)

How do we actually get the matched text? Here's the solution:

(defun ff ()
  "return matched text with underscore replaced by space."
  (replace-regexp-in-string "_" " " (match-string 1))
)

The (match-string 1) gives you the first captured string. (“1” is for 1st captured pattern, “2” for 2nd captured pattern. “0” is the entire match.). The replace-regexp-in-string is used to transform the text. (To make emacs aware of ff, select the whole definition, then call eval-region.)

So, with this function written, we can call query-replace-regexp or dired-do-query-replace-regexp, then give this pattern:

>\([_A-Za-z0-9]+\)</a>

And the replacement expression would be:

\,(ff)

and we are all done.

You can use this technique to do find replace on all files in a dir, including subdirectories. Call find-dired. For detail, see Emacs: Interactively Find/Replace String Patterns on Multiple Files.

To save and close all changed files, call ibuffer and type 【* u S D】. For detail, see Emacs: List Buffers.

Replacement Function Template

Whenever you need to replace text patterns by a function, you can use this template:

(defun wikipedia-link-replacement ()
  "Returns a canonical form of Wikipedia link from a regex match.

The regex to be used for this function is:

 <a href=\"http://\\(..\\)\\.wikipedia.org/wiki/\\([^\"]+\\)\">\\(\\([-.A-Za-z0-9]+_\\)+[-.A-Za-z0-9]+ ?\\)</a>

To use this function, call `query-replace-regexp', then in the replacement prompt give:
 \\,(wikipedia-link-replacement)
"
  (let (langCode articlePath linkText linkText2 returnText)
    (setq langCode (match-string 1))
    (setq articlePath (match-string 2))
    (setq linkText (match-string 3))
    (setq linkText2 (replace-regexp-in-string "_" " " articlePath))
    (setq returnText
          (concat "<a href=\"http://"
                  langCode ".wikipedia.org/wiki/"
                  articlePath "\">" linkText2 "</a>" ))
    returnText ) )

Emacs ♥

Example: Replace Emacs Manual Links

Here's another example of batch find & replace using a function for replacement.

I need to find all text of the form:

(info "(emacs) Option Index")

and change it into this form:

<a href="../emacs_manual/Option-Index.html">(info "(emacs) Option Index")</a>

Here's the regex i use:

(info "(emacs) \([^"]+?\)")

Here's the replacement code: \,(ff).

Here's the elisp code:

(defun ff ()
  "temp"
  (interactive)
  (let (matchedText url replaceText anchorText)
    (setq matchedText (match-string 1 ) )
    (setq replaceText (replace-regexp-in-string " " "-" matchedText))
    (setq url (concat "../emacs_manual/" replaceText ".html" ) )
    (setq anchorText
          (concat
           "(info \"(emacs) "
           matchedText
           "\")"
           )
          )
    (concat "<a href=\"" url "\">" anchorText "</a>")
    ))

More Examples

Here are more examples of using a function as replacement string.

Like what you read?
Buy Xah Emacs Tutorial
or share some
blog comments powered by Disqus