Skip to content

Commit fb4f255

Browse files
authored
qmd_to_r_script hide eval=FALSE and skip purl=TRUE (#278)
This matches more closely to the behavior of the `knitr` package with `purl`
1 parent cc50446 commit fb4f255

File tree

8 files changed

+122
-8
lines changed

8 files changed

+122
-8
lines changed

DESCRIPTION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Package: quarto
22
Title: R Interface to 'Quarto' Markdown Publishing System
3-
Version: 1.4.4.9028
3+
Version: 1.4.4.9029
44
Authors@R: c(
55
person("JJ", "Allaire", , "[email protected]", role = "aut",
66
comment = c(ORCID = "0000-0003-0174-9868")),

NEWS.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -81,10 +81,11 @@
8181

8282
- `qmd_to_r_script()` extracts R code cells from Quarto documents and
8383
creates R scripts. This experimental function preserves chunk options
84-
using `#|` syntax, adds YAML metadata as spin-style headers, and handles
85-
mixed-language documents by filtering only R cells. Complements the
86-
existing `add_spin_preamble()` function for working with R scripts in
87-
Quarto workflows (#208, quarto-dev/quarto-cli#9112).
84+
using `#|` syntax, adds YAML metadata as spin-style headers, handles
85+
mixed-language documents by filtering only R cells, skips chunks with
86+
`purl: false`, and properly processes `eval: false` chunks by commenting
87+
out their code. Complements the existing `add_spin_preamble()` function
88+
for working with R scripts in Quarto workflows (#208, #277, quarto-dev/quarto-cli#9112).
8889

8990
- `quarto_available()` checks if Quarto CLI is found (thanks, @hadley,
9091
#187).

R/utils-extract.R

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@
2323
#' - Adding the document's YAML metadata as a spin-style header
2424
#' - Creating an R script that can be rendered with the same options
2525
#'
26+
#' ## Chunk option handling:
27+
#' - Chunks with `purl: false` are completely skipped and not included in the output
28+
#' - Chunks with `eval: false` have their code commented out (prefixed with `# `) in the R script
29+
#' - All other chunk options are preserved as `#|` comment headers
30+
#'
2631
#' ## File handling:
2732
#' - If the output R script already exists, the function will abort with an error
2833
#' - Non-R code cells (e.g., Python, Julia, Observable JS) are ignored
@@ -35,7 +40,15 @@
3540
#' more details on rendering R scripts with Quarto.
3641
#'
3742
#' The resulting R script uses Quarto's executable cell format with `#|`
38-
#' comments to preserve chunk options like `echo`, `eval`, `output`, etc.
43+
#' comments to preserve chunk options like `label`, `echo`, `output`, etc.
44+
#'
45+
#' The resulting R script could also be `source()`d in R, as any `eval = FALSE` will be commented out.
46+
#'
47+
#' ## Limitations:
48+
#' This function relies on static analysis of the Quarto document by `quarto inspect`. This means that
49+
#' any \pkg{knitr} specific options like `child=` or specific feature like [knitr::read_chunk()] are not supported.
50+
#' They rely on tangling or knitting by \pkg{knitr} itself. For this support,
51+
#' one should look at [knitr::hook_purl()] or [knitr::purl()].
3952
#'
4053
#' @return Invisibly returns the path to the created R script file, or
4154
#' `NULL` if no R code cells were found.
@@ -110,12 +123,22 @@ qmd_to_r_script <- function(qmd, script = NULL) {
110123
}
111124

112125
r_codeCells <- codeCells[codeCells$language == "r", ]
113-
114126
content <- character(nrow(r_codeCells))
115127
for (i in seq_len(nrow(r_codeCells))) {
116128
row <- r_codeCells[i, ]
117129
metadata_list <- as.list(row$metadata)
118130
metadata_clean <- metadata_list[!is.na(metadata_list)]
131+
if (isFALSE(metadata_clean$purl)) {
132+
# cell with purl: false should be skipped
133+
next
134+
}
135+
if (isFALSE(metadata_clean$eval)) {
136+
# cell with eval: false should be commented out in R script
137+
row$source <- paste(
138+
c(paste0("# ", head(xfun::split_lines(row$source), -1)), ""),
139+
collapse = "\n"
140+
)
141+
}
119142
content[i] <- paste(
120143
c(create_code_preamble(metadata_clean), row$source),
121144
collapse = "\n"

man/qmd_to_r_script.Rd

Lines changed: 19 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/testthat/_snaps/utils-extract/purl.R

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,16 @@
44
#' ---
55
#'
66

7+
#| label: my-label
78
#| echo: false
89
#| output: asis
910
cat("Hello, world")
1011

1112
#| echo: true
1213
cat("more")
1314

15+
#| eval: false
16+
# # This code should not run.
17+
# 1 + a
18+
19+

tests/testthat/_snaps/utils-extract/purl.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,10 @@ cat("more")
1414
more
1515
:::
1616
::::
17+
18+
::: cell
19+
``` {.r .cell-code}
20+
# # This code should not run.
21+
# 1 + a
22+
```
23+
:::

tests/testthat/resources/purl-r.qmd

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ format: html
66
## Section
77

88
```{r}
9+
#| label: my-label
910
#| echo: false
1011
#| output: asis
1112
cat("Hello, world")
@@ -16,4 +17,21 @@ cat("Hello, world")
1617
```{r}
1718
#| echo: true
1819
cat("more")
20+
```
21+
22+
## A section that should be commented out
23+
24+
```{r}
25+
#| eval: false
26+
# This code should not run.
27+
1 + a
28+
```
29+
30+
## A section explicitly commented out with `purl = FALSE`
31+
32+
```{r}
33+
#| purl: false
34+
# This code should not be included in the purl output.
35+
# but it works
36+
1 + 1
1937
```

tests/testthat/test-utils-extract.R

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,47 @@ test_that("qmd_to_r_script() writes R file that renders", {
4646
)
4747
})
4848

49+
test_that("qmd_to_r_script() comment cells with eval = TRUE", {
50+
r_script <- withr::local_tempfile(pattern = "purl", fileext = ".R")
51+
52+
qmd_to_r_script(
53+
resources_path("purl-r.qmd"),
54+
script = r_script
55+
)
56+
content <- xfun::file_string(r_script)
57+
expect_match(
58+
content,
59+
"# # This code should not run.",
60+
fixed = TRUE
61+
)
62+
expect_no_match(
63+
content,
64+
"(?<!# )# This code should not run\\.",
65+
perl = TRUE
66+
)
67+
})
68+
69+
test_that("qmd_to_r_script() ignore cells with purl = FALSE", {
70+
r_script <- withr::local_tempfile(pattern = "purl", fileext = ".R")
71+
72+
qmd_to_r_script(
73+
resources_path("purl-r.qmd"),
74+
script = r_script
75+
)
76+
content <- xfun::file_string(r_script)
77+
expect_no_match(
78+
content,
79+
"#| purl: false",
80+
fixed = TRUE
81+
)
82+
expect_no_match(
83+
content,
84+
"# This code should not be included in the purl output.",
85+
fixed = TRUE
86+
)
87+
})
88+
89+
4990
test_that("qmd_to_r_script() do nothing on file with no code", {
5091
skip_if_no_quarto()
5192
expect_message(

0 commit comments

Comments
 (0)