The default errors, warnings and messages in R Markdown are OK, but HTML output could look so much better in HTML, considering the templates use Twitter Bootstrap by default.

Let’s see what we can do instead with a few CSS classes!

stop('This should break!')

Error in eval(expr, envir, enclos): This should break!

warning('Watch out!')

Warning Watch out!

message('This is a message for you')

This is a message for you

All this was achieved with the following knitr settings. Potentially, LaTeX templates could be written for PDF and Beamer output as well.

knitr::knit_hooks$set(
   error = function(x, options) {
     paste('\n\n<div class="alert alert-danger">',
           gsub('##', '\n', gsub('^##\ Error', '**Error**', x)),
           '</div>', sep = '\n')
   },
   warning = function(x, options) {
     paste('\n\n<div class="alert alert-warning">',
           gsub('##', '\n', gsub('^##\ Warning:', '**Warning**', x)),
           '</div>', sep = '\n')
   },
   message = function(x, options) {
     paste('\n\n<div class="alert alert-info">',
           gsub('##', '\n', x),
           '</div>', sep = '\n')
   }
)

Notice the regular expressions, which replace the ## Error/## Warning with bold text. (It is necessary to get rid of the hashes to avoid the text being turned into a level two (<h2>) header, but you could also adjust the comment chunk option.) Is there a way of accessing the errors/warnings/messages before this stage for a more elegant solution?

What if the messages go onto multiple lines? The double gsub should take care of it.

stop('This\nshould\nbreak!\n')

Error in eval(expr, envir, enclos): This

should

break!

warning('Watch\nout!')

Warning Watch

out!

message('Let\nthat\nbe\na\nmessage')

Let

that

be

a

message

A better solution, perhaps, might be to read carefully what is written at https://yihui.name/knitr/hooks/ and consider the knitr::render_html() function. Doing things this way might also side-step the problem of writing conditional markup for the different output formats.

After a bit of research, the solution to the multi-line commenting problem appears to lie in the functions knitr:::wrap.error, wrap.message and wrap.warning. My idea is: just trick them into thinking that options$comment has been set to an empty string, and leave everything else the same.

wrap.error <- function(x, options) {
  options$comment <- ""
  knitr:::msg_wrap(paste(x$message, collapse = ""), "error", options)
}
wrap.message <- function(x, options) {
  options$comment <- ""
  knitr:::msg_wrap(paste(x$message, collapse = ""), "message", options)
}
wrap.warning <- function(x, options) {
  options$comment <- ""
  knitr:::msg_wrap(paste(x$message, collapse = ""), "warning", options)
}

But it is not easy, as far as I can tell, to replace non-exported S3 functions in packages, so short of forking the entire knitr package and rebuilding it with this file changed, the find-and-replace strategy will have to do.

LS0tDQp0aXRsZTogIkJvb3RzdHJhcCBhbGVydHMgaW4gUiBNYXJrZG93biINCmF1dGhvcjogIkRhdmlkIEEuIFNlbGJ5Ig0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50Og0KICAgIGtlZXBfbWQ6IHllcw0KICAgIGNvZGVfZG93bmxvYWQ6IHllcw0KLS0tDQoNClRoZSBkZWZhdWx0IGVycm9ycywgd2FybmluZ3MgYW5kIG1lc3NhZ2VzIGluIFIgTWFya2Rvd24gYXJlIE9LLCBidXQgSFRNTCBvdXRwdXQgY291bGQgbG9vayBzbyBtdWNoIGJldHRlciBpbiBIVE1MLCBjb25zaWRlcmluZyB0aGUgdGVtcGxhdGVzIHVzZSBUd2l0dGVyIEJvb3RzdHJhcCBieSBkZWZhdWx0Lg0KDQpMZXQncyBzZWUgd2hhdCB3ZSBjYW4gZG8gaW5zdGVhZCB3aXRoIGEgZmV3IENTUyBjbGFzc2VzIQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjprbml0X2hvb2tzJHNldCgNCiAgIGVycm9yID0gZnVuY3Rpb24oeCwgb3B0aW9ucykgew0KICAgICBwYXN0ZSgnXG5cbjxkaXYgY2xhc3M9ImFsZXJ0IGFsZXJ0LWRhbmdlciI+JywNCiAgICAgICAgICAgZ3N1YignIyMnLCAnXG4nLCBnc3ViKCdeIyNcIEVycm9yJywgJyoqRXJyb3IqKicsIHgpKSwNCiAgICAgICAgICAgJzwvZGl2PicsIHNlcCA9ICdcbicpDQogICB9LA0KICAgd2FybmluZyA9IGZ1bmN0aW9uKHgsIG9wdGlvbnMpIHsNCiAgICAgcGFzdGUoJ1xuXG48ZGl2IGNsYXNzPSJhbGVydCBhbGVydC13YXJuaW5nIj4nLA0KICAgICAgICAgICBnc3ViKCcjIycsICdcbicsIGdzdWIoJ14jI1wgV2FybmluZzonLCAnKipXYXJuaW5nKionLCB4KSksDQogICAgICAgICAgICc8L2Rpdj4nLCBzZXAgPSAnXG4nKQ0KICAgfSwNCiAgIG1lc3NhZ2UgPSBmdW5jdGlvbih4LCBvcHRpb25zKSB7DQogICAgIHBhc3RlKCdcblxuPGRpdiBjbGFzcz0iYWxlcnQgYWxlcnQtaW5mbyI+JywNCiAgICAgICAgICAgZ3N1YignIyMnLCAnXG4nLCB4KSwNCiAgICAgICAgICAgJzwvZGl2PicsIHNlcCA9ICdcbicpDQogICB9DQopDQpgYGANCg0KYGBge3Igb25lbGluZXJzLCBlcnJvciA9IFRSVUV9DQpzdG9wKCdUaGlzIHNob3VsZCBicmVhayEnKQ0Kd2FybmluZygnV2F0Y2ggb3V0IScpDQptZXNzYWdlKCdUaGlzIGlzIGEgbWVzc2FnZSBmb3IgeW91JykNCmBgYA0KDQpBbGwgdGhpcyB3YXMgYWNoaWV2ZWQgd2l0aCB0aGUgZm9sbG93aW5nIGtuaXRyIHNldHRpbmdzLiBQb3RlbnRpYWxseSwgTGFUZVggdGVtcGxhdGVzIGNvdWxkIGJlIHdyaXR0ZW4gZm9yIFBERiBhbmQgQmVhbWVyIG91dHB1dCBhcyB3ZWxsLg0KDQpgYGB7ciByZWYubGFiZWwgPSAnc2V0dXAnLCBlY2hvID0gVFJVRSwgZXZhbCA9IEZBTFNFfQ0KYGBgDQoNCk5vdGljZSB0aGUgcmVndWxhciBleHByZXNzaW9ucywgd2hpY2ggcmVwbGFjZSB0aGUgYCMjIEVycm9yYC9gIyMgV2FybmluZ2Agd2l0aCBib2xkIHRleHQuIChJdCBpcyBuZWNlc3NhcnkgdG8gZ2V0IHJpZCBvZiB0aGUgaGFzaGVzIHRvIGF2b2lkIHRoZSB0ZXh0IGJlaW5nIHR1cm5lZCBpbnRvIGEgbGV2ZWwgdHdvIChgPGgyPmApIGhlYWRlciwgYnV0IHlvdSBjb3VsZCBhbHNvIGFkanVzdCB0aGUgYGNvbW1lbnRgIGNodW5rIG9wdGlvbi4pDQpJcyB0aGVyZSBhIHdheSBvZiBhY2Nlc3NpbmcgdGhlIGVycm9ycy93YXJuaW5ncy9tZXNzYWdlcyBiZWZvcmUgdGhpcyBzdGFnZSBmb3IgYSBtb3JlIGVsZWdhbnQgc29sdXRpb24/DQoNCldoYXQgaWYgdGhlIG1lc3NhZ2VzIGdvIG9udG8gbXVsdGlwbGUgbGluZXM/IFRoZSBkb3VibGUgYGdzdWJgIHNob3VsZCB0YWtlIGNhcmUgb2YgaXQuDQoNCmBgYHtyIG11bHRpbGluZSwgZXJyb3IgPSBUUlVFfQ0Kc3RvcCgnVGhpc1xuc2hvdWxkXG5icmVhayFcbicpDQp3YXJuaW5nKCdXYXRjaFxub3V0IScpDQptZXNzYWdlKCdMZXRcbnRoYXRcbmJlXG5hXG5tZXNzYWdlJykNCmBgYA0KDQpBIGJldHRlciBzb2x1dGlvbiwgcGVyaGFwcywgbWlnaHQgYmUgdG8gcmVhZCBjYXJlZnVsbHkgd2hhdCBpcyB3cml0dGVuIGF0IGh0dHBzOi8veWlodWkubmFtZS9rbml0ci9ob29rcy8gYW5kIGNvbnNpZGVyIHRoZSBga25pdHI6OnJlbmRlcl9odG1sKClgIGZ1bmN0aW9uLg0KRG9pbmcgdGhpbmdzIHRoaXMgd2F5IG1pZ2h0IGFsc28gc2lkZS1zdGVwIHRoZSBwcm9ibGVtIG9mIHdyaXRpbmcgY29uZGl0aW9uYWwgbWFya3VwIGZvciB0aGUgZGlmZmVyZW50IG91dHB1dCBmb3JtYXRzLg0KDQpBZnRlciBhIGJpdCBvZiByZXNlYXJjaCwgdGhlIHNvbHV0aW9uIHRvIHRoZSBtdWx0aS1saW5lIGNvbW1lbnRpbmcgcHJvYmxlbSBhcHBlYXJzIHRvIGxpZSBpbiB0aGUgZnVuY3Rpb25zIGBrbml0cjo6OndyYXAuZXJyb3JgLCBgd3JhcC5tZXNzYWdlYCBhbmQgYHdyYXAud2FybmluZ2AuIE15IGlkZWEgaXM6IGp1c3QgdHJpY2sgdGhlbSBpbnRvIHRoaW5raW5nIHRoYXQgYG9wdGlvbnMkY29tbWVudGAgaGFzIGJlZW4gc2V0IHRvIGFuIGVtcHR5IHN0cmluZywgYW5kIGxlYXZlIGV2ZXJ5dGhpbmcgZWxzZSB0aGUgc2FtZS4NCg0KYGBge3IgcmVuZGVySFRNTCwgZXZhbCA9IEZBTFNFfQ0Kd3JhcC5lcnJvciA8LSBmdW5jdGlvbih4LCBvcHRpb25zKSB7DQogIG9wdGlvbnMkY29tbWVudCA8LSAiIg0KICBrbml0cjo6Om1zZ193cmFwKHBhc3RlKHgkbWVzc2FnZSwgY29sbGFwc2UgPSAiIiksICJlcnJvciIsIG9wdGlvbnMpDQp9DQp3cmFwLm1lc3NhZ2UgPC0gZnVuY3Rpb24oeCwgb3B0aW9ucykgew0KICBvcHRpb25zJGNvbW1lbnQgPC0gIiINCiAga25pdHI6Ojptc2dfd3JhcChwYXN0ZSh4JG1lc3NhZ2UsIGNvbGxhcHNlID0gIiIpLCAibWVzc2FnZSIsIG9wdGlvbnMpDQp9DQp3cmFwLndhcm5pbmcgPC0gZnVuY3Rpb24oeCwgb3B0aW9ucykgew0KICBvcHRpb25zJGNvbW1lbnQgPC0gIiINCiAga25pdHI6Ojptc2dfd3JhcChwYXN0ZSh4JG1lc3NhZ2UsIGNvbGxhcHNlID0gIiIpLCAid2FybmluZyIsIG9wdGlvbnMpDQp9DQpgYGANCg0KQnV0IGl0IGlzIG5vdCBlYXN5LCBhcyBmYXIgYXMgSSBjYW4gdGVsbCwgdG8gcmVwbGFjZSBub24tZXhwb3J0ZWQgUzMgZnVuY3Rpb25zIGluIHBhY2thZ2VzLCBzbyBzaG9ydCBvZiBmb3JraW5nIHRoZSBlbnRpcmUgYGtuaXRyYCBwYWNrYWdlIGFuZCByZWJ1aWxkaW5nIGl0IHdpdGggdGhpcyBmaWxlIGNoYW5nZWQsIHRoZSBmaW5kLWFuZC1yZXBsYWNlIHN0cmF0ZWd5IHdpbGwgaGF2ZSB0byBkby4NCg==