diff --git a/NEWS.md b/NEWS.md index baadbcf8a3..e4a732325c 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,7 @@ # pkgdown (development version) +* Anchors are displayed when they're the target of a link. +* `build_reference()` adds anchors to arguments making it possible to link directly to an argument, if desired. A subtle visual treatment makes it easy to see which argument is targeted (#2228). * `build_redirects()` is now exported to make it easier to document (#2500). * `build_reference()` now automatically renders any tables created by gt (#2326). * `build_articles()` now drops a section called "internal". This allows you to have articles that either aren't indexed at all or are included manually elsewhere in the navbar (#2205). diff --git a/R/rd-data.R b/R/rd-data.R index 55c3251247..56c7550af6 100644 --- a/R/rd-data.R +++ b/R/rd-data.R @@ -65,7 +65,7 @@ as_data.tag_section <- function(x, ...) { as_data.tag_arguments <- function(x, ...) { list( title = tr_("Arguments"), - contents = describe_contents(x, ...) + contents = describe_contents(x, ..., id_prefix = "arg-") ) } @@ -77,7 +77,7 @@ as_data.tag_value <- function(x, ...) { ) } -describe_contents <- function(x, ...) { +describe_contents <- function(x, ..., id_prefix = NULL) { # Drop pure whitespace nodes between items is_ws <- purrr::map_lgl(x, is_whitespace) @@ -90,7 +90,7 @@ describe_contents <- function(x, ...) { if (length(x) == 0) { NULL } else if (any(purrr::map_lgl(x, inherits, "tag_item"))) { - paste0("
\n", parse_descriptions(x, ...), "
") + paste0("
\n", parse_descriptions(x, ..., id_prefix = id_prefix), "
") } else { flatten_para(x, ...) } diff --git a/R/rd-html.R b/R/rd-html.R index b99557d14f..958e40c27e 100644 --- a/R/rd-html.R +++ b/R/rd-html.R @@ -366,16 +366,27 @@ parse_items <- function(rd, ...) { paste(collapse = "") } -parse_descriptions <- function(rd, ...) { +parse_descriptions <- function(rd, ..., id_prefix = NULL) { if (length(rd) == 0) { return(character()) } parse_item <- function(x) { if (inherits(x, "tag_item")) { + term <- flatten_text(x[[1]], ...) + def <- flatten_para(x[[2]], ...) + + if (!is.null(id_prefix)) { + id <- paste0(id_prefix, make_slug(term)) + id_attr <- paste0(" id='", id, "'") + anchor <- anchor_html(id) + } else { + id_attr <- "" + anchor <- "" + } paste0( - "
", flatten_text(x[[1]], ...), "
\n", - "
", flatten_para(x[[2]], ...), "
\n" + "", term, anchor, "\n", + "
", def , "
\n" ) } else { flatten_text(x, ...) diff --git a/R/tweak-tags.R b/R/tweak-tags.R index b61698ecd9..d6a962b392 100644 --- a/R/tweak-tags.R +++ b/R/tweak-tags.R @@ -22,9 +22,7 @@ tweak_anchors <- function(html) { xml2::xml_attr(headings, "id") <- new_id # Insert anchors - anchor <- paste0( - "" - ) + anchor <- anchor_html(new_id) for (i in seq_along(headings)) { heading <- headings[[i]] if (length(xml2::xml_contents(heading)) == 0) { @@ -37,6 +35,10 @@ tweak_anchors <- function(html) { invisible() } +anchor_html <- function(id) { + paste0("") +} + tweak_link_md <- function(html) { links <- xml2::xml_find_all(html, ".//a") if (length(links) == 0) diff --git a/inst/BS5/assets/pkgdown.scss b/inst/BS5/assets/pkgdown.scss index ccfe4e1b9b..7e14ebc11a 100644 --- a/inst/BS5/assets/pkgdown.scss +++ b/inst/BS5/assets/pkgdown.scss @@ -236,8 +236,9 @@ body { // spacing tweaks dd { - margin-left: 1.5rem; + padding-left: 1.5rem; } + summary { margin-bottom: 0.5rem; } @@ -252,7 +253,9 @@ details { // Section anchors a.anchor { display: none; - margin-left: 5px; + // style like a footnote + margin-left: 2px; + vertical-align: top; width: Min(0.9em, 20px); height: Min(0.9em, 20px); @@ -261,8 +264,20 @@ a.anchor { background-size: Min(0.9em, 20px) Min(0.9em, 20px); background-position: center center; } -h2, h3, h4, h5, h6 { - &:hover .anchor {display: inline-block;} +h2, h3, h4, h5, h6, dt { + &:hover .anchor, &:target .anchor {display: inline-block;} +} + +// Give targetted arguments some visual distinction +dt:target, dt:target + dd { + border-left: 0.25rem solid $primary; + margin-left: -0.75rem; +} +dt:target { + padding-left: 0.5rem; +} +dt:target + dd { + padding-left: 2rem; } // orcid badge diff --git a/tests/testthat/_snaps/rd-html.md b/tests/testthat/_snaps/rd-html.md index 02e009aa77..a97d8f7831 100644 --- a/tests/testthat/_snaps/rd-html.md +++ b/tests/testthat/_snaps/rd-html.md @@ -86,6 +86,18 @@

Paragraph 2

+ + +# can add ids to descriptions + +
+
abc
+

Contents 1

+ +
xyz
+

Contents 2

+ +
# nested item with whitespace parsed correctly diff --git a/tests/testthat/assets/reference/R/funs.R b/tests/testthat/assets/reference/R/funs.R index b3cd42500c..a1f3b814db 100644 --- a/tests/testthat/assets/reference/R/funs.R +++ b/tests/testthat/assets/reference/R/funs.R @@ -1,7 +1,10 @@ #' A #' @export #' @keywords foo -a <- function() {} +#' @param a a letter +#' @param b a a number +#' @param c a logical +a <- function(a, b, c) {} #' B #' @export diff --git a/tests/testthat/assets/reference/man/a.Rd b/tests/testthat/assets/reference/man/a.Rd index c71c4950dd..0a4c258426 100644 --- a/tests/testthat/assets/reference/man/a.Rd +++ b/tests/testthat/assets/reference/man/a.Rd @@ -4,7 +4,14 @@ \alias{a} \title{A} \usage{ -a() +a(a, b, c) +} +\arguments{ +\item{a}{a letter} + +\item{b}{a a number} + +\item{c}{a logical} } \description{ A diff --git a/tests/testthat/test-build-reference.R b/tests/testthat/test-build-reference.R index 273fc0cb76..da6b49626d 100644 --- a/tests/testthat/test-build-reference.R +++ b/tests/testthat/test-build-reference.R @@ -104,6 +104,15 @@ test_that("examples are reproducible by default, i.e. 'seed' is respected", { expect_snapshot(cat(examples)) }) +test_that("arguments get individual ids", { + pkg <- local_pkgdown_site(test_path("assets/reference")) + suppressMessages(build_reference(pkg, topics = "a")) + + html <- xml2::read_html(file.path(pkg$dst_path, "reference", "a.html")) + expect_equal(xpath_attr(html, "//dt", "id"), c("arg-a", "arg-b", "arg-c")) + +}) + test_that("title and page title escapes html", { pkg <- local_pkgdown_site(test_path("assets/reference")) suppressMessages(build_reference(pkg, topics = "g")) diff --git a/tests/testthat/test-rd-html.R b/tests/testthat/test-rd-html.R index 1d77e85450..8584b84ff7 100644 --- a/tests/testthat/test-rd-html.R +++ b/tests/testthat/test-rd-html.R @@ -355,6 +355,14 @@ test_that("\\describe items can contain multiple paragraphs", { expect_snapshot_output(cat(out, sep = "\n")) }) +test_that("can add ids to descriptions", { + out <- rd2html("\\describe{ + \\item{abc}{Contents 1} + \\item{xyz}{Contents 2} + }", id_prefix = "foo") + expect_snapshot_output(cat(out, sep = "\n")) +}) + test_that("\\describe items can contain multiple paragraphs", { out <- rd2html("\\describe{ \\item{Label}{