Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Syntax Highlighting Themes for HTML #179

Merged
merged 34 commits into from Apr 12, 2012
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
3fe593e
make highlighting themes work with html too
ramnathv Mar 25, 2012
e1281fd
make render_html respect knit_theme highlight settings
ramnathv Mar 25, 2012
55378ef
add default.css to support default theme
ramnathv Mar 25, 2012
15cbc81
add .knitr.css to define knitr classes for highlighting in html
ramnathv Mar 25, 2012
df1fbd3
minor changes to .knitr.css
ramnathv Mar 25, 2012
8c8ce62
add TODOs to refactor .header.hi.html and .header.hi.tex
ramnathv Mar 25, 2012
09505a1
add background to default css
ramnathv Mar 25, 2012
8ba2e9f
use header_to_theme_xxx to generate default highlight headers
ramnathv Mar 25, 2012
131ed42
move latex header to knitr.sty and read .header.framed from there
ramnathv Mar 25, 2012
8757069
move .header.hi.xxx to themes.R
ramnathv Mar 25, 2012
dc49e76
move definition of .header.hi.tex inside render_latex
ramnathv Mar 26, 2012
4502202
move .header.hi.html inside render_html where it is used
ramnathv Mar 26, 2012
e6171eb
move .header.framed inside render_latex so that it is not run before …
ramnathv Mar 26, 2012
9114783
move definitions in .header.framed to style file knitr.sty
ramnathv Mar 26, 2012
68cf44b
Merge remote branch 'upstream/master'
ramnathv Mar 26, 2012
c06ce2b
add .Rproj.user to .*ignore files
ramnathv Mar 27, 2012
797ef9e
add misc folder and knitr.Rroj to gitignore
ramnathv Mar 27, 2012
bcc05cf
add graphicx package
ramnathv Mar 28, 2012
21cf811
make code for get_theme() more explicit
ramnathv Mar 30, 2012
96d14ef
add experimental hooks for reStructuredText
ramnathv Apr 1, 2012
7742977
add myhooks.R to collate
ramnathv Apr 1, 2012
d31f0bc
export render_rst and hook_plot_rst
ramnathv Apr 1, 2012
8958918
Merge remote branch 'upstream/master'
ramnathv Apr 9, 2012
46481d3
modify insert_header to allow writing header to external file
ramnathv Apr 9, 2012
a57e1dd
remove graphicx from list of packages loaded
ramnathv Apr 9, 2012
0d8f95e
remove <style..> </style> from header to allow writing to external file
ramnathv Apr 9, 2012
f3324e3
adjust spacing in css file
ramnathv Apr 9, 2012
af4f62e
remove myhooks from DESCRIPTION
ramnathv Apr 9, 2012
efb1a11
remove hooks for rst to do more extensive tests
ramnathv Apr 9, 2012
6780ba6
remove rst hooks to do more extensive tests
ramnathv Apr 9, 2012
a6e95c3
add self.contained = TRUE as default in opts_knit
ramnathv Apr 11, 2012
289619e
Merge remote branch 'upstream/master'
ramnathv Apr 11, 2012
67ae3a0
Merge remote branch 'upstream/master'
ramnathv Apr 11, 2012
56ab698
allow ```{r} fixes #195
ramnathv Apr 11, 2012
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions .Rbuildignore
Expand Up @@ -12,3 +12,4 @@ knitr-minimal.html
knitr-minimal.md
knitr-latex.tex
png$
^\.Rproj\.user$
4 changes: 3 additions & 1 deletion .gitignore
Expand Up @@ -8,4 +8,6 @@ inst/examples/figure
inst/examples/ex-out*
knitr-minimal.html
knitr-latex.tex

.Rproj.user
misc
knitr.Rproj
4 changes: 2 additions & 2 deletions R/defaults.R
Expand Up @@ -108,7 +108,7 @@ all_patterns =
header.begin = '\n*\\s*<head>',
ref.label = '^## @knitr (.*)$'),

`md` = list(chunk.begin = '^``` \\{r(.*)\\}\\s*$',
`md` = list(chunk.begin = '^```\\s*\\{r(.*)\\}\\s*$',
chunk.end = '^```\\s*$',
chunk.end.is.terminator = TRUE,
ref.chunk = '^\\s*<<(.*)>>\\s*$',
Expand All @@ -135,7 +135,7 @@ opts_knit =
all.patterns = all_patterns, tangle = FALSE,
child = FALSE, parent = FALSE,
cache.extra = NULL, aliases = NULL,

self.contained = TRUE,
header = c(highlight = '', tikz = '', framed = ''))
)
## header should not be set by hand unless you know what you are doing
Expand Down
140 changes: 63 additions & 77 deletions R/header.R
@@ -1,27 +1,66 @@
## x is the output of processed document
insert_header = function(x) {
if (is.null(b <- knit_patterns$get('header.begin'))) return(x)
h = opts_knit$get('header')
i = which(str_detect(x, b))
if (length(i) == 1L) {
fmt = opts_knit$get('out.format')
if (fmt %in% c('markdown', 'gfm', 'jekyll')) return(x)
if (identical('latex', fmt))
h = c('\\usepackage{graphicx, color}', h)
if (identical('html', fmt))
h = h['highlight']
h = h[nzchar(h)]; if (length(h) == 0) h = ''
loc = str_locate(x[i], b)
str_sub(x[i], loc[, 1], loc[, 2]) =
str_c(str_sub(x[i], loc[, 1], loc[, 2]), '\n', str_c(h, collapse = '\n'))
} else if (length(i) == 0L) {
if (parent_mode()) {
h = c('\\usepackage{graphicx, color}', h)
x = c(getOption('tikzDocumentDeclaration'), str_c(h, collapse = '\n'),
.knitEnv$tikzPackages, '\\begin{document}', x, '\\end{document}')
}
}
x
## doc is the output of processed document
insert_header <- function(doc){
fmt = opts_knit$get('out.format')
switch(fmt,
html = insert_header_html(doc),
latex = insert_header_latex(doc),
doc
)
}

## Makes latex header with macros required for highlighting, tikz and framed
make_header_latex <- function(){
h <- "\\usepackage{graphicx, color}"
h <- paste(c(h, opts_knit$get('header')), collapse = "\n")
if (opts_knit$get('self.contained')){
return(h)
} else {
writeLines(h, 'knitr.sty')
return('\\usepackage{knitr}')
}
}

insert_header_latex <- function(doc){
# TODO: is this really required since b will never be NULL for latex.
if (is.null(b <- knit_patterns$get('header.begin'))){
return(doc)
}
h <- make_header_latex()
i <- which(str_detect(doc, b))
l <- str_locate(doc[i], b)
if (length(i) == 1L){
tmp <- str_sub(doc[i], l[, 1], l[, 2])
str_sub(doc[i], l[,1], l[,2]) <- str_c(tmp, "\n", h)
} else if (length(i) == 0L) {
doc <- str_c(getOption('tikzDocumentDeclaration'), h, .knitEnv$packages,
"\\begin{document}", doc, "\\end{document}")
}
return(doc)
}

make_header_html <- function(){
h <- opts_knit$get('header')[['highlight']]
if (opts_knit$get('self.contained')){
h <- str_c('<style type="text/css">', h, '</style>', collapse = "\n")
return(h)
} else {
writeLines(h, 'knitr.css')
return('<link rel="stylesheet" href="knitr.css" type="text/css" />')
}
}

insert_header_html <- function(doc){
if (is.null(b <- knit_patterns$get('header.begin'))){
return(doc)
}
h <- make_header_html()
i <- which(str_detect(doc, b))
l <- str_locate(doc[i], b)
if (length(i) == 1L){
tmp <- str_sub(doc[i], l[, 1], l[, 2])
str_sub(doc[i], l[,1], l[,2]) <- str_c(tmp, "\n", h)
}
return(doc)
}

#' Set the header information
Expand Down Expand Up @@ -61,56 +100,3 @@ set_header = function(...) {
opts_knit$set(header = h)
}

## many thanks to Donald Arseneau
.header.framed = '\\usepackage{framed}
\\makeatletter
\\newenvironment{kframe}{%
\\def\\FrameCommand##1{\\hskip\\@totalleftmargin \\hskip-\\fboxsep
\\colorbox{shadecolor}{##1}\\hskip-\\fboxsep
% There is no \\@totalrightmargin, so:
\\hskip-\\linewidth \\hskip-\\@totalleftmargin \\hskip\\columnwidth}%
\\MakeFramed {\\advance\\hsize-\\width
\\@totalleftmargin\\z@ \\linewidth\\hsize
\\@setminipage}}%
{\\par\\unskip\\endMakeFramed}
\\makeatother

\\definecolor{shadecolor}{rgb}{.97, .97, .97}
\\newenvironment{knitrout}{}{} % an empty environment to be redefined in TeX

\\newcommand{\\SweaveOpts}[1]{} % do not interfere with LaTeX
\\newcommand{\\SweaveInput}[1]{} % because they are not real TeX commands
\\newcommand{\\Sexpr}[1]{} % will only be parsed by R
'

## LaTeX styles for highlight
.header.hi.tex =
str_c(c("\\newcommand{\\hlnumber}[1]{\\textcolor[rgb]{0,0,0}{#1}}%",
"\\newcommand{\\hlfunctioncall}[1]{\\textcolor[rgb]{.5,0,.33}{\\textbf{#1}}}%",
"\\newcommand{\\hlstring}[1]{\\textcolor[rgb]{.6,.6,1}{#1}}%",
"\\newcommand{\\hlkeyword}[1]{\\textbf{#1}}%",
"\\newcommand{\\hlargument}[1]{\\textcolor[rgb]{.69,.25,.02}{#1}}%",
"\\newcommand{\\hlcomment}[1]{\\textcolor[rgb]{.18,.6,.34}{#1}}%",
"\\newcommand{\\hlroxygencomment}[1]{\\textcolor[rgb]{.44,.48,.7}{#1}}%",
"\\newcommand{\\hlformalargs}[1]{\\hlargument{#1}}%",
"\\newcommand{\\hleqformalargs}[1]{\\hlargument{#1}}%",
"\\newcommand{\\hlassignement}[1]{\\textbf{#1}}%",
"\\newcommand{\\hlpackage}[1]{\\textcolor[rgb]{.59,.71,.145}{#1}}%",
"\\newcommand{\\hlslot}[1]{\\textit{#1}}%",
"\\newcommand{\\hlsymbol}[1]{#1}%",
"\\newcommand{\\hlprompt}[1]{\\textcolor[rgb]{.5,.5,.5}{#1}}%",
boxes_latex(), "\\definecolor{fgcolor}{rgb}{0,0,0}"), collapse = '\n')

.header.hi.html =
str_c(c('<style type="text/css">', '.knitr {
background-color: #F7F7F7;
}', '.error {
font-weight: bold;
color: #FF0000;
}', '.warning {
font-weight: bold;
}', '.message {
font-style: italic;
}', '.source, .output, .warning, .error, .message {
padding: 0.5em 1em;
}', styler('default'), '</style>'), collapse = '\n')
21 changes: 18 additions & 3 deletions R/hooks.R
Expand Up @@ -282,8 +282,17 @@ render_latex = function() {
if (child_mode()) return()
test_latex_pkg('framed', system.file('misc', 'framed.sty', package = 'knitr'))
h = opts_knit$get('header')
if (!nzchar(h['framed'])) set_header(framed = .header.framed)
if (!nzchar(h['highlight'])) set_header(highlight = .header.hi.tex)
if (!nzchar(h['framed'])) {
.knit.sty <- system.file('misc', 'knitr.sty', package = 'knitr')
.header.framed <- paste(readLines(.knit.sty), collapse = "\n")
set_header(framed = .header.framed)
}
if (!nzchar(h['highlight'])) {
# header for Latex Syntax Highlighting
.header.hi.tex = paste(theme_to_header_latex('edit-eclipse')$highlight,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is edit-eclipse the default theme for LaTeX output?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the default that you had used previously. I remember reading a note of yours sometime, where you indicated that you found the colors of the default highlight theme too flashy for pdfs.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I think I mentioned it at some time, but I'm not actually using edit-eclipse. The default theme has a light gray background, and the background of edit-eclipse is white.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In that event, I will create a default.latex.css theme which has the gray background.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually I do not see much gain of moving the two objects .header.framed and .header.hi.tex to two files respectively; I mean they are simply string constants and it is probably not worth it to read and write through extra files. At the end of the day, they will be concatenated in insert_header(), and that will be the time to write the header into a separate file if opts_knit$get('self.contained') == TRUE.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First, to some extent it is a style issue, since I don't prefer latex and other markups contaminating my R code. Second, these are just defaults. So pulling them inside default files helps keep the code clean without cluttering them up with a lot of if.. else statements. For instance, if we implement highlight.style as an option, you can set it to default and the headers would automatically correspond to them. Just my two cents :-)

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, .header.hi.tex can go to a file like default.css. For .header.framed, I'd rather leave it as is at the moment.

collapse = '\n')
set_header(highlight = .header.hi.tex)
}
knit_hooks$restore()
knit_hooks$set(source = function(x, options) {
if (options$highlight) {
Expand Down Expand Up @@ -339,7 +348,13 @@ render_html = function() {
force(name)
function (x, options) sprintf('<div class="%s">%s</div>', name, x)
}
set_header(highlight = .header.hi.html)
h = opts_knit$get('header')
if (!nzchar(h['highlight'])) {
# CSS for html syntax highlighting
.header.hi.html = paste(theme_to_header_html('default')$highlight,
collapse = '\n')
set_header(highlight = .header.hi.html)
}
z = list()
for (i in c('source', 'output', 'warning', 'message', 'error'))
z[[i]] = html.hook(i)
Expand Down
50 changes: 43 additions & 7 deletions R/themes.R
Expand Up @@ -2,16 +2,20 @@
#' @noRd
#' @author Ramnath Vaidyanathan
set_theme = function(theme) {
fmt = opts_knit$get('out.format')
header = if (is.list(theme)) theme else theme_to_header(theme)
highlight = paste(c(header$highlight, boxes_latex()), collapse = "\n")
opts_chunk$set(background = header$background)
highlight = paste(header$highlight, collapse = "\n")
if(fmt == 'latex') {
opts_chunk$set(background = header$background)
}
set_header(highlight = highlight)
## par(col = theme$foreground)
}
get_theme = function(theme = NULL) {
if (is.null(theme)) {
f = list.files(system.file("themes", package = "knitr"), pattern = "\\.css$")
gsub("\\.css$", "", basename(f))
theme_dir = system.file("themes", package = "knitr")
theme_files = list.files(theme_dir, pattern = "\\.css$")
gsub("\\.css$", "", basename(theme_files))
} else {
theme_to_header(theme)
}
Expand Down Expand Up @@ -41,11 +45,23 @@ get_theme = function(theme = NULL) {
#' knit_theme$set(thm)
knit_theme = list(set = set_theme, get = get_theme)


#' Generates header based on a theme and output format of document
#' @author Ramnath Vaidyanathan
#' @noRd
theme_to_header = function(theme){
fmt <- opts_knit$get('out.format')
if (fmt == 'latex'){
theme_to_header_latex(theme)
} else
theme_to_header_html(theme)
}

#' Generates latex header based on a theme
#' @importFrom highlight css.parser styler_assistant_latex
#' @author Ramnath Vaidyanathan
#' @noRd
theme_to_header = function(theme) {
theme_to_header_latex = function(theme) {
css_file = if (file.exists(theme)) theme else {
system.file("themes", sprintf("%s.css", theme), package = "knitr")
}
Expand All @@ -57,7 +73,27 @@ theme_to_header = function(theme) {

## write latex highlight header
fgheader = color_def(foreground, "fgcolor")
highlight = c(fgheader, styler_assistant_latex(css_out[-1]))

highlight = c(fgheader, styler_assistant_latex(css_out[-1]), boxes_latex())
list(highlight = highlight, background = background, foreground = foreground)
}

#' Generates css header based on a theme
#' @author Ramnath Vaidyanathan
#' @noRd
# HACK: replace ugly sub hack to match knitr background with theme
# TODO: warning, error, source etc. are still black, an issue for dark themes
# TODO: might be a good idea to regenerate the css files appending the
# .knitr.css template to the existing templates
theme_to_header_html <- function(theme){
css_file = if (file.exists(theme)) theme else {
system.file("themes", sprintf("%s.css", theme), package = "knitr")
}
bgcolor <- css.parser(css_file)$background$color
css_knitr = system.file('themes', '.knitr.css', package = 'knitr')
css_knitr_lines <- readLines(css_knitr)
css_knitr_lines <- sub('^([[:space:]]+background-color:\\s+)(.*)$',
sprintf('\\1%s;', bgcolor), css_knitr_lines)
css <- c(css_knitr_lines, readLines(css_file))
return(list(highlight = css))
}

19 changes: 19 additions & 0 deletions inst/misc/knitr.sty
@@ -0,0 +1,19 @@
\usepackage{framed}
\makeatletter
\newenvironment{kframe}{%
\def\FrameCommand##1{\hskip\@totalleftmargin \hskip-\fboxsep
\colorbox{shadecolor}{##1}\hskip-\fboxsep
% There is no \\@totalrightmargin, so:
\hskip-\linewidth \hskip-\@totalleftmargin \hskip\columnwidth}%
\MakeFramed {\advance\hsize-\width
\@totalleftmargin\z@ \linewidth\hsize
\@setminipage}}%
{\par\unskip\endMakeFramed}
\makeatother

\definecolor{shadecolor}{rgb}{.97, .97, .97}
\newenvironment{knitrout}{}{} % an empty environment to be redefined in TeX

\newcommand{\SweaveOpts}[1]{} % do not interfere with LaTeX
\newcommand{\SweaveInput}[1]{} % because they are not real TeX commands
\newcommand{\Sexpr}[1]{} % will only be parsed by R
17 changes: 17 additions & 0 deletions inst/themes/.knitr.css
@@ -0,0 +1,17 @@
.knitr {
background-color: #F7F7F7;
border:solid 1px #B0B0B0;
}
.error {
font-weight: bold;
color: #FF0000;
},
.warning {
font-weight: bold;
}
.message {
font-style: italic;
}
.source, .output, .warning, .error, .message {
padding: 0.5em 1em;
}
61 changes: 61 additions & 0 deletions inst/themes/default.css
@@ -0,0 +1,61 @@
.background{
color: #eeeeee ;
}
.number{
color: rgb(21,20,181) ;
}

.functioncall{
color: red ;
}

.string{
color: rgb(153,153,255) ;
}

.keyword{
font-weight: bolder ;
color: black;
}

.argument{
color: rgb( 177,63,5) ;
}

.comment{
color: rgb( 204,204,204) ;
}

.roxygencomment{
color: rgb(0,151,255);
}

.formalargs{
color: rgb(18,182,18);
}

.eqformalargs{
color: rgb(18,182,18);
}

.assignement{
font-weight: bolder;
color: rgb(55,55,98);
}

.package{
color: rgb(150,182,37);
}

.slot{
font-style:italic;
}

.symbol{
color: black ;
}

.prompt{
color: black ;
}