blob: 0e70443468505861150f6df09350a4482b595f50 [file] [log] [blame]
# File src/library/tools/R/Rd2ex.R
# Part of the R package, https://www.R-project.org
#
# Copyright (C) 1995-2014 The R Core Team
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# A copy of the GNU General Public License is available at
# https://www.R-project.org/Licenses/
## This warns on multiple \examples sections, never fails.
Rd2ex <-
function(Rd, out="", defines=.Platform$OS.type, stages="render",
outputEncoding="UTF-8", commentDontrun = TRUE, commentDonttest = FALSE, ...)
{
WriteLines <- function(x, con, outputEncoding, ...) {
if (outputEncoding != "UTF-8") {
x <- iconv(x, "UTF-8", outputEncoding, mark=FALSE)
if (anyNA(x))
x <- iconv(x, "UTF-8", outputEncoding, sub="byte", mark=FALSE)
}
writeLines(x, con, useBytes = TRUE, ...)
}
dropNewline <- FALSE # drop next char if newline
of0 <- function(...)
of1(paste0(...))
of1 <- function(text) {
if (dropNewline && length(text)) {
text[1L] <- psub("^\n", "", text[1L])
dropNewline <<- FALSE
}
WriteLines(text, con, outputEncoding, sep = "")
}
wr <- function(x)
paste0("###", strwrap(remap(x), 73L, indent=1L, exdent=3L), collapse="\n")
remap <- function(x) {
if(!length(x)) return(x)
## \link, \var are untouched in comments: e.g. is.R
x <- psub("\\\\(link|var)\\{([^}]+)\\}", "\\2", x)
## not valid in perl: use lookbehind instead.
## x <- gsub("(^|[^\\])\\\\([%{])", "\\1\\2", x)
x <- psub("(?<!\\\\)\\\\([%{])", "\\1", x)
x <- psub("\\\\(l|)dots", "...", x)
## FIXME: Previously said "Want to leave file bytes unchanged"
x
}
render <- function(x, prefix = "")
{
renderDont <- function(txt, comment, label=TRUE, xtra1=comment) {
if (label)
of0("## ", txt, ": ")
## Special case for one line.
if (xtra1 && length(x) == 1L) {
render(x[[1L]], prefix)
} else {
if (!grepl("^\n", x[[1L]][1L], perl = TRUE) &&
RdTags(x)[1L] != "COMMENT") {
writeLines("", con)
render(x[[1L]], paste0(if (comment) "##D ", prefix))
} else render(x[[1L]], prefix)
for(i in seq_along(x)[-1]) ## `` i in 2:length(x) ''
render(x[[i]], paste0(if (comment) "##D ", prefix))
last <- x[[length(x)]]
if (!grepl("\n$", last[length(last)], perl = TRUE))
writeLines("", con)
if (label)
of0("## End(",txt,")")
}
}
tag <- attr(x, "Rd_tag")
if(tag %in% c("\\dontshow", "\\testonly")) {
renderDont("Don't show", comment=FALSE)
} else if (tag == "\\dontrun") {
renderDont("Not run", commentDontrun, label=commentDontrun)
} else if (tag == "\\donttest") {
renderDont("No test", commentDonttest, xtra1=FALSE)
} else if (tag == "COMMENT") {
## % can escape a whole line (e.g. beavers.Rd) or
## be trailing when we want a NL
## This is not right (leading spaces?) but it may do
if(attr(x, "srcref")[2L] == 1L) dropNewline <<- TRUE
} else if (tag %in% c("\\dots", "\\ldots")) {
of1("...")
} else if (tag == "\\if" || tag == "\\ifelse") {
if (testRdConditional("example", x, Rdfile))
for(i in seq_along(x[[2L]])) render(x[[2L]][[i]], prefix)
else if (tag == "\\ifelse")
for(i in seq_along(x[[3L]])) render(x[[3L]][[i]], prefix)
} else if (tag == "\\out") {
for (i in seq_along(x))
of1(x[[i]])
} else if (tag %in% c("USERMACRO", "\\newcommand", "\\renewcommand")) {
# do nothing
} else {
txt <- unlist(x)
of0(prefix, remap(txt))
}
}
Rd <- prepare_Rd(Rd, defines=defines, stages=stages, ...)
Rdfile <- attr(Rd, "Rdfile")
sections <- RdTags(Rd)
## FIXME should we skip empty \examples sections?
where <- which(sections == "\\examples")
if(length(where)) {
if (is.character(out)) {
if(out == "") {
con <- stdout()
} else {
con <- file(out, "wt")
on.exit(close(con))
}
} else {
con <- out
out <- summary(con)$description
}
if(length(where) > 1L)
warning("more than one \\examples section, using the first")
ex <- Rd[[ where[1L] ]]
exl <- unlist(ex)
## Do we need to output an encoding?
if(length(exl) && any(Encoding(exl) != "unknown")) {
if(any(f <- sections == "\\encoding")) {
encoding <- unlist(Rd[which(f)])[1L]
## FIXME: which should win here?
if(nzchar(outputEncoding))
encoding <- outputEncoding
else
outputEncoding <- encoding
of0("### Encoding: ", encoding, "\n\n") #
}
}
nameblk <- sections == "\\name"
if (any(nameblk)) {
## perl wrapped here, but it seems unnecessary
name <- as.character(Rd[[ which.max(nameblk) ]])
of0("### Name: ", name, "\n")
}
title <- .Rd_format_title(.Rd_get_title(Rd))
if (!length(title))
title <- "No title found"
of0(wr(paste0("Title: ", title)), "\n")
aliasblks <- sections == "\\alias"
if (any(aliasblks)) {
aliases <- unlist(Rd[aliasblks])
sp <- grep(" ", aliases, fixed = TRUE)
aliases[sp] <- paste0("'", aliases[sp], "'")
of0(wr(paste0("Aliases: ", paste(aliases, collapse=" "))),
"\n")
}
keyblks <- sections == "\\keyword"
if (any(keyblks)) {
## some people have only empty keyword blocks.
keys <- trimws(unlist(Rd[keyblks])) %w/o% .Rd_keywords_auto
if(length(keys)) {
of0(wr(paste("Keywords: ",
paste0(keys, collapse=" "))), "\n")
}
}
writeLines(c("", "### ** Examples"), con)
for (i in seq_along(ex)) render(ex[[i]])
of1("\n\n\n")
}
invisible(out)
}