diff --git a/DESCRIPTION b/DESCRIPTION index e8903d0428..34d1becba0 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Package: knitr Type: Package Title: A General-Purpose Package for Dynamic Report Generation in R -Version: 1.45.13 +Version: 1.45.14 Authors@R: c( person("Yihui", "Xie", role = c("aut", "cre"), email = "xie@yihui.name", comment = c(ORCID = "0000-0003-0645-5666")), person("Abhraneel", "Sarma", role = "ctb"), @@ -120,7 +120,7 @@ Imports: highr, methods, tools, - xfun (>= 0.42), + xfun (>= 0.42.5), yaml (>= 2.1.19) Suggests: bslib, @@ -200,3 +200,4 @@ Collate: 'utils-vignettes.R' 'zzz.R' RoxygenNote: 7.3.1 +Remotes: yihui/xfun diff --git a/R/hooks-md.R b/R/hooks-md.R index 665b157f9f..6e91d060ad 100644 --- a/R/hooks-md.R +++ b/R/hooks-md.R @@ -144,19 +144,6 @@ css_text_align = function(align) { if (align == 'default') '' else sprintf(' style="text-align: %s"', align) } -# turn a class string "a b" to c(".a", ".b") for Pandoc fenced code blocks -block_class = function(x) { - if (length(x) > 0) gsub('^[.]*', '.', unlist(strsplit(x, '\\s+'))) -} - -# concatenate block attributes (including classes) for Pandoc fenced code blocks -block_attr = function(attr, class = NULL, lang = NULL) { - x = c(block_class(class), attr) - if (length(x) == 0) return(lang) - x = c(sprintf('.%s', lang), x) - paste0('{', paste0(x, collapse = ' '), '}') -} - #' @rdname output_hooks #' @export #' @param strict Boolean; whether to use strict markdown or reST syntax. For markdown, if @@ -194,12 +181,12 @@ hooks_markdown = function(strict = FALSE, fence_char = '`') { hook.r = function(x, options) { lang = tolower(options$lang %n% eng2lang(options$engine)) if (!options$highlight) lang = 'text' - fenced_block(x, options$attr.source, options$class.source, lang, .char = fence_char) + fenced_block(x, options$attr.source, c(lang, options$class.source), .char = fence_char) } list( source = function(x, options) { x = hilight_source(x, 'markdown', options) - if (strict) hook.t(x) else hook.r(c(x, ''), options) + if (strict) hook.t(x) else hook.r(x, options) }, inline = function(x) { if (is_latex_output()) .inline.hook.tex(x) else { @@ -225,22 +212,26 @@ hooks_markdown = function(strict = FALSE, fence_char = '`') { ) } -pandoc_div = function(x, .attr = NULL, .class = NULL) { - if (is.null(.attr) && is.null(.class)) return(x) - fenced_block(c(x, ''), .attr, .class, .char = ':', .sep = ' ', .outer = '') +pandoc_div = function(x, attr = NULL, class = NULL) { + if (is.null(attr) && is.null(class)) return(x) + x = fenced_block(x, attr, class, .char = ':') + x = gsub('^\n\n|\n\n$', '', x) + gsub('^(:::+) *', '\\1 ', x) # add a space if necessary } -# add a fence around content (either fenced code block ``` or Div :::) -fenced_block = function(x, ..., .char = '`', .sep = '', .outer = '\n\n') { - x = one_string(c('', x)) - f = create_fence(x, .char) - paste0(.outer, paste(f, block_attr(...), sep = .sep), x, f, .outer) +# turn a class string "a b" to c(".a", ".b") for Pandoc fenced code blocks +block_class = function(x, attr = NULL) { + if (length(x)) x = unlist(strsplit(x, '\\s+')) + if (length(x) > 1 || length(attr)) gsub('^[.]*', '.', x) else x } -create_fence = function(x, char = '`') { - r = paste0('\n', char, '{3,}') - l = max(if (grepl(r, x)) attr(gregexpr(r, x)[[1]], 'match.length'), 3) - paste(rep(char, l), collapse = '') +# add a fence around content (either fenced code block ``` or Div :::) +fenced_block = function(x, attr = NULL, class = NULL, .char = '`') { + x = sub('\n$', '', x) + x = xfun::fenced_block(x, c(block_class(class, attr), attr), char = .char) + x = one_string(c('', x, '', '')) + # remove the space between ``` and { for backward-compatibility + sub('``` {', '```{', x, fixed = TRUE) } # convert some engine names to language names diff --git a/R/utils.R b/R/utils.R index 8dcdce4a75..ca99c73aeb 100644 --- a/R/utils.R +++ b/R/utils.R @@ -1052,8 +1052,9 @@ raw_output = function(x, markers = raw_markers, ...) { #' knitr::raw_latex('\\emph{some text}') raw_block = function(x, type = 'latex', ...) { if (!rmarkdown::pandoc_available('2.0.0')) warning('raw_block() requires Pandoc >= 2.0.0') - x = c(sprintf('\n```{=%s}', type), x, '```\n') - asis_output(one_string(x), ...) + x = fenced_block(x, attr = paste0('=', type)) + x = gsub('^\n|\n$', '', x) + asis_output(x, ...) } #' @rdname raw_block diff --git a/tests/testit/test-hooks-md.R b/tests/testit/test-hooks-md.R index 26e46f50f7..96052f9eb4 100644 --- a/tests/testit/test-hooks-md.R +++ b/tests/testit/test-hooks-md.R @@ -70,8 +70,8 @@ hook_chunk = knit_hooks$get("chunk") assert('Chunks are enclosed by fenced divs when needed.', { (hook_chunk('', list(class.chunk=NULL)) %==% '') (hook_chunk('', list(class.chunk="")) %==% '::: \n\n:::') - (hook_chunk('', list(class.chunk="foo")) %==% '::: {.foo}\n\n:::') - (hook_chunk(':::', list(class.chunk="foo")) %==% ':::: {.foo}\n:::\n::::') + (hook_chunk('', list(class.chunk="foo")) %==% '::: foo\n\n:::') + (hook_chunk(':::', list(class.chunk="foo")) %==% ':::: foo\n:::\n::::') }) knit_hooks$restore() diff --git a/tests/testit/test-utils.R b/tests/testit/test-utils.R index c99d6abf81..a26cfd8819 100644 --- a/tests/testit/test-utils.R +++ b/tests/testit/test-utils.R @@ -168,26 +168,18 @@ assert('restore_raw_output() restores raw output', { }) assert('raw_block() returns a raw attribute block for Pandoc', { - (raw_latex('\\emph{x}') == '\n```{=latex}\n\\emph{x}\n```\n') - (raw_html('foo') == '\n```{=html}\nfoo\n```\n') + (c(raw_latex('\\emph{x}')) %==% '\n```{=latex}\n\\emph{x}\n```\n') + (c(raw_html('foo')) %==% '\n```{=html}\nfoo\n```\n') }) assert('block_class() turns a character vector into Pandoc attributes for code block classes', { (block_class(NULL) %==% NULL) - (block_class('a') %==% '.a') + (block_class('a') %==% 'a') + (block_class('a', '.b') %==% '.a') (block_class('a b') %==% c('.a', '.b')) (block_class(c('a', 'b')) %==% c('.a', '.b')) }) -assert('block_attr(x) turns a character vector into Pandoc attributes', { - (block_attr(NULL) %==% NULL) - (block_attr(NULL, lang = 'r') %==% 'r') - (block_attr('.a') %==% '{.a}') - (block_attr('.a b="11"') %==% '{.a b="11"}') - (block_attr(c('.a', 'b="11"')) %==% '{.a b="11"}') -}) - - assert('when collapse is TRUE, class.* and attr.* become NULL except for class.source and attr.source', { keys = unlist(lapply( c('class.', 'attr.'), paste0, c('source', 'output', 'message', 'warning', 'error') diff --git a/vignettes/knitr-intro.Rmd b/vignettes/knitr-intro.Rmd index a2b89b1b20..7ed5dacaaf 100644 --- a/vignettes/knitr-intro.Rmd +++ b/vignettes/knitr-intro.Rmd @@ -45,6 +45,6 @@ abline(fit, col = 'red') ## References -```{r, echo=FALSE, results='asis'} +```{r, echo=FALSE, results='asis', warning=FALSE} print(citation('knitr'), style = 'html') ```