From ab00d8e21008614a07c5db4a774bc1b1c6121a8a Mon Sep 17 00:00:00 2001 From: Luis Verde Arregoitia Date: Thu, 17 Sep 2026 14:44:23 -0600 Subject: [PATCH] Don't auto-link fs_path values inside link targets (#683) Interpolating an fs_path into the target of a markdown-style link (e.g. {.run [text]({path})}) applied the class-map file style, whose embedded file:// hyperlink and quotes corrupted the outer link URL. Skip implicit class-map styling when substituting inside run/href/ help/topic/vignette link containers. --- NEWS.md | 4 ++++ R/inline.R | 8 +++++++- tests/testthat/test-links.R | 28 ++++++++++++++++++++++++++++ 3 files changed, 39 insertions(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index bb4f73768..e681f83d6 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,9 @@ # cli (development version) +* Interpolating an `fs_path` (or any object mapped to the `file` style via + the theme's `class-map`) into the target of a link, e.g. + `{.run [text]({path})}`, no longer corrupts the link's URL (#683). + * `keypress()` improvements: - `timeout` argument to wait at most a given number of seconds for a key press. diff --git a/R/inline.R b/R/inline.R index 91b503023..71377cdef 100644 --- a/R/inline.R +++ b/R/inline.R @@ -191,10 +191,16 @@ inline_transformer <- function(code, envir) { id <- NULL } + # Don't apply implicit `class-map` styling (e.g. `fs_path` -> `file`) + # inside URL-building link containers + in_link <- any(vlapply(app$doc, function(x) { + any(x$class %in% c("run", "href", "help", "topic", "vignette")) + })) + rcls <- class(val) stls <- app$get_current_style()$`class-map` cls <- na.omit(match(rcls, names(stls)))[1] - if (!is.na(cls)) { + if (!is.na(cls) && !in_link) { class <- c(class, stls[[cls]]) } diff --git a/tests/testthat/test-links.R b/tests/testthat/test-links.R index 60f16f8de..420238d22 100644 --- a/tests/testthat/test-links.R +++ b/tests/testthat/test-links.R @@ -236,6 +236,34 @@ test_that_cli(configs = "plain", links = "all", ".run with custom format", { }) }) +test_that("fs_path is not auto-linked inside a link target (#683)", { + withr::local_options( + cli.hyperlink = TRUE, + cli.hyperlink_run = TRUE + ) + + # fs_path values must not be implicitly styled as files when they are + # substituted into a link target, as the embedded file link would + # corrupt the target URL. + path <- structure("~/foo.R", class = c("fs_path", "character")) + chr <- "~/foo.R" + + # the target should be identical to the plain-character case + expect_equal( + format_inline("{.run ['hi mom']({path})}"), + format_inline("{.run ['hi mom']({chr})}") + ) + expect_equal( + format_inline("{.run ['file.R'](pkgdown::preview_page('{path}'))}"), + format_inline("{.run ['file.R'](pkgdown::preview_page('{chr}'))}") + ) + + # and the target must not contain a nested (file) hyperlink + out <- format_inline("{.run ['hi mom']({path})}") + expect_match(out, "x-r-run:~/foo.R", fixed = TRUE) + expect_false(grepl("file://", out, fixed = TRUE)) +}) + # -- {.topic} ------------------------------------------------------------- test_that_cli(configs = "plain", links = c("all", "none"), "{.topic}", {