Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

in emacs-lisp, how do I correctly use replace-regexp-in-string?

Tags:

regex

emacs

lisp

Given a string, I want to replace all links within it with the link's description. For example, given

this is a [[http://link][description]]

I would like to return

this is a description

I used re-builder to construct this regexp for a link:

\\[\\[[^\\[]+\\]\\[[^\\[]+\\]\\]

This is my function:

(defun flatten-string-with-links (string)
    (replace-regexp-in-string "\\[\\[[^\\[]+\\]\\[[^\\[]+\\]\\]"
                (lambda(s) (nth 2 (split-string s "[\]\[]+"))) string))

Instead of replacing the entire regexp sequence, it only replaces the trailing "]]". This is what it produces:

this is a [[http://link][descriptiondescription

I don't understand what's going wrong. Any help would be much appreciated.

UPDATE: I've improved the regex for the link. It's irrelevant to the question but if someone's gonna copy it they may as well get the better version.

like image 649
eatloaf Avatar asked Feb 04 '11 04:02

eatloaf


People also ask

How do I replace a string in Emacs?

When you want to replace every instance of a given string, you can use a simple command that tells Emacs to do just that. Type ESC x replace-string RETURN, then type the search string and press RETURN. Now type the replacement string and press RETURN again.

Can I use RegEx in replace?

The Regex. Replace(String, String, MatchEvaluator, RegexOptions) method is useful for replacing a regular expression match if any of the following conditions is true: If the replacement string cannot readily be specified by a regular expression replacement pattern.


1 Answers

Your problem is that split-string is clobbering the match data, which replace-regexp-in-string is relying on being unchanged, since it is going to go use that match data to decide which sections of the string to cut out. This is arguably a doc bug in that replace-regexp-in-string does not mention that your replacement function must preserve the match data.

You can work around by using save-match-data, which is a macro provided for exactly this purpose:

(defun flatten-string-with-links (string)
    (replace-regexp-in-string "\\[\\[[a-zA-Z:%@/\.]+\\]\\[[a-zA-Z:%@/\.]+\\]\\]"
                (lambda (s) (save-match-data
                         (nth 2 (split-string s "[\]\[]+")))) string))
like image 138
nelhage Avatar answered Oct 05 '22 12:10

nelhage