0

Running spacemacs 0.200.13 on emacs 25.2.2 on Kubuntu 18.04.

I need to search and replace only on lines containing a specific string.

Interactively, I use occur, move to the occur buffer, and do all search and replacement there.

I would, now, like to do the same in Elisp code. So, I wrote the following code:

(defun xx-things ()
  (interactive)
  (save-excursion
    (goto-char (point-min))
    (while (search-forward "A" nil t)
      (while (search-forward "B" nil t)
        (replace-match "C" 1)))))

And used it on a buffer with:

AB
BB
CB

But it replaces all Bs with Cs whereas it should only replace AB with AC.

What is wrong here? How do I set it right?

  • 3
    When searching for B you must set the BOUND argument of search-forward to (line-end-position) . Depending on what you want you also could also add (forward-line 0) after (search-forward "A" nil t) to start the search for B at the beginning of the line containing A. Post an answer when you have a working version and accept it. – Tobias Jun 19 '18 at 8:31
  • @Tobias I am awfully sorry I need to ask something as basic as this. But I am not able to set the bound or limit for my search-forward. I tried (while (search-forward "A" (line-end-position nil) nil t) but that does not work. Tried googling for setting the limit/ bound, but no luck. What am I doing wrong? – deshmukh Jun 19 '18 at 12:36
  • 2
    You must not just add (line-end-position) but replace the nil with (line-end-position). Currently your argument list looks like: STRING="A", BOUND=(line-end-position nil), NOERROR=nil, COUNT=t. Two problems: NOERROR should be t and COUNT must be nil or a number, it must not be t. – Tobias Jun 19 '18 at 12:41
  • @Tobias A BiG thank you for your help. But looks like I am not able to fathom what's happening. I have updated the code. Now it hangs --- looks like it goes in some kind of infinite loop. – deshmukh Jun 19 '18 at 13:41
1

After you showed some effort for solving the problem I post here a version that is working for me. (At first I did not present you a full solution to give you the chance to learn from your own problems.)

The main problem with your code is that you do not restrict the search for B to the line where you found A.

EDIT: User @deshmukh found out that search-forwardonly goes to the end boundary in the case of no matches if the argument NOERROR is not nil and not t. Therefore t has to be replaced by 0 in the search for "B".

(defun xx-things ()
  (interactive)
  (save-excursion
    (goto-char (point-min))
    (while (search-forward "A" nil t)
      (beginning-of-line)
      (while (search-forward "B" (line-end-position) 0)
        (replace-match "C" 1)))))

Tested with emacs 26.1. But that code should run quite independently of the emacs-version.

  • I had reached exactly here. But it hangs. After I interrupt with a C-g, I see that only the first instance of AB is converted to AC. – deshmukh Jun 19 '18 at 14:06
  • 1
    @deshmukh Try to debug with edebug: Put point at defun and press C-u M-C-x. Afterwards use M-x xx-things. – Tobias Jun 19 '18 at 14:08
  • Thanks, yet again. I could find out the error. In the code, if it finds a line with A but not with B, it just keeps going back and forth on the same line. So, I changed the line to (while (search-forward "B" (line-end-position) 0) and now it works. BTW, that 0 was because the documentation said not nil and not t! And now it works. :) – deshmukh Jun 19 '18 at 14:39
  • 1
    @deshmukh I've corrected the solution. Thanks for the notice. – Tobias Jun 19 '18 at 14:47

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.