Skip to content

Commit

Permalink
Support running deferred_run() within knitr
Browse files Browse the repository at this point in the history
  • Loading branch information
lionel- committed Jan 16, 2024
1 parent 8582b92 commit 40a6fd9
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 12 deletions.
2 changes: 2 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
* `deferred_run()` now reports the number of executed expressions with
a message.

* `deferred_run()` can now be run at any point in a knitr file (#235).

,* `local_tempfile()` now writes `lines` in UTF-8 (#210) and always uses
`\n` for newlines (#216).

Expand Down
21 changes: 13 additions & 8 deletions R/defer.R
Original file line number Diff line number Diff line change
Expand Up @@ -109,15 +109,19 @@ defer_parent <- function(expr, priority = c("first", "last")) {
#' @rdname defer
#' @export
deferred_run <- function(envir = parent.frame()) {
if (knitr_in_progress()) {
stop("Can't run `deferred_run()` in a knitted document")
}
if (is_top_level_global_env(envir)) {
handlers <- the$global_exits
if (knitr_in_progress() && identical(envir, knitr::knit_global())) {
# The handlers are thunks so we don't need to clear them.
# They will only be run once.
frame <- knitr_exit_frame(envir)
handlers <- knitr_handlers(frame)
} else {
handlers <- frame_exits(envir)
if (is_top_level_global_env(envir)) {
handlers <- the$global_exits
} else {
handlers <- frame_exits(envir)
}
deferred_clear(envir)
}
deferred_clear(envir)

n <- length(handlers)
i <- 0L
Expand Down Expand Up @@ -224,7 +228,8 @@ is_top_level_global_env <- function(envir, frames = sys.frames()) {
# This picks up knitr's first frame on the stack and registers the
# handler there. To avoid mixing up knitr's own exit handlers with
# ours, we don't hook directly but instead save the list of handlers
# as an attribute on the frame environment.
# as an attribute on the frame environment. This allows `deferred_run()`
# to run our handlers without running the knitr ones.
defer_knitr <- function(expr, envir, priority = c("first", "last")) {
priority <- match.arg(priority, choices = c("first", "last"))

Expand Down
50 changes: 50 additions & 0 deletions tests/testthat/_snaps/defer.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,53 @@
Message
Ran 1/3 deferred expressions

# can't run `deferred_run()` in knitr

Code
writeLines(readLines(out))
Output
```r
withr::deferred_run()
```
```
## No deferred expressions to run
```
```r
defer(writeLines('1'))
writeLines('2')
```
```
## 2
```
```r
defer(writeLines('3'))
```
```r
writeLines('4')
```
```
## 4
```
```r
withr::deferred_run()
```
```
## 3
## 1
```
```
## Ran 2/2 deferred expressions
```

20 changes: 16 additions & 4 deletions tests/testthat/test-defer.R
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,25 @@ test_that("can't run `deferred_run()` in knitr", {
skip_if_cannot_knit()

rmd <- local_tempfile(fileext = ".Rmd")
out <- local_tempfile(fileext = ".md")
writeLines(rmd, text = "
```{r}
withr::deferred_run()
```
```{r}
defer(writeLines('1'))
writeLines('2')
defer(writeLines('3'))
```
```{r}
writeLines('4')
withr::deferred_run()
```
")
expect_error(
suppressMessages(rmarkdown::render(rmd, quiet = TRUE)),
"in a knitted document"
)

knitr::knit(rmd, out, quiet = TRUE)

expect_snapshot({
writeLines(readLines(out))
})
})

0 comments on commit 40a6fd9

Please sign in to comment.