Search code examples
rdevtoolsroxygen2

Adding automatically/manually sections to package documentation


I have build my package and used devtools::build_manual() to build package documentation. The problem is that I have all my functions in section 'R topics documented' and I don't know how to change that. My goal is to have several sections without it that 'R topics documented'. I'm not sure how to do that, I tried to do @describeIn and @section but both didn't work. Do you have any idea how can I do that ?

UPGRADE vol 1 (After Abdessabour's code)

Hi I want to tell how much I appreciate your work, I know that you for sure spend a lot of time doing that. I get intuition of your code, it's brilliant. I have two questions about your code which I do not get in 100%.

(1) where in your code can I define section I want to have ? I understand intuition behind how they are being made but not quite understand where exactly in a code is place to made them. So for example functions (1) and (2) in section A functions (3) and (4) in section B and so on.

(2) How can I create a pdf after running your code. Should I just run devtools::build_manual() and that's all ?

UPGRADE vol 2

Hi! I understand your way of thinking but I have one problem which I want to show you. Let's say my package has name 'visualise'. That's the files within 'visualise' package folder.

enter image description here

visualise.R contains my functions within sections. I put that file into package folder (as you can see above)

enter image description here

I tried to import that file into R by @import, @importClassesFrom, @importFrom, @importMethodFrom but all of them don't work. I think R may do not recognize that file. I have concerns because after that creation visualise.R file I run build_manual(toc="anything") and in output I get default documentation (same one as created by build_manual()) Do you have any idea what I'm doing wrong ?

UPGRADE VOL 3

I've put visualise.R in R_HOME folder and in R_HOME/bin and it didn't work for both options. I put content of R_HOME/bin/Rd2pdf code below :

tex.mod <- function(pkg='.', file='Rd2.tex',  tocTi = "R{} topics documented:"){
  
  library(stringr)
  #get the package name
  pkg <- devtools::as.package(pkg)$package
  #reads the latex file
  out <- readLines(file)
  # modifies the Table of contents title
  out[rdcont] <- sub("(?<=\\\\Rdcontents\\{).+(?=}$)", tocTi, out[rdcont <- grep('\\\\Rdcontents', out)], perl=T)
  # Gets the defined sections
  sections <- str_replace_all(strcapture(paste0("(?s)^.+?\\\\HeaderA\\{",pkg,"\\}.+?(\\\\begin\\{Section\\}.+\\\\end{Section})\n\\\\inputencoding\\{.+$"), paste0(out, collapse='\n'), data.frame(v=character(0)), perl=T)[1,1],  c('\\\\begin\\{Section\\}\\{(.+?) *\\}\n'='\\1\n', '^\n'='', '\n$'='', '\n%?\n'='\n'))
  # splits sections
  spl <- str_split(sections, '\n?\\\\end.+?(\n|$)')[[1]]
  spl <- str_split(spl[nchar(spl)>0], '\n')
  
  #gets the package description
  pkgdesc <- out[1:rdcont]
  # gets the trailing lines
  trailing <- out[length(out)+(-1:0)]
  # get the descriptions
  outi<- str_split(paste0(out[(rdcont+2):(length(out)-2)], collapse='\n'), '\\\\inputencoding.+\n')[[1]]
  # sets the names to th name of the function/package described
  names(outi) <- str_extract(outi, '(?<=\\\\HeaderA\\{).+?(?=\\})')
  # adds lines to add to the toc file if you want more indentation put section instead of chapter
  outi <- c(outi, sapply(spl, function(x) {v<-paste0("\\myaddcontentsline{toc}{chapter}{\\protect\\textbf{", x[1],"}}"); names(v)<-x[1]; v})) 
  # unlists the names of the sections with their functions
  spl <- unlist(spl)
  # orders them so that the functions that have no section are shown first
  outi <- c(pkgdesc, outi[!(names(outi) %in% spl)], outi[spl], trailing)
  # writes to file
  if (require(data.table)){
    # the data.table way
    fwrite(list( outi), quote=F, sep='\n', file=file, append=F)
  } else {
    # the baseR way
    fileConn<-file(file, open='w')
    writeLines(outi, fileConn)
    close(fileConn)
  }
  
}
..Rd2pdf <- function (args = NULL, quit = TRUE) 
{
    do_cleanup <- function() {
        if (clean) {
            setwd(startdir)
            unlink(build_dir, recursive = TRUE)
        }
        else {
            cat("You may want to clean up by 'rm -Rf ", build_dir, 
                "'\n", sep = "")
        }
    }
    Usage <- function() {
        cat("Usage: R CMD Rd2pdf [options] files", "", "Generate PDF output from the Rd sources specified by files, by", 
            "either giving the paths to the files, or the path to a directory with", 
            "the sources of a package, or an installed package.", 
            "", "Unless specified via option '--output', the basename of the output file", 
            "equals the basename of argument 'files' if this specifies a package", 
            "or a single file, and 'Rd2' otherwise.", "", "The Rd sources are assumed to be ASCII unless they contain \\encoding", 
            "declarations (which take priority) or --encoding is supplied or if using", 
            "package sources, if the package DESCRIPTION file has an Encoding field.", 
            "The output encoding defaults to the package encoding then to 'UTF-8'.", 
            "", "Files are listed in the order given: for a package they are in alphabetic", 
            "order of the \\name sections.", "", "Options:", 
            "  -h, --help\t\tprint short help message and exit", 
            "  -v, --version\t\tprint version info and exit", 
            "      --batch\t\tno interaction", "      --no-clean\tdo not remove created temporary files", 
            "      --no-preview\tdo not preview generated PDF file", 
            "      --encoding=enc    use 'enc' as the default input encoding", 
            "      --outputEncoding=outenc", "                        use 'outenc' as the default output encoding", 
            "      --os=NAME\t\tuse OS subdir 'NAME' (unix or windows)", 
            "      --OS=NAME\t\tthe same as '--os'", "  -o, --output=FILE\twrite output to FILE", 
            "      --force\t\toverwrite output file if it exists", 
            "      --title=NAME\tuse NAME as the title of the document", 
            "      --toc=NAME   use NAME as the title of the Table of contents", 
            "      --no-index\tdo not index output", "      --no-description\tdo not typeset the description of a package", 
            "      --internals\ttypeset 'internal' documentation (usually skipped)", 
            "      --build_dir=DIR\tuse DIR as the working directory", 
            "      --RdMacros=pkglist", "             \t\tpackages from which to get Rd macros", 
            "", "The output papersize is set by the environment variable R_PAPERSIZE.", 
            "The PDF previewer is set by the environment variable R_PDFVIEWER.", 
            "", "Report bugs at <https://bugs.R-project.org>.", 
            sep = "\n")
    }
    options(showErrorCalls = FALSE, warn = 1)
    if (is.null(args)) {
        args <- commandArgs(TRUE)
        args <- paste(args, collapse = " ")
        args <- strsplit(args, "nextArg", fixed = TRUE)[[1L]][-1L]
    }
    startdir <- getwd()
    if (is.null(startdir)) 
        stop("current working directory cannot be ascertained")
    build_dir <- paste0(".Rd2pdf", Sys.getpid())
    title <- ""
    tocTi <- ""
    batch <- FALSE
    clean <- TRUE
    only_meta <- FALSE
    out_ext <- "pdf"
    output <- ""
    enc <- "unknown"
    outenc <- "latin1"
    index <- TRUE
    description <- TRUE
    internals <- FALSE
    files <- character()
    dir <- ""
    force <- FALSE
    pkglist <- NULL
    WINDOWS <- .Platform$OS.type == "windows"
    preview <- Sys.getenv("R_PDFVIEWER", if (WINDOWS) 
        "open"
    else "false")
    OSdir <- if (WINDOWS) 
        "windows"
    else "unix"
    while (length(args)) {
        a <- args[1L]
        if (a %in% c("-h", "--help")) {
            Usage()
            q("no", runLast = FALSE)
        }
        else if (a %in% c("-v", "--version")) {
            cat("Rd2pdf: ", R.version[["major"]], ".", R.version[["minor"]], 
                " (r", R.version[["svn rev"]], ")\n", sep = "")
            cat("", "Copyright (C) 2000-2011 The R Core Team.", 
                "This is free software; see the GNU General Public License version 2", 
                "or later for copying conditions.  There is NO warranty.", 
                sep = "\n")
            q("no", runLast = FALSE)
        }
        else if (a == "--batch") {
            batch <- TRUE
        }
        else if (a == "--no-clean") {
            clean <- FALSE
        }
        else if (a == "--no-preview") {
            preview <- "false"
        }
        else if (a == "--pdf") {
        }
        else if (substr(a, 1, 8) == "--title=") {
            title <- substr(a, 9, 1000)
        }
        else if (substr(a, 1, 6) == "--toc=") {
            tocTi <- sub("--toc=", "", a)
        }
        else if (a == "-o") {
            if (length(args) >= 2L) {
                output <- args[2L]
                args <- args[-1L]
            }
            else stop("-o option without value", call. = FALSE)
        }
        else if (substr(a, 1, 9) == "--output=") {
            output <- substr(a, 10, 1000)
        }
        else if (a == "--force") {
            force <- TRUE
        }
        else if (a == "--only-meta") {
            only_meta <- TRUE
        }
        else if (substr(a, 1, 5) %in% c("--os=", "--OS=")) {
            OSdir <- substr(a, 6, 1000)
        }
        else if (substr(a, 1, 11) == "--encoding=") {
            enc <- substr(a, 12, 1000)
        }
        else if (substr(a, 1, 17) == "--outputEncoding=") {
            outenc <- substr(a, 18, 1000)
        }
        else if (substr(a, 1, 12) == "--build-dir=") {
            build_dir <- substr(a, 13, 1000)
        }
        else if (a == "--no-index") {
            index <- FALSE
        }
        else if (a == "--no-description") {
            description <- FALSE
        }
        else if (a == "--internals") {
            internals <- TRUE
        }
        else if (substr(a, 1, 11) == "--RdMacros=") {
            pkglist <- substr(a, 12, 1000)
        }
        else if (startsWith(a, "-")) {
            message("Warning: unknown option ", sQuote(a))
        }
        else files <- c(files, a)
        args <- args[-1L]
    }
    if (!length(files)) {
        message("no inputs")
        q("no", status = 1L, runLast = FALSE)
    }
    if (WINDOWS) 
        files[1L] <- sub("[\\/]$", "", files[1L])
    if (dir.exists(files[1L])) {
        if (file.exists(file.path(files[1L], "DESCRIPTION"))) {
            cat("Hmm ... looks like a package\n")
            dir <- files[1L]
            if (!nzchar(output)) 
                output <- paste(basename(dir), out_ext, sep = ".")
        }
        else if (file.exists(f <- file.path(files[1L], "DESCRIPTION.in")) && 
            any(grepl("^Priority: *base", readLines(f)))) {
            cat("Hmm ... looks like a package from the R distribution\n")
            dir <- files[1L]
            if (!nzchar(output)) 
                output <- paste(basename(dir), out_ext, sep = ".")
            if (index && basename(dir) == "base") {
                index <- FALSE
                cat("_not_ indexing 'base' package\n")
            }
        }
        else {
            dir <- if (dir.exists(d <- file.path(files[1L], "man"))) 
                d
            else files[1L]
        }
    }
    else {
        if (length(files) == 1L && !nzchar(output)) 
            output <- paste(sub("[.][Rr]d$", "", basename(files)), 
                out_ext, sep = ".")
    }
    if (!nzchar(dir)) 
        dir <- paste(files, collapse = " ")
    if (dir.exists(build_dir) && unlink(build_dir, recursive = TRUE)) {
        cat("cannot write to build dir\n")
        q("no", status = 2L, runLast = FALSE)
    }
    dir.create(build_dir, FALSE)
    if (!nzchar(output)) 
        output <- paste0("Rd2.", out_ext)
    if (file.exists(output) && !force) {
        cat("file", sQuote(output), "exists; please remove it first\n")
        q("no", status = 1L, runLast = FALSE)
    }
    res <- try(tools:::.Rd2pdf(files[1L], file.path(build_dir, "Rd2.tex"), 
        title, batch, description, only_meta, enc, outenc, dir, 
        OSdir, internals, index, pkglist))
    if (inherits(res, "try-error")) 
        q("no", status = 11L, runLast = FALSE)
    if (!batch) 
        cat("Creating", out_ext, "output from LaTeX ...\n")
    setwd(build_dir)
    tocTi <- if(nchar(tocTi)>0) tocTi else "\\R{} topics documented:"
    tex.mod(pkg=files[1L], file="Rd2.tex", tocTi=tocTi)
    res <- try(tools:::texi2pdf("Rd2.tex", quiet = FALSE, index = index))
    if (inherits(res, "try-error")) {
        res <- try(tools:::texi2pdf("Rd2.tex", quiet = FALSE, index = index))
        if (inherits(res, "try-error")) {
            message("Error in running tools::tools:::texi2pdf()")
            do_cleanup()
            q("no", status = 1L, runLast = FALSE)
        }
    }
    setwd(startdir)
    cat("Saving output to", sQuote(output), "...\n")
    file.copy(file.path(build_dir, paste0("Rd2.", out_ext)), 
        output, overwrite = force)
    cat("Done\n")
    do_cleanup()
    if (preview != "false") 
        system(paste(preview, output))
    if (quit) 
        q("no", runLast = FALSE)
}
..Rd2pdf()

UPGRADE vol 4

I'm using code following :

# modifies the latex of the converted .Rd files
# in order to add sections to the toc
tex.mod <- function(pkg='.', file='Rd2.tex',  tocTi = "R{} topics documented:"){
  
  library(stringr)
  #get the package name
  pkg <- devtools::as.package(pkg)$package
  #reads the latex file
  out <- readLines(file)
  # modifies the Table of contents title
  out[rdcont] <- sub("(?<=\\\\Rdcontents\\{).+(?=}$)", tocTi, out[rdcont <- grep('\\\\Rdcontents', out)], perl=T)
  # Gets the defined sections
  sections <- str_replace_all(strcapture(paste0("(?s)^.+?\\\\HeaderA\\{",pkg,"\\}.+?(\\\\begin\\{Section\\}.+\\\\end{Section})\n(?:\\\\inputencoding\\{|\n?\\\\printindex).+$"), paste0(out, collapse='\n'), data.frame(v=character(0)), perl=T)[1,1],  c('\\\\begin\\{Section\\}\\{(.+?) *\\}\n'='\\1\n', '^\n'='', '\n$'='', '\n%?\n'='\n'))
  
  # splits sections
  spl <- str_split(sections, '\n?\\\\end.+?(\n|$)')[[1]]
  spl <- str_split(spl[nchar(spl)>0], '\n')
  
  #gets the package description
  pkgdesc <- out[1:rdcont]
  # gets the trailing lines
  trailing <- out[length(out)+(-1:0)]
  # get the descriptions
  outi<- str_split(paste0(out[(rdcont+2):(length(out)-2)], collapse='\n'), '\\\\inputencoding.+\n')[[1]]
  # sets the names to th name of the function/package described
  names(outi) <- str_extract(outi, '(?<=\\\\HeaderA\\{).+?(?=\\})')
  # adds lines to add to the toc file if you want more indentation put section instead of chapter
  outi <- c(outi, sapply(spl, function(x) {v <- paste0("\\chapter{", x[1],"}", '\n', "\\myaddcontentsline{toc}{chapter}{\\protect\\textbf{", x[1],"}}"); names(v)<-x[1]; v})) 
  # unlists the names of the sections with their functions
  spl <- unlist(spl)
  # orders them so that the functions that have no section are shown first
  outi <- c(pkgdesc, "\\renewcommand\\thesection{}", outi[!(names(outi) %in% c(spl, pkg))], outi[spl], trailing)
  # writes to file
  if (require(data.table)){
    # the data.table way
    fwrite(list( outi), quote=F, sep='\n', file=file, append=F)
  } else {
    # the baseR way
    fileConn<-file(file, open='w')
    writeLines(outi, fileConn)
    close(fileConn)
  }
  
}

write.fun <- function(fun, path){
  fun.src <- capture.output(fun)
  fun.name <- as.character(substitute(fun))
  fun.src[1] <- paste0(fun.name[length(fun.name)],' <- ', fun.src[1])
  fun.src<-fun.src[!grepl('^<.+?>$',fun.src)]
  if (require(data.table)){
    # the data.table way
    fwrite(list(fun.src), quote=F, sep='\n', file=path, append=T)
  } else {
    # the baseR way
    fileConn<-file(path, open='a')
    writeLines(fun.src, fileConn)
    close(fileConn)
  }
}
# get the location of R_HOME
Rh <- Sys.getenv('R_HOME')
f <- file.path(Rh, 'bin', 'Rd2pdf.overload.R')
file.create(f)

# adds the function to the file
write.fun(tex.mod,f)
write.fun(tools:::..Rd2pdf,f)

# reads the file
out <- readLines(f)
# forces all functions to be loaded from tools
out <- gsub('(?=(.DESCRIPTION_to_latex|.Rdfiles2tex|latex_canonical_encoding|texi2pdf|.Rd2pdf)\\()', "tools:::", out, perl=T)
# add param description to usage
out[i] <- paste(out[i <- grep('--title=NAME', out, fixed=T)], '            "      --toc=NAME\tuse NAME as the title of the Table of contents", ', sep='\n' )
# parses param
out[grep('substr(a, 1, 8) == "--title="', out, fixed=T)+2] <- paste('        }', '        else if (substr(a, 1, 6) == "--toc=") {', '            tocTi <- sub("--toc=", "", a)', '        }', sep='\n')
# adds definition
out[grep('    title <- ""', out, fixed=T)] <- '    title <- ""\n    tocTi <- ""'
# adds call to tex.mod to force modification
out[i] <- paste0(out[i<-grep('setwd\\(build_dir\\)', out)], '\n\ttocTi <- if(nchar(tocTi)>0) tocTi else "\\\\R{} topics documented:"\n\ttex.mod(pkg=files[1L], file="Rd2.tex", tocTi=tocTi)')
# adds call to ..Rd2pdf at the end
out <- c(out, '..Rd2pdf()')
# rewrites the file
fileConn<-file(f, open='w')
writeLines(out, fileConn)
close(fileConn)

# overloading devtools::build_manual
build_manual <- function (pkg = ".", path = NULL, toc=NULL) 
{
  pkg <- devtools::as.package(pkg)
  path <- rlang::`%||%`(path, dirname(pkg$path))
  
  name <- paste0(pkg$package, "_", pkg$version, ".pdf", collapse = " ")
  tryCatch(msg <- callr::rscript(file.path(Sys.getenv("R_HOME"), "bin", "Rd2pdf.overload.R"), cmdargs = paste0('nextArg', paste0(c("--force", 
                                                                                                                                   paste0("--output=", path, "/", name), pkg$path, if(!is.null(toc)) paste0("--toc=",toc)), collapse='nextArg')), fail_on_status = TRUE), 
           error = function(e) {
             cat(e$stderr)
             stop("Failed to build manual", call. = FALSE)
           })
  cat(msg$stdout)
  invisible(msg)
}


write.fun(build_manual, file.path(path.expand('~'), '.Rprofile'))

build_manual(toc="Functions")

with output :

enter image description here

As you can see "Section one" and "Section two" is mentioned two times, embolden and not embolden. How can I have only embolden one ?


Solution

  • I have written an Rscript that'll take care of all the edits to be made.

    Explanation of the solution

    Basically the devtools::build_manual calls the R CMD Rd2pdf script and this script basically bundles the args to call tools:::..Rd2pdf.

    So what I did was overload the tools:::..Rd2pdf function and add a --toc param to change the toc title, and add another function tex.mod that'll modify the latex before it's rendered these two function are saved to ${R_HOME}/bin/Rd2pdf.overload.R so they could be used by the modified Rd2pdf, modify the Rd2pdf script basically execute Rd2pdf.overload.R instead of running tools:::Rd2pdf() and overload the devtools::build_manual to accept a toc param that'll be passed to the Rd2pdf script.

    tex.mod reorders the descriptions and adds the sections to the toc using myaddcontentsline macro defined in the Rd latex package which is a wrapper to the latex macro addcontentsline.

    Note: the functions that are not contained in the sections are shown first.

    How sections will be defined

    Section will be defined by documenting the package.
    ie create an R file with the name ur.pkg.name.R and add the description of the package like this:

    #' name.of.ur.pkg : A package for doing awesome things.
    #'
    #' The name.of.ur.pkg package provides two categories of important functions:
    #' foo and baz.
    #' 
    #' @section Foo functions:
    #' first.fun
    #' second.fun
    #' 
    #' @section baz functions:
    #' third.fun
    #' fourth.fun
    #' 
    #' @docType package
    #' @name name.of.ur.pkg
    NULL
    

    the Rscript:

    # modifies the latex of the converted .Rd files
    # in order to add sections to the toc
    tex.mod <- function(pkg='.', file='Rd2.tex',  tocTi = "R{} topics documented:"){
        
        library(stringr)
        #get the package name
        pkg <- devtools::as.package(pkg)$package
        #reads the latex file
        out <- readLines(file)
        # modifies the Table of contents title
        out[rdcont] <- sub("(?<=\\\\Rdcontents\\{).+(?=}$)", tocTi, out[rdcont <- grep('\\\\Rdcontents', out)], perl=T)
        # Gets the defined sections
        sections <- str_replace_all(strcapture(paste0("(?s)^.+?\\\\HeaderA\\{",pkg,"\\}.+?(\\\\begin\\{Section\\}.+\\\\end{Section})\n(?:\\\\inputencoding\\{|\n?\\\\printindex).+$"), paste0(out, collapse='\n'), data.frame(v=character(0)), perl=T)[1,1],  c('\\\\begin\\{Section\\}\\{(.+?) *\\}\n'='\\1\n', '^\n'='', '\n$'='', '\n%?\n'='\n'))
        
        # splits sections
        spl <- str_split(sections, '\n?\\\\end.+?(\n|$)')[[1]]
        spl <- str_split(spl[nchar(spl)>0], '\n')
        
        #gets the package description
        pkgdesc <- out[1:rdcont]
        # gets the trailing lines
        trailing <- out[length(out)+(-1:0)]
        # get the descriptions
        outi<- str_split(paste0(out[(rdcont+2):(length(out)-2)], collapse='\n'), '\\\\inputencoding.+\n')[[1]]
        # sets the names to th name of the function/package described
        names(outi) <- str_extract(outi, '(?<=\\\\HeaderA\\{).+?(?=\\})')
        # adds lines to add to the toc file if you want more indentation put section instead of chapter
        outi <- c(outi, sapply(spl, function(x) {v<-paste0("\\myaddcontentsline{toc}{chapter}{\\protect\\textbf{", x[1],"}}"); names(v)<-x[1]; v})) 
        # unlists the names of the sections with their functions
        spl <- unlist(spl)
        # orders them so that the functions that have no section are shown first
        outi <- c(pkgdesc, outi[!(names(outi) %in% c(spl, pkg))], outi[spl], trailing)
        # writes to file
        if (require(data.table)){
            # the data.table way
            fwrite(list( outi), quote=F, sep='\n', file=file, append=F)
        } else {
            # the baseR way
            fileConn<-file(file, open='w')
            writeLines(outi, fileConn)
            close(fileConn)
        }
        
    }
    
    write.fun <- function(fun, path){
        fun.src <- capture.output(fun)
        fun.name <- as.character(substitute(fun))
        fun.src[1] <- paste0(fun.name[length(fun.name)],' <- ', fun.src[1])
        fun.src<-fun.src[!grepl('^<.+?>$',fun.src)]
        if (require(data.table)){
            # the data.table way
            fwrite(list(fun.src), quote=F, sep='\n', file=path, append=T)
        } else {
            # the baseR way
            fileConn<-file(path, open='a')
            writeLines(fun.src, fileConn)
            close(fileConn)
        }
    }
    # get the location of R_HOME
    Rh <- Sys.getenv('R_HOME')
    f <- file.path(Rh, 'bin', 'Rd2pdf.overload.R')
    file.create(f)
    
    # adds the function to the file
    write.fun(tex.mod,f)
    write.fun(tools:::..Rd2pdf,f)
    
    # reads the file
    out <- readLines(f)
    # forces all functions to be loaded from tools
    out <- gsub('(?=(.DESCRIPTION_to_latex|.Rdfiles2tex|latex_canonical_encoding|texi2pdf|.Rd2pdf)\\()', "tools:::", out, perl=T)
    # add param description to usage
    out[i] <- paste(out[i <- grep('--title=NAME', out, fixed=T)], '            "      --toc=NAME\tuse NAME as the title of the Table of contents", ', sep='\n' )
    # parses param
    out[grep('substr(a, 1, 8) == "--title="', out, fixed=T)+2] <- paste('        }', '        else if (substr(a, 1, 6) == "--toc=") {', '            tocTi <- sub("--toc=", "", a)', '        }', sep='\n')
    # adds definition
    out[grep('    title <- ""', out, fixed=T)] <- '    title <- ""\n    tocTi <- ""'
    # adds call to tex.mod to force modification
    out[i] <- paste0(out[i<-grep('setwd\\(build_dir\\)', out)], '\n\ttocTi <- if(nchar(tocTi)>0) tocTi else "\\\\R{} topics documented:"\n\ttex.mod(pkg=files[1L], file="Rd2.tex", tocTi=tocTi)')
    # adds call to ..Rd2pdf at the end
    out <- c(out, '..Rd2pdf()')
    # rewrites the file
    fileConn<-file(f, open='w')
    writeLines(out, fileConn)
    close(fileConn)
    
    # overloading devtools::build_manual
    build_manual <- function (pkg = ".", path = NULL, toc=NULL) 
    {
        pkg <- devtools::as.package(pkg)
        path <- rlang::`%||%`(path, dirname(pkg$path))
    
        name <- paste0(pkg$package, "_", pkg$version, ".pdf", collapse = " ")
        tryCatch(msg <- callr::rscript(file.path(Sys.getenv("R_HOME"), "bin", "Rd2pdf.overload.R"), cmdargs = paste0('nextArg', paste0(c("--force", 
            paste0("--output=", path, "/", name), pkg$path, if(!is.null(toc)) paste0("--toc=",toc)), collapse='nextArg')), fail_on_status = TRUE), 
            error = function(e) {
                cat(e$stderr)
                stop("Failed to build manual", call. = FALSE)
            })
        cat(msg$stdout)
        invisible(msg)
    }
    
    
    write.fun(build_manual, file.path(path.expand('~'), '.Rprofile'))
    

    It automatically adds the new definition of build_manual to ~/.Rprofile.

    After running devtools::document() you can

    • Now to build your manual run build_manual()
    • Now to build your manual with a custom Table of contents title pass a toc argument to the call