blob: d3c6037636136738ce0439f43491852b56d52888 [file] [log] [blame]
# File src/library/tools/R/Rd2txt.R
# Part of the R package, https://www.R-project.org
#
# Copyright (C) 1995-2018 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 stops on
## unrecognized tag
## \\tabular format must be simple text
## too many columns for format
## invalid markup in \[S3]method
## "Tag ", tag, " not expected in code block"
tabExpand <- function(x) {
srcref <- attr(x, "srcref")
start <- if(is.null(srcref)) 0L else srcref[5L] - 1L
.Call(C_doTabExpand, x, start)
}
Rd2txt_options <- local({
opts <- list(width = 80L,
minIndent = 10L,
extraIndent = 4L,
sectionIndent = 5L,
sectionExtra = 2L,
itemBullet = "* ",
enumFormat = function(n) sprintf("%d. ", n),
showURLs = FALSE,
code_quote = TRUE,
underline_titles = TRUE)
function(...) {
args <- list(...)
if (!length(args))
return(opts)
else {
if (is.list(args[[1L]])) args <- args[[1L]]
result <- opts[names(args)]
opts[names(args)] <<- args
invisible(result)
}
}
})
transformMethod <- function(i, blocks, Rdfile) {
editblock <- function(block, newtext)
list(structure(newtext, Rd_tag = attr(block, "Rd_tag"),
srcref = attr(block, "srcref")))
# Most of the internal functions below are more like macros
# than functions; they mess around with these variables:
chars <- NULL
char <- NULL
j <- NULL
findOpen <- function(i) {
j <- i
char <- NULL
while (j < length(blocks)) {
j <- j + 1L
tag <- attr(blocks[[j]], "Rd_tag")
if (tag == "RCODE") {
# FIXME: This search and the ones below will be fooled
# by "#" comments
chars <- strsplit(blocks[[j]], "")[[1]]
parens <- cumsum( (chars == "(") - (chars == ")") )
if (any(parens > 0)) {
char <- which.max(parens > 0)
break
}
}
}
if (is.null(char))
stopRd(block, Rdfile, sprintf("no parenthesis following %s", blocktag))
chars <<- chars
char <<- char
j <<- j
}
findComma <- function(i) {
j <- i
level <- 1L
char <- NULL
while (j < length(blocks)) {
j <- j + 1L
tag <- attr(blocks[[j]], "Rd_tag")
if (tag == "RCODE") {
chars <- strsplit(blocks[[j]], "")[[1]]
parens <- level + cumsum( (chars == "(") - (chars == ")") )
if (any(parens == 1 & chars == ",")) {
char <- which.max(parens == 1 & chars == ",")
break
}
if (any(parens == 0))
break
level <- parens[length(parens)]
}
}
if (is.null(char))
stopRd(block, Rdfile, sprintf("no comma in argument list following %s", blocktag))
chars <<- chars
char <<- char
j <<- j
}
findClose <- function(i) {
j <- i
level <- 1L
char <- NULL
while (j < length(blocks)) {
j <- j + 1L
tag <- attr(blocks[[j]], "Rd_tag")
if (tag == "RCODE") {
chars <- strsplit(blocks[[j]], "")[[1]]
parens <- level + cumsum( (chars == "(") - (chars == ")") )
if (any(parens == 0)) {
char <- which(parens == 0)[1]
break
}
level <- parens[length(parens)]
}
}
if (is.null(char))
stopRd(block, Rdfile, sprintf("no closing parenthesis following %s", blocktag))
chars <<- chars
char <<- char
j <<- j
}
rewriteBlocks <- function()
c(blocks[seq_len(j-1L)],
editblock(blocks[[j]],
paste(chars[seq_len(char)], collapse="")),
if (char < length(chars))
editblock(blocks[[j]],
paste(chars[-seq_len(char)], collapse="")),
if (j < length(blocks)) blocks[-seq_len(j)])
deleteBlanks <- function() {
while (char < length(chars)) {
if (chars[char + 1] == " ") {
char <- char + 1
chars[char] <- ""
} else
break
}
char <<- char
chars <<- chars
}
block <- blocks[[i]]
blocktag <- attr(block, "Rd_tag")
srcref <- attr(block, "srcref")
class <- block[[2L]] # or signature
generic <- as.character(block[[1L]])
default <- as.character(class) == "default"
if(generic %in% c("[", "[[", "$")) {
## need to assemble the call by matching parens in RCODE
findOpen(i) # Sets chars, char and j
chars[char] <- ""
blocks <- c(blocks[seq_len(j-1L)],
editblock(blocks[[j]],
paste(chars[seq_len(char)], collapse="")),
if (char < length(chars))
editblock(blocks[[j]],
paste(chars[-seq_len(char)], collapse="")),
if (j < length(blocks)) blocks[-seq_len(j)])
findComma(j) # Sets chars, char and j
chars[char] <- generic
# Delete blanks after the comma
deleteBlanks()
blocks <- rewriteBlocks()
findClose(j)
# Edit the closing paren
chars[char] <- switch(generic,
"[" = "]",
"[[" = "]]",
"$" = "")
blocks[j] <- editblock(blocks[[j]],
paste(chars, collapse=""))
methodtype <- if (grepl("<-", blocks[[j]])) "replacement " else ""
} else if(grepl(sprintf("^%s$",
paste(c("\\+", "\\-", "\\*",
"\\/", "\\^", "<=?",
">=?", "!=?", "==",
"\\&", "\\|", "!",
"\\%[[:alnum:][:punct:]]*\\%"),
collapse = "|")),
generic)) {
## Binary operators and unary '!'.
findOpen(i)
if (generic != "!") {
chars[char] <- ""
blocks <- rewriteBlocks()
findComma(j)
chars[char] <- paste0(" ", generic, " ")
# Delete blanks after the comma
deleteBlanks()
blocks <- rewriteBlocks()
} else {
chars[char] <- "!"
blocks <- rewriteBlocks()
}
findClose(j)
chars[char] <- ""
blocks[j] <- editblock(blocks[[j]],
paste(chars, collapse=""))
methodtype <- ""
} else {
findOpen(i)
chars[char] <- paste0(generic, "(")
blocks <- rewriteBlocks()
findClose(j)
methodtype <- if (grepl("<-", blocks[[j]])) "replacement " else ""
}
if (blocktag == "\\S4method") {
## some signatures are very long.
blocks <- if(nchar(class) > 50L) {
cl <- paste0("'", as.character(class), "'")
if(nchar(cl) > 70L) {
cl <- strsplit(cl, ",")[[1L]]
ncl <- length(cl)
cl[-ncl] <- paste0(cl[-ncl], ",")
cl[-1L] <- paste0(" ", cl[-1L])
}
cl <- paste("##", cl, collapse="\n")
c( blocks[seq_len(i-1L)],
list(structure(paste0("## S4 ", methodtype, "method for signature \n"),
Rd_tag="RCODE", srcref=srcref)),
list(structure(cl, Rd_tag="TEXT", srcref=srcref)),
list(structure("\n", Rd_tag="RCODE", srcref=srcref)),
blocks[-seq_len(i)] )
} else
c( blocks[seq_len(i-1L)],
list(structure(paste0("## S4 ", methodtype, "method for signature '"),
Rd_tag="RCODE", srcref=srcref)),
class,
list(structure("'\n", Rd_tag="RCODE", srcref=srcref)),
blocks[-seq_len(i)] )
} else if (default)
blocks <- c( blocks[seq_len(i-1)],
list(structure(paste0("## Default S3 ", methodtype, "method:\n"),
Rd_tag="RCODE", srcref=srcref)),
blocks[-seq_len(i)] )
else
blocks <- c( blocks[seq_len(i-1)],
list(structure(paste0("## S3 ", methodtype, "method for class '"),
Rd_tag="RCODE", srcref=srcref)),
class,
list(structure("'\n", Rd_tag="RCODE", srcref=srcref)),
blocks[-seq_len(i)] )
blocks
}# transformMethod()
Rd2txt <-
function(Rd, out="", package = "", defines=.Platform$OS.type,
stages = "render", outputEncoding = "",
fragment = FALSE, options, ...)
{
## we need to keep track of where we are.
buffer <- character() # Buffer not yet written to con
# Newlines have been processed, each line in buffer is
# treated as a separate input line (but may be wrapped before output)
linestart <- TRUE # At start of line?
indent <- 0L # Default indent
wrapping <- TRUE # Do word wrap?
keepFirstIndent <- FALSE # Keep first line indent?
dropBlank <- FALSE # Drop initial blank lines?
haveBlanks <- 0L # How many blank lines have just been written?
enumItem <- 0L # Last enumeration item number
inEqn <- FALSE # Should we do edits needed in an eqn?
sectionLevel <- 0 # How deeply nested within sections/subsections
saveOpts <- Rd2txt_options()
on.exit(Rd2txt_options(saveOpts))# Rd files may change these, so restore them
# whether or not the caller set them.
if (!missing(options)) Rd2txt_options(options)
## these attempt to mimic pre-2.10.0 layout
WIDTH <- 0.9 * Rd2txt_options()$width
HDR_WIDTH <- WIDTH - 2L
startCapture <- function() {
save <- list(buffer=buffer, linestart=linestart, indent=indent,
wrapping=wrapping, keepFirstIndent=keepFirstIndent,
dropBlank=dropBlank, haveBlanks=haveBlanks,
enumItem=enumItem, inEqn=inEqn)
buffer <<- character()
linestart <<- TRUE
indent <<- 0L
wrapping <<- TRUE
keepFirstIndent <<- FALSE
dropBlank <<- FALSE
haveBlanks <<- 0L
enumItem <<- 0L
inEqn <<- FALSE
save
}
endCapture <- function(saved) {
result <- buffer
buffer <<- saved$buffer
linestart <<- saved$linestart
indent <<- saved$indent
wrapping <<- saved$wrapping
keepFirstIndent <<- saved$keepFirstIndent
dropBlank <<- saved$dropBlank
haveBlanks <<- saved$haveBlanks
enumItem <<- saved$enumItem
inEqn <<- saved$inEqn
result
}
## for efficiency
WriteLines <-
if(outputEncoding == "UTF-8" ||
(outputEncoding == "" && l10n_info()[["UTF-8"]])) {
function(x, con, outputEncoding, ...)
writeLines(x, con, useBytes = TRUE, ...)
} else {
function(x, con, outputEncoding, ...) {
x <- iconv(x, "UTF-8", outputEncoding, sub="byte", mark=FALSE)
writeLines(x, con, useBytes = TRUE, ...)
}
}
## Use display widths as used by cat not print.
frmt <- function(x, justify="left", width = 0L) {
justify <- match.arg(justify, c("left", "right", "centre", "none"))
w <- sum(nchar(x, "width")) # copes with 0-length x
if(w < width && justify != "none") {
excess <- width - w
left <- right <- 0L
if(justify == "left") right <- excess
else if(justify == "right") left <- excess
else if(justify == "centre") {
left <- excess %/% 2
right <- excess-left
}
paste(c(rep_len(" ", left), x, rep_len(" ", right)), collapse = "")
} else x
}
wrap <- function(doWrap = TRUE)
if (doWrap != wrapping) { flushBuffer(); wrapping <<- doWrap }
putw <- function(...) { wrap(TRUE); put(...) }
putf <- function(...) { wrap(FALSE); put(...) }
put <- function(...) {
txt <- paste0(..., collapse="")
trail <- endsWith(txt, "\n")
# Convert newlines
txt <- strsplit(txt, "\n", fixed = TRUE)[[1L]]
if (dropBlank) {
while(length(txt) && grepl("^[[:space:]]*$", txt[1L]))
txt <- txt[-1L]
if (length(txt)) dropBlank <<- FALSE
}
if(!length(txt)) return()
haveBlanks <<- 0
if (linestart) buffer <<- c(buffer, txt)
else if (length(buffer)) {
buffer[length(buffer)] <<-
paste0(buffer[length(buffer)], txt[1L])
buffer <<- c(buffer, txt[-1L])
}
else buffer <<- txt
linestart <<- trail
}
flushBuffer <- function() {
if (!length(buffer)) return()
if (wrapping) {
if (keepFirstIndent) {
first <- nchar(psub1("[^ ].*", "", buffer[1L]))
keepFirstIndent <<- FALSE
} else
first <- indent
buffer <<- c(buffer, "") # Add an extra blank sentinel
blankLines <- grep("^[[:space:]]*$", buffer)
result <- character()
start <- 1L
for (i in seq_along(blankLines)) {
if (blankLines[i] > start) {
result <- c(result,
strwrap(paste(buffer[start:(blankLines[i]-1L)],
collapse = " "),
WIDTH, indent = first, exdent = indent))
first <- indent
}
result <- c(result, "")
start <- blankLines[i]+1L
}
## we want to collapse multiple blank lines when wrapping
## and to remove the sentinel (which we need to do first or
## we will drop a single blank line)
buffer <<- result[-length(result)]
empty <- !nzchar(buffer)
drop <- empty & c(FALSE, empty[-length(empty)])
buffer <<- buffer[!drop]
} else { # Not wrapping
if (keepFirstIndent) {
if (length(buffer) > 1L)
buffer[-1L] <<- paste0(strrep(" ", indent), buffer[-1L])
keepFirstIndent <- FALSE
} else
buffer <<- paste0(strrep(" ", indent), buffer)
}
if (length(buffer)) WriteLines(buffer, con, outputEncoding)
buffer <<- character()
linestart <<- TRUE
}
encoding <- "unknown"
li <- l10n_info()
## See the comment in ?Rd2txt as to why we do not attempt fancy quotes
## in Windows CJK locales -- and in any case they would need more work
## This covers the common single-byte locales and Thai (874)
use_fancy_quotes <-
(.Platform$OS.type == "windows" &&
((li$codepage >= 1250 && li$codepage <= 1258) || li$codepage == 874)) ||
li[["UTF-8"]]
if(!isFALSE(getOption("useFancyQuotes")) &&
use_fancy_quotes) {
## On Windows, Unicode literals are translated to local code page
LSQM <- intToUtf8("0x2018") # Left single quote
RSQM <- intToUtf8("0x2019") # Right single quote
LDQM <- intToUtf8("0x201c") # Left double quote
RDQM <- intToUtf8("0x201d") # Right double quote
} else {
LSQM <- RSQM <- "'"
LDQM <- RDQM <- '"'
}
trim <- function(x) {
x <- psub1("^\\s*", "", x)
psub1("\\s*$", "", x)
}
## underline via backspacing
txt_header <- function(header) {
opts <- Rd2txt_options()
header <- paste(strwrap(header, WIDTH), collapse="\n")
if (opts$underline_titles) {
letters <- strsplit(header, "", fixed = TRUE)[[1L]]
isaln <- grep("[[:alnum:]]", letters)
letters[isaln] <- paste0("_\b", letters[isaln])
paste(letters, collapse = "")
} else header
}
unescape <- function(x) {
x <- psub("(---|--)", "-", x)
x
}
writeCode <- function(x) {
txt <- as.character(x)
if(inEqn) txt <- txt_eqn(txt)
txt <- fsub('"\\{"', '"{"', txt)
## \dots gets left in noquote.Rd
txt <- fsub("\\dots", "...", txt)
put(txt)
}
# This function strips pending blank lines, then adds n new ones.
blankLine <- function(n = 1L) {
while (length(buffer) &&
grepl("^[[:blank:]]*$", buffer[length(buffer)]))
buffer <<- buffer[-length(buffer)]
flushBuffer()
if (n > haveBlanks) {
buffer <<- rep_len("", n - haveBlanks)
flushBuffer()
haveBlanks <<- n
}
dropBlank <<- TRUE
}
txt_eqn <- function(x) {
x <- psub("\\\\(Alpha|Beta|Gamma|Delta|Epsilon|Zeta|Eta|Theta|Iota|Kappa|Lambda|Mu|Nu|Xi|Omicron|Pi|Rho|Sigma|Tau|Upsilon|Phi|Chi|Psi|Omega|alpha|beta|gamma|delta|epsilon|zeta|eta|theta|iota|kappa|lambda|mu|nu|xi|omicron|pi|rho|sigma|tau|upsilon|phi|chi|psi|omega|sum|prod|sqrt)", "\\1", x)
x <- psub("\\\\(dots|ldots)", "...", x)
x <- fsub("\\le", "<=", x)
x <- fsub("\\ge", ">=", x)
x <- fsub("\\infty", "Inf", x)
## FIXME: are these needed?
x <- psub("\\\\(bold|strong|emph|var)\\{([^}]*)\\}", "\\2", x)
x <- psub("\\\\(code|samp)\\{([^}]*)\\}", "'\\2'", x)
x
}
writeDR <- function(block, tag) {
if (length(block) > 1L) {
putf('## Not run:\n')
writeCodeBlock(block, tag)
blankLine(0L)
putf('## End(Not run)\n')
} else {
putf('## Not run: ')
writeCodeBlock(block, tag)
blankLine(0L)
}
}
writeQ <- function(block, tag, quote=tag)
{
if (use_fancy_quotes) {
if (quote == "\\sQuote") {
put(LSQM); writeContent(block, tag); put(RSQM)
} else {
put(LDQM); writeContent(block, tag); put(RDQM)
}
} else {
if (quote == "\\sQuote") {
put("'"); writeContent(block, tag); put("'")
} else {
put("\""); writeContent(block,tag); put("\"")
}
}
}
writeBlock <- function(block, tag, blocktag) {
switch(tag,
UNKNOWN =,
VERB =,
RCODE = writeCode(tabExpand(block)),
TEXT = if(blocktag == "\\command") putw(block) else putw(unescape(tabExpand(block))),
USERMACRO =,
"\\newcommand" =,
"\\renewcommand" =,
COMMENT = {},
LIST = writeContent(block, tag),
"\\describe" = {
blankLine(0L)
writeContent(block, tag)
blankLine()
},
"\\itemize"=,
"\\enumerate"= {
blankLine(0L)
enumItem0 <- enumItem
enumItem <<- 0L
indent0 <- indent
opts <- Rd2txt_options()
indent <<- max(opts$minIndent,
indent + opts$extraIndent)
dropBlank <<- TRUE
writeContent(block, tag)
blankLine()
indent <<- indent0
enumItem <<- enumItem0
},
"\\code"=,
"\\command"=,
"\\env"=,
"\\file"=,
"\\kbd"=,
"\\option"=,
"\\pkg"=,
"\\samp" = {
opts <- Rd2txt_options()
if(opts$code_quote)
writeQ(block, tag, quote="\\sQuote")
else writeContent(block,tag)
},
"\\email" = {
put("<email: ",
trimws(gsub("\n", "",
paste(as.character(block),
collapse=""))),
">")
},
"\\url" = {
put("<URL: ",
trimws(gsub("\n", "",
paste(as.character(block),
collapse=""))),
">")
},
"\\href" = {
opts <- Rd2txt_options()
writeContent(block[[2L]], tag)
if (opts$showURLs)
put(" (URL: ",
trimws(gsub("\n", "",
paste(as.character(block[[1L]]),
collapse=""))),
")")
},
"\\Sexpr"= put(as.character.Rd(block, deparse=TRUE)),
"\\acronym" =,
"\\cite"=,
"\\dfn"= ,
"\\special" = ,
"\\var" = writeContent(block, tag),
"\\bold"=,
"\\strong"= {
put("*")
writeContent(block, tag)
put("*")
},
"\\emph"= {
put("_")
writeContent(block, tag)
put("_")
},
"\\sQuote" =,
"\\dQuote"= writeQ(block, tag) ,
"\\preformatted"= {
putf("\n")
writeCodeBlock(block, tag)
},
"\\verb"= put(block),
"\\linkS4class" =,
"\\link" = writeContent(block, tag),
"\\cr" = {
## we want to print out what we have, and if
## followed immediately by \n (as it usually is)
## discard that. This is not entirely correct,
## but it is better than before ....
flushBuffer()
dropBlank <<- TRUE
},
"\\dots" =,
"\\ldots" = put("..."),
"\\R" = put("R"),
"\\enc" = {
## Test to see if we can convert the encoded version
txt <- as.character(block[[1L]])
test <- iconv(txt, "UTF-8", outputEncoding, mark = FALSE)
txt <- if(!anyNA(test)) txt else as.character(block[[2L]])
put(txt)
} ,
"\\eqn" = {
block <- block[[length(block)]]
## FIXME: treat 2 of 2 differently?
inEqn0 <- inEqn
inEqn <<- TRUE
writeContent(block, tag)
inEqn <<- inEqn0
},
"\\deqn" = {
blankLine()
block <- block[[length(block)]]
save <- startCapture()
inEqn <<- TRUE
writeContent(block, tag)
eqn <- endCapture(save)
eqn <- frmt(eqn, justify="centre", width=WIDTH-indent)
putf(paste(eqn, collapse="\n"))
blankLine()
},
"\\figure" = {
blankLine()
save <- startCapture()
writeContent(block[[length(block)]], tag)
alt <- endCapture(save)
if (length(alt)) {
alt <- frmt(alt, justify = "centre",
width = WIDTH - indent)
putf(paste(alt, collapse = "\n"))
blankLine()
}
},
"\\tabular" = writeTabular(block),
"\\subsection" = writeSection(block, tag),
"\\if"=,
"\\ifelse" =
if (testRdConditional("text", block, Rdfile))
writeContent(block[[2L]], tag)
else if (tag == "\\ifelse")
writeContent(block[[3L]], tag),
"\\out" = for (i in seq_along(block))
put(block[[i]]),
stopRd(block, Rdfile, "Tag ", tag, " not recognized")
)
}
writeTabular <- function(table) {
formats <- table[[1L]]
content <- table[[2L]]
if (length(formats) != 1L || RdTags(formats) != "TEXT")
stopRd(table, Rdfile, "\\tabular format must be simple text")
formats <- strsplit(formats[[1L]], "", fixed = TRUE)[[1L]]
tags <- RdTags(content)
entries <- list()
row <- 1L
col <- 1L
save <- startCapture()
dropBlank <<- TRUE
newEntry <- function() {
entries <<- c(entries, list(list(text=trim(endCapture(save)),
row=row, col=col)))
save <<- startCapture()
dropBlank <<- TRUE
}
for (i in seq_along(tags)) {
switch(tags[i],
"\\tab" = {
newEntry()
col <- col + 1
if (col > length(formats))
stopRd(content[[i]], Rdfile,
sprintf("too many columns for format '%s'",
table[[1L]]))
},
"\\cr" = {
newEntry()
row <- row + 1L
col <- 1L
},
writeBlock(content[[i]], tags[i], "\\tabular")
)
}
newEntry()
endCapture(save)
entries <- with(entries[[length(entries)]],
{
if (!length(text) && col == 1L)
entries[-length(entries)]
else
entries
})
if(!length(entries)) return()
rows <- entries[[length(entries)]]$row
cols <- max(sapply(entries, function(e) e$col))
widths <- rep_len(0L, cols)
lines <- rep_len(1L, rows)
for (i in seq_along(entries)) {
e <- entries[[i]]
while(length(e$text) && !nzchar(e$text[length(e$text)])) {
e$text <- e$text[-length(e$text)]
entries[[i]] <- e
}
if (any(nzchar(e$text)))
widths[e$col] <- max(widths[e$col], max(nchar(e$text, "w")))
lines[e$row] <- max(lines[e$row], length(e$text))
}
result <- matrix("", sum(lines), cols)
for (i in seq_len(cols))
result[, i] <- strrep(" ", widths[i])
firstline <- c(1L, 1L+cumsum(lines))
for (i in seq_along(entries)) {
e <- entries[[i]]
if(!length(e$text)) next
## FIXME: this is not right: it justifies strings as if
## they are escaped, so in particular \ takes two columns.
text <- frmt(e$text, justify=formats[e$col], width=widths[e$col])
for (j in seq_along(text))
result[firstline[e$row] + j - 1L, e$col] <- text[j]
}
blankLine()
indent0 <- indent
indent <<- indent + 1L
for (i in seq_len(nrow(result))) {
putf(paste0(" ", result[i,], " ", collapse=""))
# This version stripped leading blanks on the first line
# for (j in seq_len(cols))
# putf(" ", result[i,j], " ")
putf("\n")
}
blankLine()
indent <<- indent0
}
writeCodeBlock <- function(blocks, blocktag)
{
tags <- RdTags(blocks)
i <- 0
while (i < length(tags)) {
i <- i + 1
block <- blocks[[i]]
tag <- tags[i]
switch(tag,
"\\method" =,
"\\S3method" =,
"\\S4method" = {
blocks <- transformMethod(i, blocks, Rdfile)
tags <- RdTags(blocks)
i <- i - 1
},
UNKNOWN =,
VERB =,
RCODE =,
TEXT = writeCode(tabExpand(block)),
"\\donttest" =,
"\\special" =,
"\\var" = writeCodeBlock(block, tag),
"\\dots" =, # \ldots is not really allowed
"\\ldots" = put("..."),
"\\dontrun"= writeDR(block, tag),
USERMACRO =,
"\\newcommand" =,
"\\renewcommand" =,
COMMENT =,
"\\dontshow" =,
"\\testonly" = {}, # do nothing
## All the markup such as \emph
stopRd(block, Rdfile, "Tag ", tag,
" not expected in code block")
)
}
}
writeContent <- function(blocks, blocktag) {
itemskip <- FALSE
tags <- RdTags(blocks)
for (i in seq_along(tags)) {
tag <- tags[i]
block <- blocks[[i]]
switch(tag,
"\\item" = {
switch(blocktag,
"\\describe"= {
blankLine()
save <- startCapture()
dropBlank <<- TRUE
writeContent(block[[1L]], tag)
DLlab <- endCapture(save)
indent0 <- indent
opts <- Rd2txt_options()
indent <<- max(opts$minIndent,
indent + opts$extraIndent)
keepFirstIndent <<- TRUE
putw(strrep(" ", indent0),
frmt(paste0(DLlab),
justify="left", width=indent),
" ")
writeContent(block[[2L]], tag)
blankLine(0L)
indent <<- indent0
},
"\\value"=,
"\\arguments"= {
blankLine()
save <- startCapture()
dropBlank <<- TRUE
writeContent(block[[1L]], tag)
DLlab <- endCapture(save)
indent0 <- indent
opts <- Rd2txt_options()
indent <<- max(opts$minIndent, indent + opts$extraIndent)
keepFirstIndent <<- TRUE
putw(frmt(paste0(DLlab, ": "),
justify="right", width=indent))
writeContent(block[[2L]], tag)
blankLine(0L)
indent <<- indent0
},
"\\itemize" =,
"\\enumerate" = {
blankLine()
keepFirstIndent <<- TRUE
opts <- Rd2txt_options()
if (blocktag == "\\itemize")
label <- opts$itemBullet
else {
enumItem <<- enumItem + 1L
label <- opts$enumFormat(enumItem)
}
putw(frmt(label, justify="right",
width=indent))
})
itemskip <- TRUE
},
{ # default
if (itemskip) {
## The next item must be TEXT, and start with a space.
itemskip <- FALSE
if (tag == "TEXT") {
txt <- psub("^ ", "", as.character(tabExpand(block)))
put(txt)
} else writeBlock(block, tag, blocktag) # should not happen
} else writeBlock(block, tag, blocktag)
})
}
}
writeSection <- function(section, tag) {
if (tag %in% c("\\alias", "\\concept", "\\encoding", "\\keyword"))
return()
save <- c(indent, sectionLevel, keepFirstIndent, dropBlank, wrapping)
blankLine(min(sectionLevel, 1L))
titlePrefix <- strrep(" ", sectionLevel)
opts <- Rd2txt_options()
indent <<- opts$sectionIndent + opts$sectionExtra*sectionLevel
sectionLevel <<- sectionLevel + 1
keepFirstIndent <<- TRUE
if (tag == "\\section" || tag == "\\subsection") {
## section header could have markup
title <- .Rd_format_title(.Rd_get_text(section[[1L]]))
putf(titlePrefix, txt_header(title), ":")
blankLine()
dropBlank <<- TRUE
wrapping <<- TRUE
keepFirstIndent <<- FALSE
writeContent(section[[2L]], tag)
} else if (tag %in% c("\\usage", "\\examples")) {
putf(txt_header(sectionTitles[tag]), ":")
blankLine()
dropBlank <<- TRUE
wrapping <<- FALSE
keepFirstIndent <<- FALSE
writeCodeBlock(section, tag)
} else {
putf(txt_header(sectionTitles[tag]), ":")
blankLine()
dropBlank <<- TRUE
wrapping <<- TRUE
keepFirstIndent <<- FALSE
writeContent(section, tag)
}
blankLine()
indent <<- save[1L]
sectionLevel <<- save[2L]
keepFirstIndent <<- save[3L]
dropBlank <<- save[4L]
wrapping <<- save[5L]
}
if (is.character(out)) {
if(out == "") {
con <- stdout()
} else {
con <- file(out, "wt")
on.exit(close(con), add=TRUE)
}
} else {
con <- out
out <- summary(con)$description
}
Rd <- prepare_Rd(Rd, defines=defines, stages=stages, fragment=fragment, ...)
Rdfile <- attr(Rd, "Rdfile")
sections <- RdTags(Rd)
if (fragment) {
if (sections[1L] %in% names(sectionOrder))
for (i in seq_along(sections))
writeSection(Rd[[i]], sections[i])
else
for (i in seq_along(sections))
writeBlock(Rd[[i]], sections[i], "")
} else {
title <- .Rd_format_title(.Rd_get_title(Rd))
name <- trim(Rd[[2L]][[1L]])
if(nzchar(package)) {
left <- name
mid <- if(nzchar(package)) paste0("package:", package) else ""
right <- "R Documentation"
if(encoding != "unknown")
right <- paste0(right, "(", encoding, ")")
pad <- max(HDR_WIDTH - nchar(left, "w") - nchar(mid, "w") - nchar(right, "w"), 0)
pad0 <- pad %/% 2L
pad1 <- strrep(" ", pad0)
pad2 <- strrep(" ", pad - pad0)
putf(paste0(left, pad1, mid, pad2, right, "\n\n"))
}
putf(txt_header(title))
blankLine()
for (i in seq_along(sections)[-(1:2)])
writeSection(Rd[[i]], sections[i])
}
blankLine(0L)
invisible(out)
}