143 lines
4.3 KiB
Plaintext
143 lines
4.3 KiB
Plaintext
---
|
||
title: "ANSI CSI SGR Sequences in Rmarkdown"
|
||
author: "Brodie Gaslam"
|
||
output:
|
||
rmarkdown::html_vignette:
|
||
css: styles.css
|
||
mathjax: local
|
||
vignette: >
|
||
%\VignetteIndexEntry{ANSI CSI SGR Sequences in Rmarkdown}
|
||
%\VignetteEngine{knitr::rmarkdown}
|
||
\usepackage[utf8]{inputenc}
|
||
---
|
||
|
||
```{r echo=FALSE}
|
||
library(fansi)
|
||
knitr::knit_hooks$set(document=function(x, options) gsub("\033", "\uFFFD", x))
|
||
```
|
||
|
||
### Browsers Do Not Interpret ANSI CSI SGR Sequences
|
||
|
||
Over the past few years color has been gaining traction in the R terminal,
|
||
particularly since Gábor Csárdi's [crayon](https://github.com/r-lib/crayon)
|
||
made it easy to format text with [ANSI CSI SGR
|
||
sequences](https://en.wikipedia.org/wiki/ANSI_escape_code). At the
|
||
same time the advent of JJ Alaire and Yihui Xie `rmarkdown` and `knitr`
|
||
packages, along with John MacFarlane `pandoc`, made it easy to automatically
|
||
incorporate R code and output in HTML documents.
|
||
|
||
Unfortunately ANSI CSI SGR sequences are not recognized by web browsers and end
|
||
up rendering weirdly<a href=#f1><sup>1</sub></a>:
|
||
|
||
```{r}
|
||
sgr.string <- c(
|
||
"\033[43;34mday > night\033[0m",
|
||
"\033[44;33mdawn < dusk\033[0m"
|
||
)
|
||
writeLines(sgr.string)
|
||
```
|
||
|
||
### Automatically Convert ANSI CSI SGR to HTML
|
||
|
||
`fansi` provides the `to_html` function which converts the ANSI CSI SGR
|
||
sequences and OSC hyperlinks into HTML markup. When we combine it with
|
||
`knitr::knit_hooks` we can modify the rendering of the `rmarkdown` document such
|
||
that ANSI CSI SGR encoding is shown in the equivalent HTML.
|
||
|
||
`fansi::set_knit_hooks` is a convenience function that does just this. You
|
||
should call it in an `rmarkdown` document with the:
|
||
|
||
* Chunk option `results` set to "asis".
|
||
* Chunk option `comments` set to "" (empty string).
|
||
* The `knitr::knit_hooks` object as an argument.
|
||
|
||
The corresponding `rmarkdown` hunk should look as follows:
|
||
|
||
````
|
||
```{r, comment="", results="asis"}`r ''`
|
||
old.hooks <- fansi::set_knit_hooks(knitr::knit_hooks)
|
||
```
|
||
````
|
||
|
||
```{r comment="", results="asis", echo=FALSE}
|
||
old.hooks <- fansi::set_knit_hooks(knitr::knit_hooks)
|
||
```
|
||
We run this function for its side effects, which cause the output to be
|
||
displayed as intended:
|
||
|
||
```{r}
|
||
writeLines(sgr.string)
|
||
```
|
||
|
||
If you are seeing extra line breaks in your output you may need to use:
|
||
|
||
````
|
||
```{r, comment="", results="asis"}`r ''`
|
||
old.hooks <- fansi::set_knit_hooks(knitr::knit_hooks, split.nl=TRUE)
|
||
```
|
||
````
|
||
|
||
If you use `crayon` to generate your ANSI CSI SGR style strings you may need to
|
||
set `options(crayon.enabled=TRUE)`, as in some cases `crayon` suppresses the SGR
|
||
markup if it thinks it is not outputting to a terminal.
|
||
|
||
We can also set hooks for the other types of outputs, and add some additional
|
||
CSS styles.
|
||
|
||
````
|
||
```{r, comment="", results="asis"}`r ''`
|
||
styles <- c(
|
||
getOption("fansi.style", dflt_css()), # default style
|
||
"PRE.fansi CODE {background-color: transparent;}",
|
||
"PRE.fansi-error {background-color: #DDAAAA;}",
|
||
"PRE.fansi-warning {background-color: #DDDDAA;}",
|
||
"PRE.fansi-message {background-color: #AAAADD;}"
|
||
)
|
||
old.hooks <- c(
|
||
old.hooks,
|
||
fansi::set_knit_hooks(
|
||
knitr::knit_hooks,
|
||
which=c("warning", "error", "message"),
|
||
style=styles
|
||
) )
|
||
```
|
||
````
|
||
```{r comment="", results="asis", echo=FALSE}
|
||
styles <- c(
|
||
getOption("fansi.style", dflt_css()), # default style
|
||
"PRE.fansi CODE {background-color: transparent;}",
|
||
"PRE.fansi-error {background-color: #DDAAAA;}",
|
||
"PRE.fansi-warning {background-color: #DDDDAA;}",
|
||
"PRE.fansi-message {background-color: #AAAADD;}"
|
||
)
|
||
old.hooks <- c(
|
||
old.hooks,
|
||
fansi::set_knit_hooks(
|
||
knitr::knit_hooks,
|
||
which=c("warning", "error", "message"),
|
||
style=styles
|
||
) )
|
||
```
|
||
```{r error=TRUE}
|
||
message(paste0(sgr.string, collapse="\n"))
|
||
warning(paste0(c("", sgr.string), collapse="\n"))
|
||
stop(paste0(c("", sgr.string), collapse="\n"))
|
||
```
|
||
|
||
You can restore the old hooks at any time in your document with:
|
||
|
||
```{r}
|
||
do.call(knitr::knit_hooks$set, old.hooks)
|
||
writeLines(sgr.string)
|
||
```
|
||
|
||
See `?fansi::set_knit_hooks` for details.
|
||
|
||
----
|
||
<a name='f1'></a><sup>1</sup>For illustrative purposes we output raw ANSI
|
||
CSI SGR sequences in this document. However, because the ESC control character
|
||
causes problems with some HTML rendering services we replace it with the <20>
|
||
symbol. Depending on the browser and process it would normally not be
|
||
visible at all, or substituted with some other symbol.
|
||
|