0

Let's assume I have the following code:

maybeBrowser <- function (msg) {
    if (interactive()) {
        cat(msg, "\n")
        ???
    } else {
        stop(msg)
    }
}

foo <- function (cond, ...) {
    if (cond) maybeBrowser("What a mess")
}

What would ??? have to look like in order to invoke browser() in the context of foo if cond evaluates to TRUE?

lith
  • 929
  • 8
  • 23

1 Answers1

2

Perhaps not the most elegant, but this seems to do what I think you're asking for.

First, two notes:

  1. I'm adding browser(); 1, knowing it doesn't work right away. The ;1 is because browser() will exit immediately if there is not some code after it. If there's something after the if/else block then you might not need it, but it's there for this. (This is only necessary with emacs/ESS: https://github.com/emacs-ess/ESS/issues/178)

  2. I added a variable within the foo environment to demonstrate that we don't (then do) see it.

First, the failing attempt:

maybeBrowser <- function (msg) {
    if (interactive()) {
        cat(msg, "\n")
        browser()
        q
    } else {
        stop(msg)
    }
}
foo <- function (cond, ...) {
    cat(capture.output(environment()), "\n")
    in_foo <- 1
    if (cond) maybeBrowser("What a mess")
}

foo(TRUE)
# <environment: 0x000000001b2beba0> 
# What a mess 
# Called from: maybeBrowser("What a mess")
# Browse[1]> 
debug at #5: q
# Browse[2]> 
environment()
# <environment: 0x000000001b280030>  <---- this is different
# Browse[2]> 
ls()
# [1] "msg"

Now a tweak to the code, motivated by https://stackoverflow.com/a/23891089/3358272

maybeBrowser <- function (msg) {
    if (interactive()) {
        cat(msg, "\n")
        return(evalq(browser(skipCalls=1), envir=parent.frame()))
    } else {
        stop(msg)
    }
}
foo <- function (cond, ...) {
    cat(capture.output(environment()), "\n")
    in_foo <- 1
    if (cond) maybeBrowser("What a mess")
}

foo(TRUE)
# <environment: 0x000000001b0b9d40> 
# What a mess 
# Called from: eval(quote({
#     browser()
#     1
#   ...
# Browse[1]> 
debug at #4: [1] 1
# Browse[3]> 
environment()
# <environment: 0x000000001b0b9d40>  <---- this is now the same
# Browse[3]> 
ls()
# [1] "cond"   "in_foo"

However, this is not allowing you to continue, stepping through any following code in foo, so it is an incomplete answer. I think unfortunately that that may not be feasible ... but perhaps a more internals-cognizant R bubba will have more clarity on this.

r2evans
  • 141,215
  • 6
  • 77
  • 149
  • 2
    This seems to do the trick: return(evalq(browser(skipCalls=1), envir=parent.frame())) – user2554330 Oct 15 '18 at 18:28
  • @user2554330 good point on `quote`ing the call. However, when I try this (adding `;1`, for my emacs/ess thing), and though this works correctly the first time, the second and subsequent times the `browse` exits instead of continuing to step in `foo`. – r2evans Oct 15 '18 at 18:36
  • In your example above, there aren't any statements after the `maybeBrowser` call. When I add one, typing `n` (for "next") steps there. – user2554330 Oct 15 '18 at 20:16
  • In my testing, I added a few more commands. After sourcing the file (defining the functions), the first call to `foo(TRUE)` results in stepping through the following commands, but the second and subsequent calls to `foo(TRUE)` (without redefining the functions) do not step through the follow-on commands. It's possible that this is an emacs/ess thing as well, though the first emacs/ess issue is due to where it assigns the call to `trace`, this does not appear to be the same thing. – r2evans Oct 15 '18 at 20:19
  • Okay, I see that too (in RStudio, not Emacs). Looks like an R bug to me. – user2554330 Oct 15 '18 at 20:22
  • 1
    Thank you all. The solution with `return(evalq(browser(skipCalls=1), envir=parent.frame()))` works for me very well. – lith Nov 22 '18 at 08:24