Skip to content

Commit

Permalink
Give user control over math rendering (r-lib#2599)
Browse files Browse the repository at this point in the history
  • Loading branch information
hadley authored and SebKrantz committed Jun 1, 2024
1 parent 939e6f0 commit 45bb253
Show file tree
Hide file tree
Showing 27 changed files with 209 additions and 87 deletions.
2 changes: 2 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# pkgdown (development version)

* New `template.math-rendering` allows you to control how math is rendered across your site. The default uses `mathml` which is low-dependency, but has the lowest fidelity. You can also use `mathjax`, the previous default, and `katex`, a faster alternative. (#1966).
* Mathjax now uses version 3.2.2.
* `build_sitemap()` no longer includes redirected pages (#2582).
* All external assets (JS, CSS, fonts) are now directly included in the site instead of fetched from external CDN (@salim-b, #2249)
* `build_reference_index()` now displays function lifecycle badges next to the function name (#2123). The badges are extracted only from the function description. You can now also use `has_lifecycle()` to select functions by their lifecycle status.
Expand Down
1 change: 1 addition & 0 deletions R/build-article.R
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ build_rmarkdown_format <- function(pkg,
theme = NULL,
template = template$path,
anchor_sections = FALSE,
math_method = config_math_rendering(pkg),
extra_dependencies = bs_theme_deps_suppress()
)
out$knitr$opts_chunk <- fig_opts_chunk(pkg$figures, out$knitr$opts_chunk)
Expand Down
6 changes: 3 additions & 3 deletions R/build-articles.R
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@ data_articles <- function(pkg = ".", is_index = FALSE, call = caller_env()) {
external <- config_pluck_external_articles(pkg, call = call)
articles <- rbind(internal, external)

articles$description <- lapply(articles$description, markdown_text_block)
articles$description <- lapply(articles$description, markdown_text_block, pkg = pkg)

# Hack data structure so we can use select_topics()
articles$alias <- as.list(articles$name)
Expand Down Expand Up @@ -375,9 +375,9 @@ data_articles_index_section <- function(section, index, articles, pkg, call = ca
error_call = call
)
title <- markdown_text_inline(
pkg,
section$title,
error_path = paste0("articles[", index, "].title"),
error_pkg = pkg,
error_call = call
)

Expand All @@ -400,7 +400,7 @@ data_articles_index_section <- function(section, index, articles, pkg, call = ca

list(
title = title,
desc = markdown_text_block(section$desc),
desc = markdown_text_block(pkg, section$desc),
class = section$class,
contents = purrr::transpose(contents)
)
Expand Down
4 changes: 2 additions & 2 deletions R/build-footer.R
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ data_footer <- function(pkg = ".", call = caller_env()) {
meta_structure <- config_pluck_list(pkg, "footer.structure", call = call)
structure <- modify_list(footnote_structure(), meta_structure)

left <- markdown_text_block(paste0(components[structure$left], collapse = " "))
right <- markdown_text_block(paste0(components[structure$right], collapse = " "))
left <- markdown_text_block(pkg, paste0(components[structure$left], collapse = " "))
right <- markdown_text_block(pkg, paste0(components[structure$right], collapse = " "))

list(left = left, right = right)
}
Expand Down
7 changes: 2 additions & 5 deletions R/build-home-authors.R
Original file line number Diff line number Diff line change
Expand Up @@ -114,11 +114,8 @@ author_name <- function(x, authors, pkg) {
author <- authors[[name]]

if (!is.null(author$html)) {
name <- markdown_text_inline(
author$html,
error_path = paste0("authors.", name, ".html"),
error_pkg = pkg
)
error_path <- paste0("authors.", name, ".html")
name <- markdown_text_inline(pkg, author$html, error_path = error_path)
}

if (is.null(author$href)) {
Expand Down
4 changes: 2 additions & 2 deletions R/build-home-index.R
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ build_home_index <- function(pkg = ".", quiet = TRUE) {
data$index <- linkify(pkg$desc$get_field("Description", ""))
} else {
local_options_link(pkg, depth = 0L)
data$index <- markdown_body(src_path)
data$index <- markdown_body(pkg, src_path)
}
render_page(pkg, "home", data, "index.html", quiet = quiet)

Expand Down Expand Up @@ -116,7 +116,7 @@ data_home_sidebar <- function(pkg = ".", call = caller_env()) {
needs_components <- setdiff(structure, names(default_components))
custom_yaml <- config_pluck_sidebar_components(pkg, needs_components, call = call)
custom_components <- purrr::map(custom_yaml, function(x) {
sidebar_section(x$title, markdown_text_block(x$text))
sidebar_section(x$title, markdown_text_block(pkg, x$text))
})
components <- modify_list(default_components, custom_components)

Expand Down
2 changes: 1 addition & 1 deletion R/build-home-md.R
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ package_mds <- function(path, in_dev = FALSE) {
render_md <- function(pkg, filename) {
cli::cli_inform("Reading {src_path(path_rel(filename, pkg$src_path))}")

body <- markdown_body(filename, strip_header = TRUE)
body <- markdown_body(pkg, filename, strip_header = TRUE)
path <- path_ext_set(path_file(filename), "html")

render_page(pkg, "title-body",
Expand Down
2 changes: 1 addition & 1 deletion R/build-news.R
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ build_news_multi <- function(pkg = ".") {
utils::globalVariables(".")

data_news <- function(pkg = list()) {
html <- markdown_body(path(pkg$src_path, "NEWS.md"))
html <- markdown_body(pkg, path(pkg$src_path, "NEWS.md"))
xml <- xml2::read_html(html)
downlit::downlit_html_node(xml)

Expand Down
12 changes: 6 additions & 6 deletions R/build-reference-index.R
Original file line number Diff line number Diff line change
Expand Up @@ -96,27 +96,27 @@ data_reference_index_rows <- function(section, index, pkg, call = caller_env())
if (has_name(section, "title")) {
rows[[1]] <- list(
title = markdown_text_inline(
pkg,
section$title,
error_path = paste0("reference[", index, "].title"),
error_call = call,
error_pkg = pkg
error_call = call
),
slug = make_slug(section$title),
desc = markdown_text_block(section$desc),
desc = markdown_text_block(pkg, section$desc),
is_internal = is_internal
)
}

if (has_name(section, "subtitle")) {
rows[[2]] <- list(
subtitle = markdown_text_inline(
pkg,
section$subtitle,
error_path = paste0("reference[", index, "].subtitle"),
error_call = call,
error_pkg = pkg
error_call = call
),
slug = make_slug(section$subtitle),
desc = markdown_text_block(section$desc),
desc = markdown_text_block(pkg, section$desc),
is_internal = is_internal
)
}
Expand Down
9 changes: 2 additions & 7 deletions R/config.R
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,7 @@ config_pluck_markdown_inline <- function(pkg,
call = caller_env()) {

text <- config_pluck_string(pkg, path, default, call = call)
markdown_text_inline(
text,
error_path = path,
error_pkg = pkg,
error_call = call
)
markdown_text_inline(pkg, text, error_path = path, error_call = call)
}

config_pluck_markdown_block <- function(pkg,
Expand All @@ -68,7 +63,7 @@ config_pluck_markdown_block <- function(pkg,
call = caller_env()) {

text <- config_pluck_string(pkg, path, default, call = call)
markdown_text_block(text)
markdown_text_block(pkg, text)
}

config_pluck_bool <- function(pkg,
Expand Down
36 changes: 28 additions & 8 deletions R/external-deps.R
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
external_dependencies <- function() {
list(
external_dependencies <- function(pkg, call = caller_env()) {
purrr::compact(list(
fontawesome::fa_html_dependency(),
cached_dependency(
name = "headroom",
Expand Down Expand Up @@ -53,21 +53,41 @@ external_dependencies <- function() {
)
)
),
math_dependency(pkg, call = call)
))
}

math_dependency <- function(pkg, call = caller_env()) {
math <- config_math_rendering(pkg)
if (math == "mathjax") {
cached_dependency(
name = "MathJax",
version = "2.7.5",
version = "3.2.2",
files = list(
list(
url = "https://cdnjs.cloudflare.com/ajax/libs/mathjax/3.2.2/es5/tex-chtml.min.js",
integrity = "sha512-T8xxpazDtODy3WOP/c6hvQI2O9UPdARlDWE0CvH1Cfqc0TXZF6GZcEKL7tIR8VbfS/7s/J6C+VOqrD6hIo++vQ=="
)
)
)
} else if (math == "katex") {
cached_dependency(
name = "KaTex",
version = "0.16.10",
files = list(
list(
url = "https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/MathJax.js",
integrity = "sha256-nvJJv9wWKEm88qvoQl9ekL2J+k/RWIsaSScxxlsrv8k="
url = "https://cdn.jsdelivr.net/npm/[email protected]/dist/katex.min.js",
integrity = "sha384-hIoBPJpTUs74ddyc4bFZSM1TVlQDA60VBbJS0oA934VSz82sBx1X7kSx2ATBDIyd"
),
list(
url = "https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/config/TeX-AMS-MML_HTMLorMML.js",
integrity = "sha256-84DKXVJXs0/F8OTMzX4UR909+jtl4G7SPypPavF+GfA="
url = "https://cdn.jsdelivr.net/npm/[email protected]/dist/katex.min.css",
integrity = "sha384-wcIxkf4k558AjM3Yz3BBFQUbk/zgIYC2R0QpeeYb+TwlBVMrlgLqwRjRtGZiK7ww"
)
)
)
)
} else {
NULL
}
}

cached_dependency <- function(name, version, files) {
Expand Down
61 changes: 40 additions & 21 deletions R/markdown.R
Original file line number Diff line number Diff line change
@@ -1,36 +1,33 @@
markdown_text <- function(text, ...) {
markdown_text <- function(pkg, text, ...) {
if (identical(text, NA_character_) || is.null(text)) {
return(NULL)
}

md_path <- withr::local_tempfile()
write_lines(text, md_path)
markdown_path_html(md_path, ...)
markdown_path_html(pkg, md_path, ...)
}

markdown_text_inline <- function(text,
markdown_text_inline <- function(pkg,
text,
error_path,
error_pkg,
error_call = caller_env()) {
html <- markdown_text(text)
html <- markdown_text(pkg, text)
if (is.null(html)) {
return()
}

children <- xml2::xml_children(xml2::xml_find_first(html, ".//body"))
if (length(children) > 1) {
config_abort(
error_pkg,
"{.field {error_path}} must be inline markdown.",
call = error_call
)
msg <- "{.field {error_path}} must be inline markdown."
config_abort(pkg, msg, call = error_call)
}

paste0(xml2::xml_contents(children), collapse = "")
}

markdown_text_block <- function(text, ...) {
html <- markdown_text(text, ...)
markdown_text_block <- function(pkg, text, ...) {
html <- markdown_text(pkg, text, ...)
if (is.null(html)) {
return()
}
Expand All @@ -39,8 +36,8 @@ markdown_text_block <- function(text, ...) {
paste0(as.character(children, options = character()), collapse = "")
}

markdown_body <- function(path, strip_header = FALSE) {
xml <- markdown_path_html(path, strip_header = strip_header)
markdown_body <- function(pkg, path, strip_header = FALSE) {
xml <- markdown_path_html(pkg, path, strip_header = strip_header)

if (is.null(xml)) {
return(NULL)
Expand All @@ -63,9 +60,9 @@ markdown_body <- function(path, strip_header = FALSE) {
)
}

markdown_path_html <- function(path, strip_header = FALSE) {
markdown_path_html <- function(pkg, path, strip_header = FALSE) {
html_path <- withr::local_tempfile()
convert_markdown_to_html(path, html_path)
convert_markdown_to_html(pkg, path, html_path)
xml <- xml2::read_html(html_path, encoding = "UTF-8")
if (!inherits(xml, "xml_node")) {
return(NULL)
Expand All @@ -81,7 +78,7 @@ markdown_path_html <- function(path, strip_header = FALSE) {
structure(xml, title = title)
}

markdown_to_html <- function(text, dedent = 4, bs_version = 3) {
markdown_to_html <- function(pkg, text, dedent = 4, bs_version = 3) {
if (dedent) {
text <- gsub(paste0("($|\n)", strrep(" ", dedent)), "\\1", text, perl = TRUE)
}
Expand All @@ -90,14 +87,14 @@ markdown_to_html <- function(text, dedent = 4, bs_version = 3) {
html_path <- withr::local_tempfile()

write_lines(text, md_path)
convert_markdown_to_html(md_path, html_path)
convert_markdown_to_html(pkg, md_path, html_path)

html <- xml2::read_html(html_path, encoding = "UTF-8")
tweak_page(html, "markdown", list(bs_version = bs_version))
html
}

convert_markdown_to_html <- function(in_path, out_path, ...) {
convert_markdown_to_html <- function(pkg, in_path, out_path, ...) {
if (rmarkdown::pandoc_available("2.0")) {
from <- "markdown+gfm_auto_identifiers-citations+emoji+autolink_bare_uris"
} else if (rmarkdown::pandoc_available("1.12.3")) {
Expand All @@ -109,7 +106,6 @@ convert_markdown_to_html <- function(in_path, out_path, ...) {
cli::cli_abort("Pandoc not available")
}
}

rmarkdown::pandoc_convert(
input = in_path,
output = out_path,
Expand All @@ -121,10 +117,33 @@ convert_markdown_to_html <- function(in_path, out_path, ...) {
"--indented-code-classes=R",
"--section-divs",
"--wrap=none",
"--mathjax",
paste0("--", config_math_rendering(pkg)),
...
))
)

invisible()
}

config_math_rendering <- function(pkg, call = caller_env()) {
if (is.null(pkg)) {
# Special case for tweak_highlight_other() where it's too annoying to
# pass down the package, and it doesn't matter much anyway.
return("mathml")
}

math <- config_pluck_string(
pkg,
"template.math-rendering",
default = "mathml",
call = call
)
allowed <- c("mathml", "mathjax", "katex")

if (!math %in% allowed) {
msg <- "{.field template.math-rendering} must be one of {allowed}, not {math}."
config_abort(pkg, msg, call = call)
}

math
}
1 change: 1 addition & 0 deletions R/render.R
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ data_template <- function(pkg = ".", depth = 0L) {
out$navbar <- data_navbar(pkg, depth = depth)
out$footer <- data_footer(pkg)
out$lightswitch <- uses_lightswitch(pkg)
out$uses_katex <- config_math_rendering(pkg) == "katex"

print_yaml(out)
}
Expand Down
2 changes: 1 addition & 1 deletion R/theme.R
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ build_bslib <- function(pkg = ".", call = caller_env()) {
cur_deps <- find_deps(pkg)
cur_digest <- purrr::map_chr(cur_deps, file_digest)

deps <- c(bslib::bs_theme_dependencies(bs_theme), external_dependencies())
deps <- c(bslib::bs_theme_dependencies(bs_theme), external_dependencies(pkg))
deps <- lapply(deps, htmltools::copyDependencyToDir, path(pkg$dst_path, "deps"))
deps <- lapply(deps, htmltools::makeDependencyRelative, pkg$dst_path)

Expand Down
2 changes: 1 addition & 1 deletion R/tweak-reference.R
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ tweak_highlight_other <- function(div) {
# many backticks to account for possible nested code blocks
# like a Markdown code block with code chunks inside
md <- paste0("``````", lang, "\n", xml2::xml_text(code), "\n``````")
html <- markdown_text(md)
html <- markdown_text(NULL, md)

xml_replace_contents(code, xml2::xml_find_first(html, "body/div/pre/code"))
TRUE
Expand Down
14 changes: 14 additions & 0 deletions inst/BS5/assets/katex-auto.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// https://github.com/jgm/pandoc/blob/29fa97ab96b8e2d62d48326e1b949a71dc41f47a/src/Text/Pandoc/Writers/HTML.hs#L332-L345
document.addEventListener("DOMContentLoaded", function () {
var mathElements = document.getElementsByClassName("math");
var macros = [];
for (var i = 0; i < mathElements.length; i++) {
var texText = mathElements[i].firstChild;
if (mathElements[i].tagName == "SPAN") {
katex.render(texText.data, mathElements[i], {
displayMode: mathElements[i].classList.contains("display"),
throwOnError: false,
macros: macros,
fleqn: false
});
}}});
Loading

0 comments on commit 45bb253

Please sign in to comment.