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.

John Kitchin
  • 11,555
  • 1
  • 19
  • 41
  • 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. – John Kitchin Feb 03 '17 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 03 '17 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 03 '17 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 03 '17 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 03 '17 at 22:15
  • Thanks for the debugging tips. That is what I was looking for. It sounds like messages are among the best debugging options for this. i will give them a try! – John Kitchin Feb 04 '17 at 08:22

1 Answers1

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))))))
Drew
  • 75,699
  • 9
  • 109
  • 225
  • 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. – John Kitchin Feb 03 '17 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 03 '17 at 21:32