Skip to content

Commit

Permalink
Apply knitr print method for inline r code (#1193)
Browse files Browse the repository at this point in the history
Fixes #1179
  • Loading branch information
maxheld83 authored Apr 18, 2022
1 parent 6ea92ed commit 8279a15
Show file tree
Hide file tree
Showing 6 changed files with 228 additions and 99 deletions.
4 changes: 4 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# roxygen2 (development version)

* Inline R code is now powered by knitr. Where available, (knit) print methods
are applied (#1179). This change alters outputs and brings roxygen in line
with console and R markdown behavior. `x <- "foo"` no longer inserts anything
into the resulting documentation, but `x <- "foo"; x` will.
* roxygen2 can once again read UTF-8 paths on windows (#1277).

* `@exportS3method pkg::generic` now works even when `pkg::generic` isn't
Expand Down
33 changes: 19 additions & 14 deletions R/markdown.R
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

markdown <- function(text, tag = NULL, sections = FALSE) {
tag <- tag %||% list(file = NA, line = NA)
expanded_text <- tryCatch(
Expand Down Expand Up @@ -27,7 +26,7 @@ markdown <- function(text, tag = NULL, sections = FALSE) {
#' To insert the name of the current package: `r packageName()`.
#'
#' The `iris` data set has `r ncol(iris)` columns:
#' `r paste0("``", colnames(iris), "``", collapse = ", ")`.
#' `r paste0("\x60\x60", colnames(iris), "\x60\x60", collapse = ", ")`.
#'
#' ```{r}
#' # Code block demo
Expand All @@ -46,10 +45,13 @@ markdown <- function(text, tag = NULL, sections = FALSE) {
#' ```{r test-figure}
#' plot(1:10)
#' ```
#'
#'
#' Also see `vignette("rd-formatting")`.
#'
#' @param text Input text.
#' @return Text with the inline code expanded. A character vector of the
#' same length as the input `text`.
#' @return
#' Text with R code expanded.
#' A character vector of the same length as the input `text`.
#'
#' @importFrom xml2 xml_ns_strip xml_find_all xml_attr
#' @importFrom purrr keep
Expand Down Expand Up @@ -95,20 +97,23 @@ eval_code_nodes <- function(nodes) {

eval_code_node <- function(node, env) {
if (xml_name(node) == "code") {
text <- str_replace(xml_text(node), "^r ", "")
paste(eval(parse(text = text), envir = env), collapse = "\n")

# write knitr markup for inline code
text <- paste0("`", xml_text(node), "`")
} else {
# write knitr markup for fenced code
text <- paste0("```", xml_attr(node, "info"), "\n", xml_text(node), "```\n")
opts_chunk$set(
error = FALSE,
fig.path = "man/figures/",
fig.process = function(path) basename(path)
)
knit(text = text, quiet = TRUE, envir = env)
}
old_opts <- purrr::exec(opts_chunk$set, knitr_chunk_defaults)
withr::defer(purrr::exec(opts_chunk$set, old_opts))
knit(text = text, quiet = TRUE, envir = env)
}

knitr_chunk_defaults <- list(
error = FALSE,
fig.path = "man/figures/",
fig.process = function(path) basename(path)
)

str_set_all_pos <- function(text, pos, value, nodes) {
# Cmark has a bug when reporting source positions for multi-line
# code tags, and it does not count the indenting space in the
Expand Down
6 changes: 4 additions & 2 deletions man/markdown_pass1.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions tests/testthat/_snaps/markdown-code.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@

Code
out <- roc_proc_text(rd_roclet(), block)[[1]]
Output
Message
Quitting from lines 1-1 ()
Condition
Warning:
[<text>:4] @description failed to evaluate inline markdown code
Expand Down
91 changes: 72 additions & 19 deletions tests/testthat/test-markdown-code.R
Original file line number Diff line number Diff line change
@@ -1,33 +1,74 @@

test_that("can eval", {
test_that("can eval inline code", {
out1 <- roc_proc_text(rd_roclet(), "
#' @title Title `r 1 + 1`
#' @description Description `r 2 + 2`
#' @md
foo <- function() {}")[[1]]

foo <- function() NULL
")[[1]]
expect_equal(out1$get_value("title"), "Title 2")
expect_equal(out1$get_value("description"), "Description 4")
})

test_that("uses the same env for a block, but not across blocks", {
test_that("can eval fenced code", {
out1 <- roc_proc_text(rd_roclet(), "
#' Title `r foobarxxx123 <- 420` `r foobarxxx123`
#' @title Title
#' @details Details
#' ```{r lorem}
#' 1+1
#' ```
#' @md
foo <- function() NULL
")[[1]]
expect_match(out1$get_value("details"), "2")
})

test_that("use same env within, but not across blocks", {
example <- "
#' Title `r baz <- 420` `r baz`
#'
#' Description `r exists('foobarxxx123', inherits = FALSE)`
#' Description `r exists('baz', inherits = FALSE)`
#' @md
#' @name dummy
NULL
bar <- function() NULL
#' Title
#'
#' Description `r exists('baz', inherits = FALSE)`
#' @md
zap <- function() NULL
"
out1 <- roc_proc_text(rd_roclet(), example)[[1]]
out2 <- roc_proc_text(rd_roclet(), example)[[2]]
expect_equal(out1$get_value("title"), "Title 420")
expect_equal(out1$get_value("description"), "Description TRUE")
expect_equal(out2$get_value("description"), "Description FALSE")
})

#' Title another
test_that("appropriate knit print method for fenced and inline is applied", {
rlang::local_bindings(
knit_print.foo = function(x, inline = FALSE, ...) {
knitr::asis_output(ifelse(inline, "inline", "fenced"))
},
.env = globalenv()
)
out1 <- roc_proc_text(rd_roclet(), "
#' @title Title `r structure('default', class = 'foo')`
#'
#' @details Details
#'
#' ```{r}
#' structure('default', class = 'foo')
#' ```
#'
#' Description `r exists('foobarxxx123', inherits = FALSE)`
#' @md
#' @name dummy2
NULL")
expect_equal(out1$dummy.Rd$get_value("title"), "Title 420 420")
expect_equal(out1$dummy.Rd$get_value("description"), "Description TRUE")
expect_equal(out1$dummy2.Rd$get_value("description"), "Description FALSE")
#' @name bar
NULL
")
expect_match(out1$bar.Rd$get_value("details"), "fenced", fixed = TRUE)
expect_match(out1$bar.Rd$get_value("title"), "inline", fixed = TRUE)
})

test_that("can create markdown markup", {
Expand All @@ -40,12 +81,24 @@ test_that("can create markdown markup", {
test_that("can create markdown markup piecewise", {
expect_identical(
markdown(
"Description [`r paste0('https://url]')`](`r paste0('link text')`)"
"Description [`r paste0('https://url')`](`r paste0('link text')`)"
),
"Description \\link{https://url}](link text)"
"Description \\link{https://url}(link text)"
)
})

test_that("can create escaped markdown markup", {
# this workaround is recommended by @yihui
# "proper" escaping for inline knitr tracked in https://github.com/yihui/knitr/issues/1704
out1 <- roc_proc_text(rd_roclet(), "
#' Title
#' Description `r paste0('\\x60', 'bar', '\\x60')`
#' @md
foo <- function() NULL
")[[1]]
expect_match(out1$get_value("title"), "\\code{bar}", fixed = TRUE)
})

test_that("NULL creates no text", {
expect_identical(
markdown("Description --`r NULL`--"),
Expand Down Expand Up @@ -89,7 +142,7 @@ test_that("interleaving fences and inline code", {
out1 <- roc_proc_text(rd_roclet(), "
#' Title
#'
#' @details Details `r x <- 10`
#' @details Details `r x <- 10; x`
#'
#' ```{r}
#' y <- x + 10
Expand Down
Loading

0 comments on commit 8279a15

Please sign in to comment.