0

Lately, I have seen some cases where I use re-search-forward to find a pattern, and then try to do something with the match, only to find that the match-data is apparently not correct.

For example, in a file I might have this text:

:RESULTS:
async-abcd-1234-output
:END:

I would expect this code to replace async-abcd-1234-output with "string"

(when (re-search-forward "async-abcd-1234-\\(output\\|value\\)" nil t)
  (let ((result-type (match-string 1)))
    (cond ((string= result-type "output")
           (replace-match "string")))))

That works when I run it in a buffer, but when I run it from a callback function I find that (match-string 1) is nil, and even (match-string 0) is "". What could be causing this? I feel like sometimes it works, and then stops working. It is baffling. Any ideas on how to debug this? When I use edebug, I can see the search appears to work, but the match-data isn't correct.

6
  • That is a typo. It should be joined to spell async. I must separated it to stop tat instance from getting copied. I am on the road now and can't seem to update it on my phone. Feb 3, 2017 at 18:21
  • 1
    Okay, if re-search-forward is returning non-nil, but not setting the match data, my only guess is perhaps inhibit-changing-match-data somehow gets bound in that case?
    – npostavs
    Feb 3, 2017 at 18:29
  • 1
    You do not show anything about the calling context, so it is difficult to guess why (match-string 1) returns nil. Show the code for how you are using the code you showed. It sounds like you are searching in the wrong buffer, or with point at the wrong buffer position. You say that your code "works in a buffer, but not..." what do you mean by it not being in a buffer? Unless you provide a string argument, re-search-forward always searches the text in a buffer. The question now seems unclear - underspecified. Originally it seemed that the "typo" you refer to was your problem...
    – Drew
    Feb 3, 2017 at 21:37
  • Have you tried using debug-on-entry or inserting calls to message to determine which buffer you are searching and where point is? If not, that would be a good first step.
    – Drew
    Feb 3, 2017 at 21:38
  • Inspecting the match data with edebug might be misleading as that could change while you're looking at it. Better insert a few good old 'message' calls to show context such as current buffer, text around point, match data etc. Both just after searching and just before replacing.
    – YoungFrog
    Feb 3, 2017 at 22:15

1 Answer 1

1

I think you want to give the function a single string REGEXP argument, not two separate strings:

(re-search-forward "async-abcd-1234-\\(output\\|value\\)" nil t)

And you want to replace only the first match group, not the entire match, so you want this:

(replace-match "string" nil nil nil 1)

In other words:

(defun foo ()
  (interactive)
  (when (re-search-forward "async-abcd-1234-\\(output\\|value\\)" nil t)
    (let ((result-type (match-string 1)))
      (cond ((string= result-type "output")
             (replace-match "string" nil nil nil 1))))))
2
  • I did want to replace the whole match. The issue is the example I gave works in a buffer but not when I call it from code. When called in a function it seems the match data isn't set correctly even though if I run with edebug I can see the search found the regexp in the buffer. Feb 3, 2017 at 11:53
  • If you want to replace the whole match then just remove the last 4 args from the call to replace-match.
    – Drew
    Feb 3, 2017 at 21:32

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.