Nothing
#' Evaluate code and syntax highlight the results
#'
#' This function runs `code` and captures the output using
#' [evaluate::evaluate()]. It syntax higlights code with [highlight()], and
#' intermingles it with output.
#'
#' @param code Code to evaluate (as a string).
#' @param fig_save A function with arguments `plot` and `id` that is
#' responsible for saving `plot` to a file (using `id` to disambiguate
#' multiple plots in the same chunk). It should return a list with
#' components `path`, `width`, and `height`.
#' @param env Environment in which to evaluate code; if not supplied,
#' defaults to a child of the global environment.
#' @param output_handler Custom output handler for [evaluate::evaluate()].
#' @param highlight Optionally suppress highlighting. This is useful for tests.
#' @return An string containing HTML with a `dependencies` attribute
#' giving an additional htmltools dependencies required to render the HTML.
#' @inheritParams highlight
#' @export
#' @examples
#' cat(evaluate_and_highlight("1 + 2"))
#' cat(evaluate_and_highlight("x <- 1:10\nmean(x)"))
#'
#' # -----------------------------------------------------------------
#' # evaluate_and_highlight() powers pkgdown's documentation formatting so
#' # here I include a few examples to make sure everything looks good
#' # -----------------------------------------------------------------
#'
#' blue <- function(x) paste0("\033[34m", x, "\033[39m")
#' f <- function(x) {
#' cat("This is some output. My favourite colour is ", blue("blue"), ".\n", sep = "")
#' message("This is a message. My favourite fruit is ", blue("blueberries"))
#' warning("Now at stage ", blue("blue"), "!")
#' }
#' f()
#'
#' plot(1:10)
evaluate_and_highlight <- function(code,
fig_save,
classes = downlit::classes_pandoc(),
env = NULL,
output_handler = evaluate::new_output_handler(),
highlight = TRUE) {
env <- env %||% child_env(global_env())
expr <- evaluate::evaluate(code, child_env(env), new_device = TRUE,
output_handler = output_handler)
replay_html(expr,
fig_save = fig_save,
fig_id = unique_id(),
classes = classes,
highlight = highlight
)
}
test_evaluate <- function(code, ..., highlight = FALSE) {
fig_save <- function(plot, id) {
list(path = paste0(id, ".png"), width = 10, height = 10)
}
cat(evaluate_and_highlight(
code,
fig_save = fig_save,
env = caller_env(),
highlight = highlight,
...
))
}
replay_html <- function(x, ...) UseMethod("replay_html", x)
#' @export
replay_html.list <- function(x, ...) {
# keep only high level plots
x <- merge_low_plot(x)
html <- lapply(x, replay_html, ...)
dependencies <- unlist(lapply(html, attr, "dependencies"), recursive = FALSE)
structure(
paste0(html, collapse = ""),
dependencies = dependencies
)
}
#' @export
replay_html.NULL <- function(x, ...) ""
#' @export
replay_html.character <- function(x, ...) {
label_output(escape_html(x), "r-out")
}
#' @export
replay_html.source <- function(x, ..., classes, highlight = FALSE) {
if (highlight) {
html <- highlight(x$src, classes = classes)
}
if (!highlight || is.na(html)) {
html <- escape_html(x$src)
}
label_input(html, "r-in")
}
#' @export
replay_html.warning <- function(x, ...) {
message <- paste0(span("Warning: ", class = "warning"), escape_html(conditionMessage(x)))
label_output(message, "r-wrn")
}
#' @export
replay_html.message <- function(x, ...) {
message <- escape_html(paste0(gsub("\n$", "", conditionMessage(x))))
label_output(message, "r-msg")
}
#' @export
replay_html.error <- function(x, ...) {
if (is.null(x$call)) {
prefix <- "Error:"
} else {
prefix <- paste0("Error in ", escape_html(paste0(deparse(x$call), collapse = "")), ":")
}
message <- paste0(span(prefix, class = "error"), " ", escape_html(conditionMessage(x)))
label_output(message, "r-err")
}
#' @export
replay_html.recordedplot <- function(x, fig_save, fig_id, ...) {
fig <- fig_save(x, fig_id())
img <- paste0(
"<img ",
"src='", escape_html(fig$path), "' ",
"alt='' ",
"width='", fig$width, "' ",
"height='", fig$height, "' ",
"/>"
)
paste0(span(img, class = "r-plt img"), "\n")
}
# htmltools ---------------------------------------------------------------
# These will only be encountered if output_handler has a custom value
# callback that returns HTML.
#' @export
replay_html.html <- function(x, ...) {
rendered <- htmltools::renderTags(x)
structure(
rendered$html,
dependencies = rendered$dependencies,
class = "downlit_html"
)
}
#' @export
replay_html.shiny.tag <- replay_html.html
#' @export
replay_html.shiny.tag.function <- replay_html.html
#' @export
replay_html.shiny.tag.list <- replay_html.html
#' @export
replay_html.htmlwidget <- replay_html.html
# helpers -----------------------------------------------------------------
label_output <- function(x, class) {
prompt <- span("#>", class = "r-pr")
lines <- strsplit(x, "\n")[[1]]
lines <- fansi::sgr_to_html(lines)
lines <- paste0(prompt, " ", lines)
lines <- span(lines, class = paste(class, "co"))
paste0(lines, "\n", collapse = "")
}
label_input <- function(x, class) {
lines <- strsplit(x, "\n")[[1]]
lines <- span(lines, class = class)
paste0(lines, "\n", collapse = "")
}
span <- function(..., class = NULL) {
paste0(
"<span", if (!is.null(class)) paste0(" class='", class, "'"), ">",
...,
"</span>"
)
}
unique_id <- function() {
i <- 0
function() {
i <<- i + 1
i
}
}
# Knitr functions ------------------------------------------------------------
# The functions below come from package knitr (Yihui Xie) in file plot.R
# get MD5 digests of recorded plots so that merge_low_plot works
digest_plot = function(x, level = 1) {
if (inherits(x, "otherRecordedplot"))
return(x)
if (!is.list(x) || level >= 3) return(structure(digest::digest(x),
class = "plot_digest"))
lapply(x, digest_plot, level = level + 1)
}
is_plot_output = function(x) {
evaluate::is.recordedplot(x) || inherits(x, 'otherRecordedplot')
}
# merge low-level plotting changes
merge_low_plot = function(x, idx = vapply(x, is_plot_output, logical(1L))) {
idx = which(idx); n = length(idx); m = NULL # store indices that will be removed
if (n <= 1) return(x)
# digest of recorded plots
rp_dg <- lapply(x[idx], digest_plot)
i1 = idx[1]; i2 = idx[2] # compare plots sequentially
for (i in 1:(n - 1)) {
# remove the previous plot and move its index to the next plot
if (is_low_change(rp_dg[[i]], rp_dg[[i+1]])) m = c(m, i1)
i1 = idx[i + 1]
i2 = idx[i + 2]
}
if (is.null(m)) x else x[-m]
}
#' Compare two recorded plots
#'
#' @param p1,p2 Plot results
#'
#' @return Logical value indicating whether `p2` is a low-level update of `p1`.
#' @export
is_low_change = function(p1, p2) {
UseMethod("is_low_change")
}
#' @export
is_low_change.default = function(p1, p2) {
p1 = p1[[1]]; p2 = p2[[1]] # real plot info is in [[1]]
if ((n2 <- length(p2)) < (n1 <- length(p1))) return(FALSE) # length must increase
identical(p1[1:n1], p2[1:n1])
}
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.