Emacs Lisp: Call Function in Replacement String

, , …,

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. (because, for example, for some context, you want the link text to be full URL.)

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

Emacs 22 Feature: Call Function for Replacement String

In emacs 22, 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.

This function can be of general use. Whenever you need to replace text patterns with complicated heuristics, you can base your replacement function on the above code.

Here's the actual replacement function i used for this job:

(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 is beautiful.

Example: Replace Emacs Manual Links

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.

blog comments powered by Disqus