2

Suppose I have a code base with many instances of string "console.log(" for example. I want to replace each one with one of the following six lines:

logger(FATAL,
logger(ERROR,
logger(WARNING,
logger(INFO,
logger(LOG,
logger(DEBUG,

Each replacement is different and depends on the context in which the search string occurs (in a way that can only be judged by a human). So I want to do a search and replace, but instead of only typing "y" or "n", I want to type for example 1, 2, 3, 4, 5 and 6 (and the number I type determines which one of the set is used as the replacement for that instance).

I prefer to do this in one pass, and I also want to avoid typing any of the replacement strings (in full) more than once as suggested here,

Query replace with different replacements

since the replacements will not occur consecutively but randomly. In a typical large code base there could be hundreds of changes between consecutive instances, and this will require typing the replacement strings too many times.

Attempted solution sketch (does not work):

Use search-and-replace but with a macro replacement, and the macro replacement itself invokes a little function to ask me what the replacement should be. For example something similar to this:

    (defun prompt-me-for-replacement (response)
      (interactive (list (read-key "Choice [1-6]: ")))
      ; or this --> (interactive "cChoice [1-6]: ")
        (cond
         ((equal response 1) "logger(FATAL, ")
         ((equal response 2) "logger(ERROR, ")
         ((equal response 3) "logger(WARN, ")
         ((equal response 4) "logger(INFO, ")
         ((equal response 5) "logger(LOG, ")
         ((equal response 6) "logger(DEBUG, ")
         (t (prompt-me-for-replacement))))

Then I want to invoke it with query-replace-regexp (or a similar function) and when I am prompted for the replacement string, I say \,(prompt-me-for-replacement). This does not work, firstly because the function prompt-me-for-replacement will not even run once directly from the minibuffer. Secondly I don't know if an interactive form can be called from the middle of a search and replace operation like I'm trying to do, because it is one interactive operation in the middle of another interactive one.

Any pointers/ideas will be greatly appreciated!!!

1 Answer 1

1

How about this?

(defun my-query-replace-console-log ()
  "Replace instances of console.log( from a menu of options."
  (interactive)
  (let ((prompt "1:fatal, 2:error, 3:warning, 4:info, 5:log, 6:debug ? ")
        (replacements '((?1 . "logger(FATAL,")
                        (?2 . "logger(ERROR,")
                        (?3 . "logger(WARNING,")
                        (?4 . "logger(INFO,")
                        (?5 . "logger(LOG,")
                        (?6 . "logger(DEBUG,"))))
    (while (search-forward "console.log(" nil t)
      (when-let ((string (alist-get (read-char prompt) replacements)))
        (replace-match string)))))

Any key other than 1-6 will skip to the next match.

1
  • 1
    If you're comfortable touch-typing then you might consider f,e,w,i,l,d as the keys, rather than 1,2,3,4,5,6, seeing as there aren't any conflicts.
    – phils
    Commented Feb 24, 2021 at 5:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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