forked from yihui/knitr
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathutils-sweave.R
185 lines (171 loc) · 8.67 KB
/
utils-sweave.R
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
#' Convert Sweave to knitr documents
#'
#' This function converts an Sweave document to a \pkg{knitr}-compatible
#' document.
#'
#' The pseudo command \samp{\\SweaveInput{file.Rnw}} is converted to a code
#' chunk header \code{<<child='file.Rnw'>>=}.
#'
#' Similarly \samp{\\SweaveOpts{opt = value}} is converted to a code chunk
#' \samp{opts_chunk$set(opt = value)} with the chunk option \code{include =
#' FALSE}; the options are automatically fixed in the same way as local chunk
#' options (explained below).
#'
#' The Sweave package \samp{\\usepackage{Sweave}} in the preamble is removed
#' because it is not required.
#'
#' Chunk options are updated if necessary: option values \code{true} and
#' \code{false} are changed to \code{TRUE} and \code{FALSE} respectively;
#' \code{fig=TRUE} is removed because it is not necessary for \pkg{knitr} (plots
#' will be automatically generated); \code{fig=FALSE} is changed to
#' \code{fig.keep='none'}; the devices \code{pdf/jpeg/png/eps/tikz=TRUE} are
#' converted to \code{dev='pdf'/'jpeg'/'png'/'postscript'/'tikz'};
#' \code{pdf/jpeg/png/eps/tikz=FALSE} are removed;
#' \code{results=tex/verbatim/hide} are changed to
#' \code{results='asis'/'markup'/'hide'}; \code{width/height} are changed to
#' \code{fig.width/fig.height}; \code{prefix.string} is changed to
#' \code{fig.path}; \code{print/term/prefix=TRUE/FALSE} are removed; most of the
#' character options (e.g. \code{engine} and \code{out.width}) are quoted;
#' \code{keep.source=TRUE/FALSE} is changed to \code{tidy=FALSE/TRUE} (note the
#' order of values).
#'
#' If a line \code{@@} (it closes a chunk) directly follows a previous
#' \code{@@}, it is removed; if a line \code{@@} appears before a code chunk and
#' no chunk is before it, it is also removed, because \pkg{knitr} only uses one
#' \samp{@@} after \samp{<<>>=} by default (which is not the original Noweb
#' syntax but more natural).
#' @param file Path to the Rnw file (must be encoded in UTF-8).
#' @param output Output file path. By default, \file{file.Rnw} produces
#' \file{file-knitr.Rnw}); if \code{text} is not NULL, no output file will be
#' produced.
#' @param text An alternative way to provide the Sweave code as a character
#' string. If \code{text} is provided, \code{file} will be ignored.
#' @return If \code{text} is \code{NULL}, the \code{output} file is written and
#' \code{NULL} is returned. Otherwise, the converted text string is returned.
#' @note If \samp{\\SweaveOpts{}} spans across multiple lines, it will not be
#' fixed, and you have to fix it manually. The LaTeX-style syntax of Sweave
#' chunks are ignored (see \code{?SweaveSyntaxLatex}); only the Noweb syntax
#' is supported.
#' @seealso \code{\link{Sweave}}, \code{\link{gsub}}
#' @references The motivation of the changes in the syntax:
#' \url{https://yihui.org/knitr/demo/sweave/}
#' @export
#' @examples Sweave2knitr(text='<<echo=TRUE>>=') # this is valid
#' Sweave2knitr(text="<<png=true>>=") # dev='png'
#' Sweave2knitr(text="<<eps=TRUE, pdf=FALSE, results=tex, width=5, prefix.string=foo>>=")
#' Sweave2knitr(text="<<,png=false,fig=TRUE>>=")
#' Sweave2knitr(text="\\SweaveOpts{echo=false}")
#' Sweave2knitr(text="\\SweaveInput{hello.Rnw}")
#' # Sweave example in utils
#' testfile = system.file("Sweave", "Sweave-test-1.Rnw", package = "utils")
#' Sweave2knitr(testfile, output = 'Sweave-test-knitr.Rnw')
#' if (interactive()) knit('Sweave-test-knitr.Rnw') # or knit2pdf() directly
#' unlink('Sweave-test-knitr.Rnw')
Sweave2knitr = function(file, output = gsub('[.]([^.]+)$', '-knitr.\\1', file), text = NULL) {
x = text
if (is.null(x)) x = read_utf8(file)
x = native_encode(x)
x = gsub_msg('removing \\usepackage{Sweave}',
'^\\s*\\\\usepackage(\\[.*\\])?\\{Sweave\\}', '', x)
i = grep('^<<(.*)>>=\\s*$', x)
if (length(i)) {
opts = gsub('^<<(.*)>>=\\s*$', '\\1', x[i])
x[i] = paste0('<<', fix_sweave(opts), '>>=')
}
x = gsub_msg("replacing \\SweaveInput{...} with <<child='...'>>=",
'^\\s*\\\\SweaveInput\\{([^}]+)\\}', "\n<<'child-\\1', child='\\1'>>=\n@\n", x)
s = '^\\s*\\\\SweaveOpts\\{([^}]*)\\}.*$'
i = grep(s, x)
if (length(i)) {
opts = fix_sweave(gsub(s, '\\1', x[i]))
x[i] = gsub_msg('changing \\SweaveOpts{} to opts_chunk$set()', s, '@_@_@', x[i])
for (j in seq_along(i))
x[i[j]] = gsub('@_@_@', one_string(c(
'\n<<include=FALSE>>=', 'library(knitr)', 'opts_chunk$set(', opts[j], ')', '@\n'
)), x[i[j]])
}
# remove the extra @
i1 = grepl(all_patterns$rnw$chunk.begin, x)
i2 = grepl(all_patterns$rnw$chunk.end, x)
i3 = group_indices(i1, i2)
# if a chunk end is not part of a chunk (odd group index), this
# chunk end is unnecessary
i = which(i2 & i3 %% 2 == 1)
if (length(i)) {
message('removing extra lines (#n shows line numbers):\n',
one_string(formatUL(sprintf('(#%d) %s', i, x[i]), offset = 4)))
x = x[-i]
}
if (is.null(text)) write_utf8(x, output) else x
}
gsub_msg = function(msg, pattern, replacement, x, ...) {
if (length(i <- grep(pattern, x, ...))) {
message(msg, ':\n', one_string(formatUL(x[i], offset = 4)))
gsub(pattern, replacement, x, ...)
} else x
}
fix_sweave = function(x) {
x = gsub_msg('capitalizing true/false to TRUE/FALSE',
'(echo|eval|split|include|fig|pdf|eps|jpeg|png|tikz|keep.source|print|term|strip.white|prefix)\\s*=\\s*(true|false)', '\\1=\\U\\2', x, perl = TRUE)
x = gsub_msg('removing the unnecessary option fig=TRUE',
'fig\\s*=\\s*TRUE', '', x)
x = gsub_msg("fig=FALSE should be fig.keep='none'",
'fig\\s*=\\s*FALSE', "fig.keep='none'", x)
x = gsub_msg("replacing pdf/jpeg/png/tikz=TRUE with dev='pdf'/'jpeg'/'png'/'tikz'",
'(pdf|jpeg|png|tikz)\\s*=\\s*TRUE', "dev='\\1'", x)
x = gsub_msg('removing pdf/jpeg/png/eps/tikz=FALSE',
'(pdf|jpeg|png|eps|tikz)\\s*=\\s*FALSE', '', x)
x = gsub_msg("replacing eps=TRUE with dev='postscript'",
'eps\\s*=\\s*TRUE', "dev='postscript'", x)
x = gsub_msg('replacing results=tex with results=asis',
'results\\s*=\\s*tex', 'results=asis', x)
x = gsub_msg('replacing results=verbatim with results=markup',
'results\\s*=\\s*verbatim', 'results=markup', x)
x = gsub_msg('quoting the results option',
'results\\s*=\\s*(asis|markup|hide)', "results='\\1'", x)
x = gsub_msg('replacing width/height with fig.width/fig.height',
'([^.])(width|height)\\s*=\\s*(\\d+)', '\\1fig.\\2=\\3', x)
x = gsub_msg("replacing prefix.string=foo with fig.path='foo'",
'prefix.string\\s*=\\s*([^,]+)', "fig.path='\\1'", x)
x = gsub_msg("removing options 'print', 'term', 'prefix'",
'(print|term|prefix)\\s*=\\s*(TRUE|FALSE|T|F)', '', x)
x = gsub_msg('quoting options engine, fig.path, cache.path, fig.keep, fig.show, dev, out.width, out.height, fig.align',
"(engine|fig\\.path|cache\\.path|fig\\.keep|fig\\.show|dev|out\\.width|out\\.height|fig\\.align)\\s*=\\s*([^,'\"]+)",
"\\1='\\2'", x)
x = gsub_msg('changing keep.source=TRUE to tidy=FALSE',
'keep\\.source\\s*=\\s*(TRUE|T)', 'tidy=FALSE', x)
x = gsub_msg('changing keep.source=FALSE to tidy=TRUE',
'keep\\.source\\s*=\\s*(FALSE|F)', 'tidy=TRUE', x)
x = gsub_msg('doubling backslashes', '\\', '\\\\', x, fixed = TRUE)
# after we remove some options, there might be , ,
while (length(grep(',\\s*,', x))) x = gsub(',\\s*,', ',', x)
x = gsub('^(\\s*,\\s*)+|(\\s*,\\s*)+$', '', x)
x
}
# check the source code to see if it is an Sweave document
which_sweave = function(x) {
unique(c(
grep('^\\s*\\\\(usepackage(\\[.*\\])?\\{Sweave|SweaveInput\\{|SweaveOpts\\{)', x),
grep('^<<.*\\b(echo|eval|split|include)\\s*=\\s*(true|false)\\b.*>>=', x),
grep('^<<.*\\bresults\\s*=\\s*(tex|verbatim|hide)\\b.*>>=', x),
grep('^<<.*\\b(fig|pdf|eps|jpeg|png|tikz)\\s*=\\s*(true|false|T|F).*>>=', x),
grep('^<<.*([, ])(width|height)\\s*=\\s*(\\d+).*>>=', x),
grep('^<<.*\\b(keep.source|print|term|prefix)\\s*=\\s*(true|false|T|F).*>>=', x)
))
}
remind_sweave = function(file, sweave_lines) {
msg = sprintf('It seems you are using the Sweave-specific syntax in line(s) %s; you may need Sweave2knitr("%s") to convert it to knitr',
paste(sweave_lines, collapse = ', '), file)
# throw a normal warning when R CMD check or tcltk not available
warning(msg)
if (!is_R_CMD_check() && Sys.getenv('CI') != 'true' && tcltk_available()) {
do.call(
getFromNamespace('tkmessageBox', 'tcltk'),
list(title = 'Sweave Noweb syntax?', icon = 'info', message = msg)
)
}
}
tcltk_available = function() {
capabilities('tcltk') && capabilities('X11') && has_package('tcltk') &&
getFromNamespace('.TkUp', 'tcltk')
}