0

I have an R function which is similar to this:

logFun <- function(msg1, msg2){
  return(sprintf("%s: %s", msg1, msg2))
}
logFun("123", "456")

It's used in a lot of places, and not always entered nicely e.g. these are all example use cases:

var1 <- "aa"
var2 <- "bb"
logFun(var1, var2)
logFun("aa", var2)
logFun("aa", "bb")
logFun(var1, "bb")
logFun(msg1 = "aa", msg2 = "bb")
logFun(msg1 = var1, msg2 = "bb")
...

Say I had a new function e.g.

logFun2 <- function(msg1, msg2, type){
  return(sprintf("[%s] %s: %s", type, msg1, msg2))
}
logFun2("123", "456", "bug")

and the function calls have to be preserved (msg1, msg2, type), as many have been converted from logFun to logFun2. What I am trying to do with Sublime is to replace the list of use cases above with the following:

logFun2(var1, var2, type = "bug")
logFun2("aa", var2, type = "bug")
logFun2("aa", "bb", type = "bug")
logFun2(var1, "bb", type = "bug")
logFun2(msg1 = "aa", msg2 = "bb", type = "bug")
logFun2(msg1 = var1, msg2 = "bb", type = "bug")

I've read some bits around lookbehind matching, but finding it tricky to find out if its a doable job, or whether I should find another solution to the problem. With https://regex101.com/, I've managed to isolate the logFun( part with (?<=logFun\(), but not sure where to go from here. Any guidance would be appreciated :)

Thanks, Jonny

Edit 1

Q: Why cannot you define your logFun2 function with default value for argument type = "bug" and just replace logFun with logFun2?

A: In reality, we don't just have logFun. We say have functions logBug, logInfo, logWarning etc. We are then changing these in to a singular function logGeneral e.g. logBug becomes logGeneral(.., .., type = "bug"). So, yep you are right we could for one type of log specify the default and not worry about that one.

Jonny Phelps
  • 2,687
  • 1
  • 11
  • 20

3 Answers3

1

I don't use Sublime but the regex to isolate your function could be:

(\blogFun)(\b\(.*)\)

And replace :

$12$2, type = "bug")
Alekos
  • 368
  • 2
  • 8
  • Hey, this works great thank you! For some reason in sublime it got rid of the `logFun` part, but `logFun2$12$2, type = "bug")` seems to work. :) – Jonny Phelps Apr 11 '18 at 10:21
1

Since there are multiple types, then one could use a regex for all of them:

\blog(\w+)\(([^()]+)\)

And then replace by using the PCRE lowercase transformation \L for the type name after "log".

Replace string:

logFun2(\2, type = "\L\1") 

Test here

Also this

LukStorms
  • 28,916
  • 5
  • 31
  • 45
  • Hey again, this has worked great, but I've found some annoying cases in the code :s. Any ideas how to get round something like `logDebug(paste("annoying", "case"), msg2 = "data_prep")` over multiple lines, here, it stops at the first bracket, due to the clause (perfectly reasonable :)) Thanks – Jonny Phelps Apr 11 '18 at 10:46
  • 1
    One way is to change the "more then 1 of anything but ( or )" `[^()]+`. For example: `\blog(Debug|Bug|Info|Warning|Fun)\(([^\r\n]+)\)`. Which assumes the string is on 1 line. Because the default of `.+` is a greedy search it will match till the last `)`. Perhaps the `[^\r\n]+` could be replaced with `.+`, if you dare. But that could match to much I guess. – LukStorms Apr 11 '18 at 12:09
  • thanks for the advise. I was able to get a very specific solution for this case, fingers crossed I don't find any more weird ones :) `(\blogDebug)(\b\(.*\).*)\n*\s*(\b.*)\)` – Jonny Phelps Apr 11 '18 at 12:16
  • 1
    So some are not on a single line after all. For the multiline with brackets in them? Then this kind of regex would match them : `\blog(\w+)\(((?:[^()]+(?:\([^()]+\))?)*)\)` – LukStorms Apr 11 '18 at 13:53
1

Using sublime I used for your cases:

Find: (logFun)(\(.+)\)

Replace: \12\2, type = "bug")

If you have also logBug, logInfo, logWarning (or even logDebug(paste("annoying", "case"), msg2 = "data_prep")) then you could modify just the find part:

Find: (log[A-Z][a-z]*)(\(.+)\)

s_baldur
  • 29,441
  • 4
  • 36
  • 69