From 5b328620390bec97ca7775ba8e5135d6e4b8d041 Mon Sep 17 00:00:00 2001 From: Yihui Xie Date: Tue, 13 Dec 2016 13:40:05 -0600 Subject: [PATCH] tweak after #1335 @ijlyttle --- NEWS.md | 6 +++-- R/hooks-md.R | 50 +++++++++++++-------------------------- tests/testit/test-utils.R | 7 ++++++ 3 files changed, 27 insertions(+), 36 deletions(-) diff --git a/NEWS.md b/NEWS.md index 8531a278b0..40a254c72b 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,9 @@ # CHANGES IN knitr VERSION 1.16 (unreleased) +## NEW FEATURES + +- adds two new options, `class.output` and `class.source` to apply additional HTML classes to output and source chunks, which can be useful for R Markdown documents with HTML output formats since you can define custom CSS styles for these classes (thanks, @ijlyttle #1335 and @AmeliaMN #1333) + ## MAJOR CHANGES - a warning will be emitted when the chunk option `fig.show='hold'` and the output format is Word (https://github.com/rstudio/bookdown/issues/249); `fig.show='hold'` will be changed to `'asis'` at the same time @@ -10,8 +14,6 @@ ## NEW FEATURES -- adds two new options, `class.output` and `class.source` to help you manage the appearance of output and source chunks. This can be useful in the context of `rmarkdown::render()` using `rmarkdown::html_document()` (@ijlyttle) # This note is aspirational for the moment - it's here so I can start a pull request. - - added a new hook function `hook_pngquant()` that can call `pngquant` to optimize PNG images (thanks, @slowkow, #1320) ## BUG FIXES diff --git a/R/hooks-md.R b/R/hooks-md.R index 44c817d7eb..3165fd577c 100644 --- a/R/hooks-md.R +++ b/R/hooks-md.R @@ -93,6 +93,15 @@ css_text_align = function(align) { if (align == 'default') '' else sprintf(' style="text-align: %s"', align) } +# helper function to manage HTML classes; turn "a b" to "{.a .b}" for Pandoc +# fenced code blocks +block_class = function(x){ + if (length(x) == 0) return() + classes = unlist(strsplit(x, '\\s+')) + .classes = paste0('.', classes, collapse = ' ') + paste0('{', .classes, '}') +} + #' @rdname output_hooks #' @export #' @param strict whether to use strict markdown or reST syntax; for markdown: if @@ -107,31 +116,8 @@ render_markdown = function(strict = FALSE, fence_char = '`') { set_html_dev() opts_knit$set(out.format = 'markdown') fence = paste(rep(fence_char, 3), collapse = '') - # helper function to manage classes - block_class = function(x){ - - classes = unlist(strsplit(x, split = ' ')) - str_class = paste0('.', classes, collapse = ' ') - block_class = paste0('{', str_class, '}') - - block_class - } # four spaces lead to

-  hook.t = function(x, options) {
-    if (strict) {
-      paste('\n', indent_block(x), '', sep = '\n')
-    } else {
-      x = paste(c('', x), collapse = '\n')
-      r = paste0('\n', fence_char, '{3,}')
-      if (grepl(r, x)) {
-        l = attr(gregexpr(r, x)[[1]], 'match.length')
-        l = max(l)
-        if (l >= 4) fence = paste(rep(fence_char, l), collapse = '')
-      }
-      paste0('\n\n', fence, x, fence, '\n\n')
-    }
-  }
-  hook.t.output = function(x, options) {
+  hook.t = function(x, options, class = NULL) {
     # this code-block duplicated from hook.t()
     if (strict) {
       paste('\n', indent_block(x), '', sep = '\n')
@@ -143,21 +129,17 @@ render_markdown = function(strict = FALSE, fence_char = '`') {
         l = max(l)
         if (l >= 4) fence = paste(rep(fence_char, l), collapse = '')
       }
-
-      # the rest is 'new'
-      class_header = NULL
-      if (!is.null(options$class.output)){
-        class_header = block_class(options$class.output)
-      }
-
-      paste0('\n\n', fence, class_header, x, fence, '\n\n')
+      paste0('\n\n', fence, block_class(class), x, fence, '\n\n')
     }
   }
+  hook.o = function(x, options) {
+    hook.t(x, options, options$class.output)
+  }
   hook.r = function(x, options) {
     language = tolower(options$engine)
     if (language == 'node') language = 'javascript'
     if (!options$highlight) language = 'text'
-    if (!is.null(options$class.source)){
+    if (!is.null(options$class.source)) {
       language = block_class(c(language, options$class.source))
     }
     paste0('\n\n', fence, language, '\n', x, fence, '\n\n')
@@ -167,7 +149,7 @@ render_markdown = function(strict = FALSE, fence_char = '`') {
       x = hilight_source(x, 'markdown', options)
       (if (strict) hook.t else hook.r)(paste(c(x, ''), collapse = '\n'), options)
     },
-    output = hook.t.output, warning = hook.t, error = hook.t, message = hook.t,
+    output = hook.o, warning = hook.t, error = hook.t, message = hook.t,
     inline = function(x) {
       fmt = pandoc_to()
       fmt = if (length(fmt) == 1L) 'latex' else 'html'
diff --git a/tests/testit/test-utils.R b/tests/testit/test-utils.R
index c46f9ddbe7..92bce34c18 100644
--- a/tests/testit/test-utils.R
+++ b/tests/testit/test-utils.R
@@ -163,3 +163,10 @@ assert(
   restore_raw_output(pre$value, pre$chunks) %==%
     'hello\ncontent *protect* me!\nworld'
 )
+
+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 b') %==% '{.a .b}',
+  block_class(c('a', 'b')) %==% '{.a .b}'
+)