diff --git a/.gitignore b/.gitignore index c8163b7..1fb2cfe 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,5 @@ docs /doc/ /Meta/ **/.quarto/ + +/.luarc.json diff --git a/DESCRIPTION b/DESCRIPTION index 2c8ada4..36ce49b 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: quarto Title: R Interface to 'Quarto' Markdown Publishing System -Version: 1.5.1.9000 +Version: 1.5.1.9002 Authors@R: c( person("JJ", "Allaire", , "jj@posit.co", role = "aut", comment = c(ORCID = "0000-0003-0174-9868")), diff --git a/NEWS.md b/NEWS.md index b6afd5b..1fd8e20 100644 --- a/NEWS.md +++ b/NEWS.md @@ -2,6 +2,8 @@ - `.libPaths()` from the calling R session will now be passed by default to all call to quarto as a subprocess. This should solve issue with **pkgdown** or when building vignettes. +- Curly braces in Quarto CLI error messages are now escaped to prevent them from being interpreted as `cli` formatting syntax (#293). + # quarto 1.5.1 - Make sure tests pass on CRAN checks even when Quarto is not installed by adding a gihub action to test when no quarto is available. Also fix tests that were diff --git a/R/quarto.R b/R/quarto.R index f1aff7d..c66f555 100644 --- a/R/quarto.R +++ b/R/quarto.R @@ -159,6 +159,11 @@ quarto_run <- function( custom_env <- c("current", custom_env) } + # Special case for tests to force hiding echo + if (is_testing() && getOption("quarto.tests.hide_echo", FALSE)) { + echo <- FALSE + } + res <- withCallingHandlers( processx::run( quarto_bin, @@ -346,7 +351,7 @@ wrap_quarto_error <- function(cnd, args, .call = rlang::caller_env()) { msg <- c( msg, " " = paste0(rep("-", nchar(msg)), collapse = ""), - quarto_error_msg + cli_escape(quarto_error_msg) ) } diff --git a/R/utils.R b/R/utils.R index f8e29c2..a5557ce 100644 --- a/R/utils.R +++ b/R/utils.R @@ -162,6 +162,12 @@ warn_or_error <- function(message, ..., .envir = parent.frame()) { } } +cli_escape <- function(x) { + x <- gsub("{", "{{", x, fixed = TRUE) + x <- gsub("}", "}}", x, fixed = TRUE) + x +} + # inline knitr:::merge_list() merge_list <- function(x, y) { x[names(y)] <- y @@ -213,3 +219,7 @@ is_empty_dir <- function(dir) { files <- list.files(dir, all.files = TRUE, no.. = TRUE) length(files) == 0 } + +is_testing <- function() { + identical(Sys.getenv("TESTTHAT"), "true") +} diff --git a/tests/testthat/resources/cli_error/error.lua b/tests/testthat/resources/cli_error/error.lua new file mode 100644 index 0000000..a0ac809 --- /dev/null +++ b/tests/testthat/resources/cli_error/error.lua @@ -0,0 +1,3 @@ +Pandoc = function(doc) + internal_error() +end \ No newline at end of file diff --git a/tests/testthat/resources/cli_error/pdf-error.qmd b/tests/testthat/resources/cli_error/pdf-error.qmd new file mode 100644 index 0000000..9d0c4dc --- /dev/null +++ b/tests/testthat/resources/cli_error/pdf-error.qmd @@ -0,0 +1,7 @@ +--- +format: latex +filters: + - error.lua +--- + +content \ No newline at end of file diff --git a/tests/testthat/test-render.R b/tests/testthat/test-render.R index b6e2574..d0c6ca3 100644 --- a/tests/testthat/test-render.R +++ b/tests/testthat/test-render.R @@ -124,3 +124,27 @@ test_that("quarto_render allows to pass output-file meta", { expect_true(file.exists("final_report.html")) expect_true(file.exists("final_report.docx")) }) + + +test_that("{ } are escaped correctly in abort message", { + skip_if_no_quarto() + proj <- test_path("resources", "cli_error") + withr::local_dir(proj) + keep_files <- list.files(".", all.files = TRUE) + withr::defer({ + unlink( + setdiff(list.files(".", all.files = TRUE), keep_files), + recursive = TRUE, + force = TRUE + ) + }) + withr::local_options(quarto.tests.hide_echo = TRUE) + expect_error( + quarto_render( + "pdf-error.qmd", + quiet = FALSE, + quarto_args = c("--log", "test.log") + ), + regexp = "Error returned by quarto CLI(.*)nil value \\(global 'internal_error'\\)" + ) +})