From 5e33043a16299e125dfcecc3af78c2a4e0952d59 Mon Sep 17 00:00:00 2001 From: Falk Benke Date: Thu, 9 Nov 2023 16:30:01 +0100 Subject: [PATCH 01/16] add support for extrapage attribute --- R/createListModularCode.R | 54 ++++++++++++- R/extractDocumentation.R | 166 +++++++++++++++++++++++--------------- R/mergeDocumentation.R | 10 ++- 3 files changed, 156 insertions(+), 74 deletions(-) diff --git a/R/createListModularCode.R b/R/createListModularCode.R index c18f764..656f6ee 100644 --- a/R/createListModularCode.R +++ b/R/createListModularCode.R @@ -156,8 +156,16 @@ createListModularCode <- function(cc, interfaces, path = ".", citation = NULL, u outSub <- list() outSub$realizations <- list() files <- list.files(path = "core", pattern = "\\.gms") - paths <- paste0("core/", files) - outSub$realizations[["core"]] <- extractDocumentation(paths, start_type = "equations") + paths <- file.path("core", files) + + documentation <- extractDocumentation(paths, start_type = "equations") + + # move extrapage items to toplevel + outSub$extrapage <- append(outSub$extrapage, documentation$extrapage) + documentation$extrapage <- NULL + + outSub$realizations[["core"]] <- documentation + } else { rea <- strsplit(cc$modulesInfo[m, "realizations"], ",")[[1]] folder <- cc$modulesInfo[m, "folder"] @@ -177,7 +185,14 @@ createListModularCode <- function(cc, interfaces, path = ".", citation = NULL, u # but ordering it based on the order of mention in realization.gms. Not # mentioned files will be added at the end. paths <- union(intersect(mentionedPaths, existingPaths), existingPaths) - outSub$realizations[[r]] <- extractDocumentation(paths, start_type = "equations") + + documentation <- extractDocumentation(paths, start_type = "equations") + + # move extrapage items to toplevel + outSub$extrapage <- append(outSub$extrapage, documentation$extrapage) + documentation$extrapage <- NULL + + outSub$realizations[[r]] <- documentation } } return(outSub) @@ -191,11 +206,30 @@ createListModularCode <- function(cc, interfaces, path = ".", citation = NULL, u return(seealso) } + sortExtraPages <- function(extraPage) { + out <- list() + attrPattern <- "^(\\w+)=\"(\\w+)\"$" + + for (i in seq(extraPage)) { + content <- extraPage[[i]][-1] + page <- sub(attrPattern, "\\2", extraPage[[i]][1]) + type <- sub("-\\w+", "", names(extraPage[i])) + l <- list(content) + names(l) <- type + out[[page]] <- append(out[[page]], l) + } + return(out) + } + out <- collectTables(cc, unitPattern) # write doc files full <- list() + data <- extractDocumentation(mainfile) + extraPage <- data$extrapage + data$extrapage <- NULL + data$citation <- citation full[["index"]] <- createIndexPage(data) @@ -205,11 +239,23 @@ createListModularCode <- function(cc, interfaces, path = ".", citation = NULL, u } else { mLoop <- setdiff(sort(names(out)), "core") } + for (m in mLoop) { - data <- append(out[[m]], collectRealizations(m, cc)) + realizations <- collectRealizations(m, cc) + extraPage <- append(extraPage, realizations$extrapage) + realizations$extrapage <- NULL + data <- append(out[[m]], realizations) data$name <- m data$seealso <- collectSeealso(interfaces[[m]], m, cc$modulesInfo) full[[m]] <- createModulePage(data, docfolder = docfolder) } + + extraPage <- sortExtraPages(extraPage) + for (i in names(extraPage)) { + data <- mergeDocumentation(extraPage[[i]]) + data$name <- i + full[[i]] <- createModulePage(data, docfolder = docfolder) + } + return(full) } diff --git a/R/extractDocumentation.R b/R/extractDocumentation.R index 5f9b7f2..dc4a0a6 100644 --- a/R/extractDocumentation.R +++ b/R/extractDocumentation.R @@ -1,112 +1,146 @@ #' extractDocumentation -#' +#' #' Extracts doxygen-like GAMS documentation. Entries are introduced with an @type at the beginning #' of the line. In case of @realization also GAMS code is read and interpreted, in all other cases -#' only the specific documentation comment is evaluated. -#' +#' only the specific documentation comment is evaluated. +#' #' @param path path to the file(s) which should be evaluated #' @param start_type set type for first line of code. This can be useful #' to extract documentation even if no documentation type has been set (e.g #' reading equations.gms as type realization) #' @param comment comment chars used for documentation comments -#' @return a list of documentation pieces with type as name of each element +#' @return a list of documentation pieces with type as name of each element #' @author Jan Philipp Dietrich #' @importFrom stringi stri_extract_all_regex stri_replace_all_regex #' @seealso \code{\link{goxygen}} -#' @examples -#' mainfile <- paste0(system.file("dummymodel",package="gms"),"/main.gms") -#' calcfile <- paste0(system.file("dummymodel",package="gms"), +#' @examples +#' mainfile <- paste0(system.file("dummymodel", package = "gms"), "/main.gms") +#' calcfile <- paste0(system.file("dummymodel", package = "gms"), #' "/modules/02_crazymodule/complex/calculations.gms") -#' # extracting information from the main file of the model +#' # extracting information from the main file of the model #' extractDocumentation(mainfile) #' # extracting information from a file with some equations in it #' extractDocumentation(calcfile) -#' +#' #' @export -extractDocumentation <- function(path, start_type=NULL, comment="*'") { - - if(length(path)>1) { +extractDocumentation <- function(path, start_type = NULL, comment = "*'") { # nolint + + if (length(path) > 1) { out <- list() - for(p in path) { - out <- append(out,extractDocumentation(p,start_type=start_type, comment=comment)) + extraPage <- list() + + for (p in path) { + documentation <- extractDocumentation(p, start_type = start_type, comment = comment) + extraPage <- append(extraPage, documentation$extrapage) + documentation$extrapage <- NULL + out <- append(out, documentation) } - return(mergeDocumentation(out)) + + out <- mergeDocumentation(out) + out$extrapage <- extraPage + + return(out) } - + escapeRegex <- function(x) { return(gsub("([.|()\\^{}+$*?]|\\[|\\])", "\\\\\\1", x)) } - - removeComments <- function(x,comment) { - return(grep(paste0("^(",escapeRegex(comment),"|[^*])"),x, value=TRUE)) + + removeComments <- function(x, comment) { + return(grep(paste0("^(", escapeRegex(comment), "|[^*])"), x, value = TRUE)) } - - extract_block <- function(x,comment) { + + extractBlock <- function(x, comment) { code <- "@(\\w*) ?(.*)$" - pattern <- paste0("^(",escapeRegex(comment),") *",code) - type <- sub(pattern,"\\2",x[1]) - if(type=="equations") { - x[1] <- sub(pattern,"\\1 \\3",x[1]) - x <- paste(x,collapse="\n") + pattern <- paste0("^(", escapeRegex(comment), ") *", code) + type <- sub(pattern, "\\2", x[1]) + attribute <- sub(pattern, "\\3", x[1]) + + if (type == "equations") { + x[1] <- sub(pattern, "\\1 \\3", x[1]) + x <- paste(x, collapse = "\n") equation <- "(^|\n)[^\n]*?[ \n\t]*\\.\\.([^.]|\n)(.|\n)*?;" - eq <- stri_extract_all_regex(x,equation)[[1]] - if(length(eq)==1 && is.na(eq)) { + eq <- stri_extract_all_regex(x, equation)[[1]] + if (length(eq) == 1 && is.na(eq)) { eq <- NULL } else { eq <- gamsequation2tex(eq) } - x <- stri_replace_all_regex(x,equation,paste(comment,"\n",comment,"#::.equation.::#","\n",comment,"\n")) - x <- stri_extract_all_regex(x,paste0(escapeRegex(comment),".*(\\n|$)"))[[1]] - x <- gsub(paste0("(\n|",escapeRegex(comment)," *)"),"",x) - + x <- stri_replace_all_regex(x, equation, paste(comment, "\n", comment, "#::.equation.::#", "\n", comment, "\n")) + x <- stri_extract_all_regex(x, paste0(escapeRegex(comment), ".*(\\n|$)"))[[1]] + x <- gsub(paste0("(\n|", escapeRegex(comment), " *)"), "", x) + # fill in equations - for(i in names(eq)) { - delim <- ifelse(grepl("CONVERSION FAILED!",i,fixed = TRUE), "```","") - x[grep("#::.equation.::#",x)[1]] <- paste0(delim,"\n",eq[i],"\n",delim) + for (i in names(eq)) { + delim <- ifelse(grepl("CONVERSION FAILED!", i, fixed = TRUE), "```", "") + x[grep("#::.equation.::#", x)[1]] <- paste0(delim, "\n", eq[i], "\n", delim) } type <- "description" - } else if(type=="code") { - com <- grepl(paste0("^",escapeRegex(comment)," *"),x) - x[!com] <- paste0("```\n",x[!com],"\n```") - x[com] <- sub(paste0("^",escapeRegex(comment)," *"),"",x[com]) - x[1] <- sub(code,"\\2",x[1]) - x <- paste(x,collapse="\n") - x <- gsub("\n```\n```\n","\n",x,fixed=TRUE) - x <- strsplit(x,"\n")[[1]] + } else if (type == "code") { + com <- grepl(paste0("^", escapeRegex(comment), " *"), x) + x[!com] <- paste0("```\n", x[!com], "\n```") + x[com] <- sub(paste0("^", escapeRegex(comment), " *"), "", x[com]) + x[1] <- sub(code, "\\2", x[1]) + x <- paste(x, collapse = "\n") + x <- gsub("\n```\n```\n", "\n", x, fixed = TRUE) + x <- strsplit(x, "\n")[[1]] type <- "description" - } else if(type=="stop") { + } else if (type == "stop") { return(NULL) } else { - x <- grep(paste0("^",escapeRegex(comment)," *"), x, value=TRUE) - x <- sub(paste0("^",escapeRegex(comment)," *"),"",x) - x[1] <- sub(code,"\\2",x[1]) + x <- grep(paste0("^", escapeRegex(comment), " *"), x, value = TRUE) + x <- sub(paste0("^", escapeRegex(comment), " *"), "", x) + x[1] <- sub(code, "\\2", x[1]) } - - while(length(x)>1 & x[1]=="") x <- x[-1] - while(length(x)>1 & tail(x,1)=="") x <- x[-length(x)] - if(length(x)==1) if(is.na(x) | x=="") return(NULL) - if(type=="description") x <- c(x,"") + + while (length(x) > 1 && x[1] == "") x <- x[-1] + while (length(x) > 1 && tail(x, 1) == "") x <- x[-length(x)] + if (length(x) == 1) if (is.na(x) || x == "") return(NULL) + if (type == "description") x <- c(x, "") out <- list() + + # determine if 'extrapage' attribute is set + attrPattern <- "^(\\w+)=\"(\\w+)\"$" + if (grepl(attrPattern, attribute)) { + attrName <- sub(attrPattern, "\\1", attribute) + if (attrName == "extrapage") { + type <- paste0("extrapage-", type) + } + } + out[[type]] <- x return(out) } - - if(!file.exists(path)) return(list()) + + if (!file.exists(path)) return(list()) x <- readLines(path, warn = FALSE) - x <- removeComments(x,comment) - if(!is.null(start_type)) { - x <- c(paste0(comment," @",start_type," "),x) + x <- removeComments(x, comment) + if (!is.null(start_type)) { + x <- c(paste0(comment, " @", start_type, " "), x) } - - blocks_start <- suppressWarnings(grep(paste0("^",escapeRegex(comment)," @[a-z]*( |$)"),x)) - if(length(blocks_start)==0) return(list()) - - blocks_end <- c(blocks_start[-1]-1,length(x)) - + + blocksStart <- suppressWarnings(grep(paste0("^", escapeRegex(comment), " @[a-z]*( |$)"), x)) + if (length(blocksStart) == 0) return(list()) + + blocksEnd <- c(blocksStart[-1] - 1, length(x)) + blocks <- list() - for(i in 1:length(blocks_start)) { - blocks <- c(blocks,extract_block(x[blocks_start[i]:blocks_end[i]], comment)) + for (i in seq_along(blocksStart)) { + blocks <- c(blocks, extractBlock(x[blocksStart[i]:blocksEnd[i]], comment)) } - return(mergeDocumentation(blocks)) + + blocks <- mergeDocumentation(blocks) + + # extract and move extrapages to top-level list + extraPages <- blocks[grepl("^extrapage-", names(blocks))] + names(extraPages) <- sub("^extrapage-", "", names(extraPages)) + + blocks <- append( + blocks[!grepl("^extrapage-", names(blocks))], + list("extrapage" = extraPages) + ) + + return(blocks) + } diff --git a/R/mergeDocumentation.R b/R/mergeDocumentation.R index fa7be1e..0330919 100644 --- a/R/mergeDocumentation.R +++ b/R/mergeDocumentation.R @@ -1,8 +1,10 @@ mergeDocumentation <- function(x) { - if(!anyDuplicated(names(x))) return(x) + if (!anyDuplicated(names(x))) { + return(x) + } out <- list() - for(i in unique(names(x))) { - out[[i]] <- unlist(x[names(x)==i], use.names=FALSE) + for (i in unique(names(x))) { + out[[i]] <- unlist(x[names(x) == i], use.names = FALSE) } return(out) -} \ No newline at end of file +} From 2400313be86dbf235aa2ebeadc66bfa57a8073fa Mon Sep 17 00:00:00 2001 From: Falk Benke Date: Tue, 14 Nov 2023 17:58:46 +0100 Subject: [PATCH 02/16] add documentation for extrapage --- R/createListModularCode.R | 1 + R/extractDocumentation.R | 6 ++++-- vignettes/goxygen.Rmd | 15 +++++++++++++-- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/R/createListModularCode.R b/R/createListModularCode.R index 656f6ee..e249eb8 100644 --- a/R/createListModularCode.R +++ b/R/createListModularCode.R @@ -250,6 +250,7 @@ createListModularCode <- function(cc, interfaces, path = ".", citation = NULL, u full[[m]] <- createModulePage(data, docfolder = docfolder) } + #browser() extraPage <- sortExtraPages(extraPage) for (i in names(extraPage)) { data <- mergeDocumentation(extraPage[[i]]) diff --git a/R/extractDocumentation.R b/R/extractDocumentation.R index dc4a0a6..5d83e74 100644 --- a/R/extractDocumentation.R +++ b/R/extractDocumentation.R @@ -100,10 +100,12 @@ extractDocumentation <- function(path, start_type = NULL, comment = "*'") { # no if (type == "description") x <- c(x, "") out <- list() - # determine if 'extrapage' attribute is set - attrPattern <- "^(\\w+)=\"(\\w+)\"$" + # mark blocks with 'extrapage' attribute + attrPattern <- "^(\\w+)=\"(\\w+)\"[ \t]*$" if (grepl(attrPattern, attribute)) { attrName <- sub(attrPattern, "\\1", attribute) + # remove trailing whitespaces and tabs + x <- sub("[ \t]*$", "", x) if (attrName == "extrapage") { type <- paste0("extrapage-", type) } diff --git a/vignettes/goxygen.Rmd b/vignettes/goxygen.Rmd index 6c59785..cfaef45 100644 --- a/vignettes/goxygen.Rmd +++ b/vignettes/goxygen.Rmd @@ -53,7 +53,7 @@ goxygen now searches the code for all lines starting with the goxygen tag `*'`, The short example GAMS file contains all identifiers available in goxygen. The resulting document starts with a table of contents. -* `@title` Adds the heading with the title to the documentation. +* `@title` Adds the heading with the title to the documentation. Only the first line will be used. * `@description` Contains the text describing the model. Start a new paragraph by adding a blank line that starts with `*'`. @@ -72,7 +72,18 @@ The short example GAMS file contains all identifiers available in goxygen. The r display v01_intern.l; ``` until the `@code` tag resumes the documentation. - + +### extrapage attribute + +Any of the identifiers can be combined with the extrapage attribute as follows: `@identifier extrapage="page"`, for example: + + ``` + *' @description extrapage="settings" + *' The macro-economic core of REMIND is a Ramsey-type optimal growth model ... + ``` + +The documentation will be moved to a new page as specified (e.g. "settings" in the example above). Note that the page name can only consist of characters and numbers and the rest of the documentation must follow on a new comment line. + ## Further features From e976220751bd63a7645d0b0d6967fc8eaf5cf21d Mon Sep 17 00:00:00 2001 From: Falk Benke Date: Tue, 14 Nov 2023 19:02:27 +0100 Subject: [PATCH 03/16] add support for multipe extra pages --- R/createListModularCode.R | 11 ++++------- R/extractDocumentation.R | 6 +++--- vignettes/goxygen.Rmd | 3 ++- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/R/createListModularCode.R b/R/createListModularCode.R index e249eb8..24d270b 100644 --- a/R/createListModularCode.R +++ b/R/createListModularCode.R @@ -208,13 +208,11 @@ createListModularCode <- function(cc, interfaces, path = ".", citation = NULL, u sortExtraPages <- function(extraPage) { out <- list() - attrPattern <- "^(\\w+)=\"(\\w+)\"$" - + attrPattern <- "^(\\w+)-(\\w+)" for (i in seq(extraPage)) { - content <- extraPage[[i]][-1] - page <- sub(attrPattern, "\\2", extraPage[[i]][1]) - type <- sub("-\\w+", "", names(extraPage[i])) - l <- list(content) + type <- sub(attrPattern, "\\1", names(extraPage)[i]) + page <- sub(attrPattern, "\\2", names(extraPage)[i]) + l <- list(extraPage[[i]]) names(l) <- type out[[page]] <- append(out[[page]], l) } @@ -250,7 +248,6 @@ createListModularCode <- function(cc, interfaces, path = ".", citation = NULL, u full[[m]] <- createModulePage(data, docfolder = docfolder) } - #browser() extraPage <- sortExtraPages(extraPage) for (i in names(extraPage)) { data <- mergeDocumentation(extraPage[[i]]) diff --git a/R/extractDocumentation.R b/R/extractDocumentation.R index 5d83e74..776e514 100644 --- a/R/extractDocumentation.R +++ b/R/extractDocumentation.R @@ -104,10 +104,10 @@ extractDocumentation <- function(path, start_type = NULL, comment = "*'") { # no attrPattern <- "^(\\w+)=\"(\\w+)\"[ \t]*$" if (grepl(attrPattern, attribute)) { attrName <- sub(attrPattern, "\\1", attribute) - # remove trailing whitespaces and tabs - x <- sub("[ \t]*$", "", x) + page <- sub(attrPattern, "\\2", attribute) if (attrName == "extrapage") { - type <- paste0("extrapage-", type) + x <- x[-1] + type <- paste0("extrapage-", type, "-", page) } } diff --git a/vignettes/goxygen.Rmd b/vignettes/goxygen.Rmd index cfaef45..7e0ae4b 100644 --- a/vignettes/goxygen.Rmd +++ b/vignettes/goxygen.Rmd @@ -82,7 +82,8 @@ Any of the identifiers can be combined with the extrapage attribute as follows: *' The macro-economic core of REMIND is a Ramsey-type optimal growth model ... ``` -The documentation will be moved to a new page as specified (e.g. "settings" in the example above). Note that the page name can only consist of characters and numbers and the rest of the documentation must follow on a new comment line. +The documentation will be moved to a new page as specified (e.g. "settings" in the example above). +**Important:** The page name can only consist of characters and numbers and the rest of the documentation must follow on a new comment line. ## Further features From 19e4f2cfe846c3481e20186d9932e26e959e51bc Mon Sep 17 00:00:00 2001 From: Falk Benke Date: Tue, 14 Nov 2023 19:19:01 +0100 Subject: [PATCH 04/16] add documentation --- vignettes/goxygen.Rmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vignettes/goxygen.Rmd b/vignettes/goxygen.Rmd index 7e0ae4b..235373d 100644 --- a/vignettes/goxygen.Rmd +++ b/vignettes/goxygen.Rmd @@ -75,7 +75,7 @@ The short example GAMS file contains all identifiers available in goxygen. The r ### extrapage attribute -Any of the identifiers can be combined with the extrapage attribute as follows: `@identifier extrapage="page"`, for example: +Any of the identifiers , except for `@stop`, can be combined with the `extrapage` attribute as follows: `@identifier extrapage="page"`, for example: ``` *' @description extrapage="settings" From bb8906079432a7f97e7fd6a356476fe439c794c7 Mon Sep 17 00:00:00 2001 From: Falk Benke Date: Wed, 15 Nov 2023 12:12:13 +0100 Subject: [PATCH 05/16] handle empty sections when creating module page --- R/createModulePage.R | 95 ++++++++++++++++++++++---------------------- R/helper_functions.R | 58 ++++++++++++++++++--------- 2 files changed, 87 insertions(+), 66 deletions(-) diff --git a/R/createModulePage.R b/R/createModulePage.R index 3304673..afdbc7a 100644 --- a/R/createModulePage.R +++ b/R/createModulePage.R @@ -1,8 +1,8 @@ #' createModulePage -#' -#' Creates markdown code from a supplied data list -#' -#' @param data a list of data entries for the resulting markdown page. Following +#' +#' Creates markdown code from a supplied data list +#' +#' @param data a list of data entries for the resulting markdown page. Following #' entries can be provided: #' \describe{ #' \item{name}{Name of the module} @@ -25,53 +25,54 @@ createModulePage <- function(data, docfolder) { - out <- NULL - zz <- textConnection("out",open = "w", local=TRUE) - - .header(zz,paste0(data$title," (",data$name,")"),1, id=data$name) - .header(zz,"Description",2) - .write(zz,data$description) - - .header(zz,"Interfaces",2) - - .interfaceplot(zz,data$name,docfolder) - - .header(zz,"Input",3) - .write(zz,data$input) - - .header(zz,"Output",3) - .write(zz,data$output) - - .header(zz,"Realizations",2) - + zz <- textConnection("out", open = "w", local = TRUE) + + .header(zz, paste0(data$title, " (", data$name, ")"), 1, id = data$name) + + .section(data$description, zz, "Description", 2) + + .header(zz, "Interfaces", 2) + + .interfaceplot(zz, data$name, docfolder) + + .section(data$input, zz, "Input", 3) + + .section(data$output, zz, "Output", 3) + + .header(zz, "Realizations", 2) + rdata <- data$realizations i <- 1 - for(r in names(rdata)) { - title <- paste0("(",LETTERS[i],") ",r) - .header(zz,title,3) - .write(zz,rdata[[r]]$description) - .limitations(zz,rdata[[r]]$limitations) - i <- i+1 + for (r in names(rdata)) { + title <- paste0("(", LETTERS[i], ") ", r) + .header(zz, title, 3) + .write(zz, rdata[[r]]$description) + .limitations(zz, rdata[[r]]$limitations) + i <- i + 1 + } + + .limitations(zz, data$limitations, emptyIfNULL = TRUE) + + if (any(!is.null(data$declarations), !is.null(data$sets))) { + .header(zz, "Definitions", 2) + + .section(data$declarations, zz, "Objects", 3) + + .section(data$sets, zz, "Sets", 3) + } + + .section(data$authors, zz, "Authors", 2) + + if (!is.null(data$seealso)) { + data$seealso <- paste0("[", sort(data$seealso), "]", collapse = ", ") } - - .limitations(zz,data$limitations, emptyIfNULL=TRUE) - - .header(zz,"Definitions",2) - .header(zz,"Objects",3) - .write(zz,data$declarations) - .header(zz,"Sets",3) - .write(zz,data$sets) - - .header(zz,"Authors",2) - .write(zz,data$authors) - - .header(zz,"See Also",2) - .write(zz,paste0("[",sort(data$seealso),"]",collapse=", ")) - + + .section(data$seealso, zz, "See Also", 2) + close(zz) - + out <- .updateImagePaths(out) - + return(out) -} \ No newline at end of file +} diff --git a/R/helper_functions.R b/R/helper_functions.R index b6115fb..123b204 100644 --- a/R/helper_functions.R +++ b/R/helper_functions.R @@ -1,41 +1,41 @@ #' .empty -#' +#' #' helper function which adds an empty line in a markdown document -#' +#' #' @param zz a connection object of class "textConnection" containing the markdown document #' @author Jan Philipp Dietrich #' @seealso \code{\link{goxygen}}, \code{\link{createModulePage}} #' @export -#' +#' .empty <- function(zz) { writeLines("",zz) } #' .write -#' +#' #' helper function which writes a character vector line by line in a markdown document -#' +#' #' @param zz a connection object of class "textConnection" containing the markdown document #' @param data a character vector to be written to the markdown document #' @author Jan Philipp Dietrich #' @seealso \code{\link{goxygen}}, \code{\link{createModulePage}} -#' +#' .write <- function(zz,data) { if(!is.null(data)) writeLines(data,zz) .empty(zz) } #' .header -#' +#' #' helper function which writes a title for a markdown section -#' +#' #' @param zz a connection object of class "textConnection" containing the markdown document #' @param title the title to be used (character vector of length 1) #' @param level level of the heading (1 means main header, higher numbers reflect lower levels) #' @param id ID given to the title (relevant for anchors) #' @author Jan Philipp Dietrich #' @seealso \code{\link{goxygen}}, \code{\link{createModulePage}} -#' +#' .header <- function(zz,title,level,id=NULL) { if(length(title)>1) { warning("Multiline entry to title detected. Only first line will be used!") @@ -53,37 +53,57 @@ .empty(zz) } + +#' .section +#' +#' helper function which creates a section consisting of header and content in a markdown document +#' and skips section when content is empty +#' +#' @param data a character vector to be written to the markdown document +#' @param zz a connection object of class "textConnection" containing the markdown document +#' @param title the title to be used (character vector of length 1) +#' @param level level of the heading (1 means main header, higher numbers reflect lower levels) +#' @param id ID given to the title (relevant for anchors) +#' @author Falk Benke +#' @seealso \code{\link{goxygen}}, \code{\link{createModulePage}} +#' +.section <- function(data, zz, title, level, id = NULL) { + if(!is.null(data)){ + .header(zz, title, level, id) + .write(zz, data) + } +} #' .interfaceplot -#' +#' #' helper function which includes interface plot figures in a markdown document, if available. #' The figures need to be created beforehand. -#' +#' #' @param zz a connection object of class "textConnection" containing the markdown document #' @param name Name of the module for which the interfaceplot should be shown #' @param docfolder folder the documentation should be written to relative to model folder #' @author Jan Philipp Dietrich #' @seealso \code{\link{goxygen}}, \code{\link{createModulePage}} -#' +#' .interfaceplot <- function(zz,name,docfolder) { file <- paste0("images/interfaces_",sub("^[^_]*_","",name),".png") if(file.exists(paste0(docfolder,"/",file))) { .write(zz,paste0("![Interfaces to other modules](",file,"){ height=50% width=100% }")) } else { - .write(zz,"**Interface plot missing!**") + .write(zz,"**Interface plot missing!**") } } #' .limitations -#' +#' #' helper function which adds a "limitations" section. -#' +#' #' @param zz a connection object of class "textConnection" containing the markdown document #' @param limitations A character vector containing the given limitations #' @param emptyIfNULL switch which decides whether limitations section should be ignored, if #' limitations input is NULL or if it should state that there are no known limitations. #' @author Jan Philipp Dietrich #' @seealso \code{\link{goxygen}}, \code{\link{createModulePage}} -#' +#' .limitations <- function(zz,limitations, emptyIfNULL=FALSE) { if(is.null(limitations)) { if(emptyIfNULL) return() @@ -96,14 +116,14 @@ #' .updateImagePaths -#' +#' #' helper function which updates relative image paths so that they refer to a subfolder #' images instead of refering to the current folder. -#' +#' #' @param x A character vector containing image paths. #' @author Jan Philipp Dietrich #' @seealso \code{\link{goxygen}}, \code{\link{createModulePage}} -#' +#' .updateImagePaths <- function(x){ return(gsub("\\(([^/]*\\.(png|jpg))\\)","(images/\\1)",x)) } From 4a6af760e6b85b75af6f595d0b53c9c1a4822002 Mon Sep 17 00:00:00 2001 From: Falk Benke Date: Wed, 15 Nov 2023 14:21:14 +0100 Subject: [PATCH 06/16] increment version --- .buildlibrary | 2 +- .github/workflows/check.yaml | 97 ++++++++++-------------------------- .pre-commit-config.yaml | 4 +- CITATION.cff | 4 +- DESCRIPTION | 4 +- R/helper_functions.R | 54 ++++++++++---------- README.md | 6 +-- man/createModulePage.Rd | 2 +- man/dot-section.Rd | 29 +++++++++++ man/extractDocumentation.Rd | 6 +-- vignettes/goxygen.Rmd | 6 +-- 11 files changed, 97 insertions(+), 117 deletions(-) create mode 100644 man/dot-section.Rd diff --git a/.buildlibrary b/.buildlibrary index 3801552..991e5ab 100644 --- a/.buildlibrary +++ b/.buildlibrary @@ -1,4 +1,4 @@ -ValidationKey: '2603874' +ValidationKey: '2754640' AutocreateReadme: yes AcceptedWarnings: - 'Warning: package ''.*'' was built under R version' diff --git a/.github/workflows/check.yaml b/.github/workflows/check.yaml index fcd7136..870f216 100644 --- a/.github/workflows/check.yaml +++ b/.github/workflows/check.yaml @@ -1,5 +1,3 @@ -# Run CI for R using https://eddelbuettel.github.io/r-ci/ - name: check on: @@ -8,11 +6,6 @@ on: pull_request: branches: [main, master] -env: - USE_BSPM: "true" - _R_CHECK_FORCE_SUGGESTS_: "false" - NO_BINARY_INSTALL_R_PACKAGES: 'c("madrat", "magclass", "citation", "gms", "goxygen", "GDPuc", "roxygen2")' - jobs: check: runs-on: ubuntu-latest @@ -20,80 +13,38 @@ jobs: steps: - uses: actions/checkout@v3 - - name: Bootstrap - run: | - sudo chown runner -R . - sudo locale-gen en_US.UTF-8 - sudo add-apt-repository -y ppa:ubuntugis/ppa - curl -OLs https://eddelbuettel.github.io/r-ci/run.sh - chmod 0755 run.sh - ./run.sh bootstrap - rm -f bspm_*.tar.gz - - - name: Enable r-universe repo, modify bspm integration - run: | - # install packages from https://pik-piam.r-universe.dev and CRAN - echo ' - options(repos = c(universe = "https://pik-piam.r-universe.dev", - CRAN = "https://cloud.r-project.org")) - ' >> .Rprofile - cat .Rprofile - # modify bspm integration to never install binary builds of PIK CRAN packages - sudo sed -i '/bspm::enable()/d' /etc/R/Rprofile.site - # need double % because of printf, %s is replaced with "$NO_BINARY_INSTALL_R_PACKAGES" (see "env:" above) - printf ' - local({ - expr <- quote({ - if (!is.null(repos)) { - noBinaryInstallRPackages <- %s - pkgs <- c(bspm::install_sys(pkgs[!pkgs %%in%% noBinaryInstallRPackages]), - pkgs[pkgs %%in%% noBinaryInstallRPackages]) - } - type <- "source" - }) - trace(utils::install.packages, expr, print = FALSE) - }) - ' "$NO_BINARY_INSTALL_R_PACKAGES" | sudo tee --append /etc/R/Rprofile.site >/dev/null - cat /etc/R/Rprofile.site - - - name: Set up Pandoc - uses: r-lib/actions/setup-pandoc@v2 + - uses: r-lib/actions/setup-pandoc@v2 - - name: Set up Python 3.9 - uses: actions/setup-python@v4 + - uses: r-lib/actions/setup-r@v2 with: - python-version: 3.9 + use-public-rspm: true + extra-repositories: "https://rse.pik-potsdam.de/r/packages" - - name: Cache R libraries - if: ${{ !env.ACT }} # skip when running locally via nektos/act - uses: pat-s/always-upload-cache@v3 + - uses: r-lib/actions/setup-r-dependencies@v2 with: - path: /usr/local/lib/R/ - key: 3-${{ runner.os }}-usr-local-lib-R-${{ hashFiles('DESCRIPTION') }} - restore-keys: | - 3-${{ runner.os }}-usr-local-lib-R- - - - name: Restore R library permissions - run: | - sudo chmod 2777 /usr/local/lib/R /usr/local/lib/R/site-library - - - name: Install dependencies - run: | - ./run.sh install_aptget libhdf5-dev libharfbuzz-dev libfribidi-dev - ./run.sh install_all - ./run.sh install_r_binary covr rstudioapi - ./run.sh install_r lucode2 + extra-packages: | + gamstransfer=?ignore + any::lucode2 + any::covr + any::madrat + any::magclass + any::citation + any::gms + any::goxygen + any::GDPuc + # piam packages also available on CRAN (madrat, magclass, citation, + # gms, goxygen, GDPuc) will usually have an outdated binary version + # available; by using extra-packages we get the newest version + + - uses: actions/setup-python@v4 + with: + python-version: 3.9 - name: Install python dependencies if applicable run: | [ -f requirements.txt ] && python -m pip install --upgrade pip wheel || true [ -f requirements.txt ] && pip install -r requirements.txt || true - - name: Remove bspm integration # to get rid of error when running install.packages - run: | - sudo sed -i '/ trace(utils::install.packages, expr, print = FALSE)/d' /etc/R/Rprofile.site - cat /etc/R/Rprofile.site - - name: Verify validation key shell: Rscript {0} run: lucode2:::validkey(stopIfInvalid = TRUE) @@ -106,6 +57,8 @@ jobs: - name: Test coverage shell: Rscript {0} - run: covr::codecov(quiet = FALSE) + run: | + nonDummyTests <- setdiff(list.files("./tests/testthat/"), c("test-dummy.R", "_snaps")) + if(length(nonDummyTests) > 0) covr::codecov(quiet = FALSE) env: NOT_CRAN: "true" diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9c3b069..2f13466 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,7 +3,7 @@ exclude: '^tests/testthat/_snaps/.*$' repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 + rev: v4.5.0 hooks: - id: check-case-conflict - id: check-json @@ -15,7 +15,7 @@ repos: - id: mixed-line-ending - repo: https://github.com/lorenzwalthert/precommit - rev: v0.3.2.9013 + rev: v0.3.2.9025 hooks: - id: parsable-R - id: deps-in-desc diff --git a/CITATION.cff b/CITATION.cff index 01867e2..4d3c4d9 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -2,8 +2,8 @@ cff-version: 1.2.0 message: If you use this software, please cite it using the metadata from this file. type: software title: 'goxygen: In-Code Documentation for ''GAMS''' -version: 1.3.3 -date-released: '2023-08-09' +version: 1.4.0 +date-released: '2023-11-15' abstract: A collection of tools which extract a model documentation from 'GAMS' code and comments. In order to use the package you need to install 'pandoc' and 'pandoc-citeproc' first (). diff --git a/DESCRIPTION b/DESCRIPTION index f7ac6bc..7dc1b6d 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,8 +1,8 @@ Package: goxygen Type: Package Title: In-Code Documentation for 'GAMS' -Version: 1.3.3 -Date: 2023-08-09 +Version: 1.4.0 +Date: 2023-11-15 Authors@R: c(person("Jan Philipp", "Dietrich", email = "dietrich@pik-potsdam.de", role = c("aut","cre")), person("Kristine", "Karstens", email = "karstens@pik-potsdam.de", role = "aut"), person("David", "Klein", email = "dklein@pik-potsdam.de", role = "aut"), diff --git a/R/helper_functions.R b/R/helper_functions.R index 123b204..358b04f 100644 --- a/R/helper_functions.R +++ b/R/helper_functions.R @@ -8,7 +8,7 @@ #' @export #' .empty <- function(zz) { - writeLines("",zz) + writeLines("", zz) } #' .write @@ -20,8 +20,8 @@ #' @author Jan Philipp Dietrich #' @seealso \code{\link{goxygen}}, \code{\link{createModulePage}} #' -.write <- function(zz,data) { - if(!is.null(data)) writeLines(data,zz) +.write <- function(zz, data) { + if (!is.null(data)) writeLines(data, zz) .empty(zz) } @@ -36,19 +36,19 @@ #' @author Jan Philipp Dietrich #' @seealso \code{\link{goxygen}}, \code{\link{createModulePage}} #' -.header <- function(zz,title,level,id=NULL) { - if(length(title)>1) { +.header <- function(zz, title, level, id = NULL) { + if (length(title) > 1) { warning("Multiline entry to title detected. Only first line will be used!") title <- title[1] } - if(!is.null(id)) id <- paste0(" {#id-",id,"}") - if(level<3) { - writeLines(paste0(title,id),zz) - symbol <- ifelse(level==1,"=","-") - writeLines(paste(rep(symbol,nchar(title)), collapse=""),zz) + if (!is.null(id)) id <- paste0(" {#id-", id, "}") + if (level < 3) { + writeLines(paste0(title, id), zz) + symbol <- ifelse(level == 1, "=", "-") + writeLines(paste(rep(symbol, nchar(title)), collapse = ""), zz) } else { - start <- paste(rep("#",level),collapse="") - writeLines(paste(start,title),zz) + start <- paste(rep("#", level), collapse = "") + writeLines(paste(start, title), zz) } .empty(zz) } @@ -68,7 +68,7 @@ #' @seealso \code{\link{goxygen}}, \code{\link{createModulePage}} #' .section <- function(data, zz, title, level, id = NULL) { - if(!is.null(data)){ + if (!is.null(data)) { .header(zz, title, level, id) .write(zz, data) } @@ -84,12 +84,12 @@ #' @author Jan Philipp Dietrich #' @seealso \code{\link{goxygen}}, \code{\link{createModulePage}} #' -.interfaceplot <- function(zz,name,docfolder) { - file <- paste0("images/interfaces_",sub("^[^_]*_","",name),".png") - if(file.exists(paste0(docfolder,"/",file))) { - .write(zz,paste0("![Interfaces to other modules](",file,"){ height=50% width=100% }")) +.interfaceplot <- function(zz, name, docfolder) { + file <- paste0("images/interfaces_", sub("^[^_]*_", "", name), ".png") + if (file.exists(paste0(docfolder, "/", file))) { + .write(zz, paste0("![Interfaces to other modules](", file, "){ height=50% width=100% }")) } else { - .write(zz,"**Interface plot missing!**") + .write(zz, "**Interface plot missing!**") } } @@ -104,14 +104,14 @@ #' @author Jan Philipp Dietrich #' @seealso \code{\link{goxygen}}, \code{\link{createModulePage}} #' -.limitations <- function(zz,limitations, emptyIfNULL=FALSE) { - if(is.null(limitations)) { - if(emptyIfNULL) return() +.limitations <- function(zz, limitations, emptyIfNULL = FALSE) { + if (is.null(limitations)) { + if (emptyIfNULL) return() limitations <- "There are no known limitations." } - limitations <- c("**Limitations**",limitations) - limitations <- paste(">",limitations) - .write(zz,limitations) + limitations <- c("**Limitations**", limitations) + limitations <- paste(">", limitations) + .write(zz, limitations) } @@ -124,8 +124,6 @@ #' @author Jan Philipp Dietrich #' @seealso \code{\link{goxygen}}, \code{\link{createModulePage}} #' -.updateImagePaths <- function(x){ - return(gsub("\\(([^/]*\\.(png|jpg))\\)","(images/\\1)",x)) +.updateImagePaths <- function(x) { + return(gsub("\\(([^/]*\\.(png|jpg))\\)", "(images/\\1)", x)) } - - diff --git a/README.md b/README.md index f6294ab..aeb10e7 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # In-Code Documentation for 'GAMS' -R package **goxygen**, version **1.3.3** +R package **goxygen**, version **1.4.0** [![CRAN status](https://www.r-pkg.org/badges/version/goxygen)](https://cran.r-project.org/package=goxygen) [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.1411404.svg)](https://doi.org/10.5281/zenodo.1411404) [![R build status](https://github.com/pik-piam/goxygen/workflows/check/badge.svg)](https://github.com/pik-piam/goxygen/actions) [![codecov](https://codecov.io/gh/pik-piam/goxygen/branch/master/graph/badge.svg)](https://app.codecov.io/gh/pik-piam/goxygen) [![r-universe](https://pik-piam.r-universe.dev/badges/goxygen)](https://pik-piam.r-universe.dev/builds) @@ -48,7 +48,7 @@ In case of questions / problems please contact Jan Philipp Dietrich , R package version 1.3.3, . +Dietrich J, Karstens K, Klein D, Baumstark L (2023). _goxygen: In-Code Documentation for 'GAMS'_. doi:10.5281/zenodo.1411404 , R package version 1.4.0, . A BibTeX entry for LaTeX users is @@ -57,7 +57,7 @@ A BibTeX entry for LaTeX users is title = {goxygen: In-Code Documentation for 'GAMS'}, author = {Jan Philipp Dietrich and Kristine Karstens and David Klein and Lavinia Baumstark}, year = {2023}, - note = {R package version 1.3.3}, + note = {R package version 1.4.0}, doi = {10.5281/zenodo.1411404}, url = {https://github.com/pik-piam/goxygen}, } diff --git a/man/createModulePage.Rd b/man/createModulePage.Rd index 6dcda0c..6e6c794 100644 --- a/man/createModulePage.Rd +++ b/man/createModulePage.Rd @@ -7,7 +7,7 @@ createModulePage(data, docfolder) } \arguments{ -\item{data}{a list of data entries for the resulting markdown page. Following +\item{data}{a list of data entries for the resulting markdown page. Following entries can be provided: \describe{ \item{name}{Name of the module} diff --git a/man/dot-section.Rd b/man/dot-section.Rd new file mode 100644 index 0000000..bae0b6e --- /dev/null +++ b/man/dot-section.Rd @@ -0,0 +1,29 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/helper_functions.R +\name{.section} +\alias{.section} +\title{.section} +\usage{ +.section(data, zz, title, level, id = NULL) +} +\arguments{ +\item{data}{a character vector to be written to the markdown document} + +\item{zz}{a connection object of class "textConnection" containing the markdown document} + +\item{title}{the title to be used (character vector of length 1)} + +\item{level}{level of the heading (1 means main header, higher numbers reflect lower levels)} + +\item{id}{ID given to the title (relevant for anchors)} +} +\description{ +helper function which creates a section consisting of header and content in a markdown document +and skips section when content is empty +} +\seealso{ +\code{\link{goxygen}}, \code{\link{createModulePage}} +} +\author{ +Falk Benke +} diff --git a/man/extractDocumentation.Rd b/man/extractDocumentation.Rd index e213517..b8c7486 100644 --- a/man/extractDocumentation.Rd +++ b/man/extractDocumentation.Rd @@ -24,10 +24,10 @@ of the line. In case of @realization also GAMS code is read and interpreted, in only the specific documentation comment is evaluated. } \examples{ -mainfile <- paste0(system.file("dummymodel",package="gms"),"/main.gms") -calcfile <- paste0(system.file("dummymodel",package="gms"), +mainfile <- paste0(system.file("dummymodel", package = "gms"), "/main.gms") +calcfile <- paste0(system.file("dummymodel", package = "gms"), "/modules/02_crazymodule/complex/calculations.gms") -# extracting information from the main file of the model +# extracting information from the main file of the model extractDocumentation(mainfile) # extracting information from a file with some equations in it extractDocumentation(calcfile) diff --git a/vignettes/goxygen.Rmd b/vignettes/goxygen.Rmd index 235373d..4830ef9 100644 --- a/vignettes/goxygen.Rmd +++ b/vignettes/goxygen.Rmd @@ -37,8 +37,8 @@ setwd(tempdir()) We take the GAMS code example from this package and save it to `dummymodel-plain`: ```{r eval=FALSE} -# copy the folder containing a simple dummy model with goxygen comments -file.copy(from = system.file("dummymodel-plain",package="goxygen"), to = ".", recursive = TRUE) +# copy the folder containing a simple dummy model with goxygen comments +file.copy(from = system.file("dummymodel-plain", package = "goxygen"), to = ".", recursive = TRUE) ``` and execute `goxygen` on this GAMS file to produce the documentation in HTML as well as PDF format. @@ -108,7 +108,7 @@ Goxygen is tailored to extract the documentation from this modular structure and ```{r eval=FALSE} # copy all files and folders containing the modular dummy model -file.copy(from = system.file("dummymodel",package="gms"), to = ".", recursive = TRUE) +file.copy(from = system.file("dummymodel", package = "gms"), to = ".", recursive = TRUE) ``` Now execute `goxygen` on the modular GAMS model: From 9a6ec246ad98b266d93531fb9a77cd70da830e59 Mon Sep 17 00:00:00 2001 From: Falk Benke Date: Wed, 15 Nov 2023 16:00:47 +0100 Subject: [PATCH 07/16] add support for extrapage attribute to simple code --- R/createListModularCode.R | 13 ------------- R/createListSimpleCode.R | 17 +++++++++++++++++ R/sortExtraPages.R | 20 ++++++++++++++++++++ man/sortExtraPages.Rd | 19 +++++++++++++++++++ 4 files changed, 56 insertions(+), 13 deletions(-) create mode 100644 R/sortExtraPages.R create mode 100644 man/sortExtraPages.Rd diff --git a/R/createListModularCode.R b/R/createListModularCode.R index 24d270b..c2a8c2e 100644 --- a/R/createListModularCode.R +++ b/R/createListModularCode.R @@ -206,19 +206,6 @@ createListModularCode <- function(cc, interfaces, path = ".", citation = NULL, u return(seealso) } - sortExtraPages <- function(extraPage) { - out <- list() - attrPattern <- "^(\\w+)-(\\w+)" - for (i in seq(extraPage)) { - type <- sub(attrPattern, "\\1", names(extraPage)[i]) - page <- sub(attrPattern, "\\2", names(extraPage)[i]) - l <- list(extraPage[[i]]) - names(l) <- type - out[[page]] <- append(out[[page]], l) - } - return(out) - } - out <- collectTables(cc, unitPattern) # write doc files diff --git a/R/createListSimpleCode.R b/R/createListSimpleCode.R index b4571b7..13249b4 100644 --- a/R/createListSimpleCode.R +++ b/R/createListSimpleCode.R @@ -16,6 +16,9 @@ createListSimpleCode <- function(path = ".", citation = NULL, mainfile = "main.g # write doc files full <- list() data <- extractDocumentation(mainfile) + extraPage <- data$extrapage + data$extrapage <- NULL + data$citation <- citation full[["index"]] <- createIndexPage(data) @@ -23,11 +26,25 @@ createListSimpleCode <- function(path = ".", citation = NULL, mainfile = "main.g for (f in files) { fname <- sub("\\.gms$", "", gsub("/", "_", f, fixed = TRUE)) data <- extractDocumentation(f) + + extraPage <- append(extraPage, data$extrapage) + data$extrapage <- NULL + if (length(data) > 0) { if (is.null(data$title)) data$title <- fname data$name <- f full[[fname]] <- createSimplePage(data) } } + + extraPage <- sortExtraPages(extraPage) + + for (i in names(extraPage)) { + data <- mergeDocumentation(extraPage[[i]]) + data$name <- i + full[[i]] <- createSimplePage(data) + } + + return(full) } diff --git a/R/sortExtraPages.R b/R/sortExtraPages.R new file mode 100644 index 0000000..319293a --- /dev/null +++ b/R/sortExtraPages.R @@ -0,0 +1,20 @@ +#' sortExtraPages +#' +#' helper transforming a flat list of extra page blocks into +#' sublists per page +#' +#' @param extraPage flat list of blocks with names indicating identifier and +#' extrapage (e.g. 'description-settings') +#' @author Jan Philipp Dietrich +sortExtraPages <- function(extraPage) { + out <- list() + attrPattern <- "^(\\w+)-(\\w+)" + for (i in seq(extraPage)) { + type <- sub(attrPattern, "\\1", names(extraPage)[i]) + page <- sub(attrPattern, "\\2", names(extraPage)[i]) + l <- list(extraPage[[i]]) + names(l) <- type + out[[page]] <- append(out[[page]], l) + } + return(out) +} diff --git a/man/sortExtraPages.Rd b/man/sortExtraPages.Rd new file mode 100644 index 0000000..6c2fef5 --- /dev/null +++ b/man/sortExtraPages.Rd @@ -0,0 +1,19 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/sortExtraPages.R +\name{sortExtraPages} +\alias{sortExtraPages} +\title{sortExtraPages} +\usage{ +sortExtraPages(extraPage) +} +\arguments{ +\item{extraPage}{flat list of blocks with names indicating identifier and +extrapage (e.g. 'description-settings')} +} +\description{ +helper transforming a flat list of extra page blocks into +sublists per page +} +\author{ +Jan Philipp Dietrich +} From 61f565ef5ef19933881fc05efa472556e35f2a4b Mon Sep 17 00:00:00 2001 From: Falk Benke Date: Wed, 15 Nov 2023 17:47:45 +0100 Subject: [PATCH 08/16] add tests --- R/sortExtraPages.R | 2 +- tests/testthat/test-goxygen.R | 14 +++++++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/R/sortExtraPages.R b/R/sortExtraPages.R index 319293a..fac0c75 100644 --- a/R/sortExtraPages.R +++ b/R/sortExtraPages.R @@ -9,7 +9,7 @@ sortExtraPages <- function(extraPage) { out <- list() attrPattern <- "^(\\w+)-(\\w+)" - for (i in seq(extraPage)) { + for (i in seq_along(extraPage)) { type <- sub(attrPattern, "\\1", names(extraPage)[i]) page <- sub(attrPattern, "\\2", names(extraPage)[i]) l <- list(extraPage[[i]]) diff --git a/tests/testthat/test-goxygen.R b/tests/testthat/test-goxygen.R index adeec3f..0d30aec 100644 --- a/tests/testthat/test-goxygen.R +++ b/tests/testthat/test-goxygen.R @@ -4,12 +4,15 @@ test_that("extract documentation from modular dummy model", { docfolder <- file.path(withr::local_tempdir(), "doc_modular") out <- suppressWarnings(try(goxygen(path = system.file("dummymodel", package = "gms"), output = c("html", "tex"), - docfolder = docfolder, includeCore = TRUE, cff = "HOWTOCITE.cff", DoNotPlot = TRUE))) + docfolder = docfolder, includeCore = TRUE, + cff = "HOWTOCITE.cff", DoNotPlot = TRUE))) expect_null(out) expect_true(file.exists(file.path(docfolder, "html", "index.htm"))) expect_true(file.exists(file.path(docfolder, "html", "core.htm"))) expect_true(file.exists(file.path(docfolder, "html", "01_fancymodule.htm"))) expect_true(file.exists(file.path(docfolder, "html", "02_crazymodule.htm"))) + expect_true(file.exists(file.path(docfolder, "html", "crazy.htm"))) + expect_true(file.exists(file.path(docfolder, "html", "settings.htm"))) expect_true(file.exists(file.path(docfolder, "documentation.tex"))) }) @@ -17,13 +20,16 @@ test_that("extract HTML documentation from modular dummy model with classic styl docfolder <- file.path(withr::local_tempdir(), "doc_modular_classic") out <- suppressWarnings(try(goxygen(path = system.file("dummymodel", package = "gms"), - htmlStyle = "classic", output = "html", - docfolder = docfolder, includeCore = TRUE, cff = "HOWTOCITE.cff", DoNotPlot = TRUE))) + htmlStyle = "classic", output = "html", + docfolder = docfolder, includeCore = TRUE, + cff = "HOWTOCITE.cff", DoNotPlot = TRUE))) expect_null(out) expect_true(file.exists(file.path(docfolder, "html", "index.htm"))) expect_true(file.exists(file.path(docfolder, "html", "core.htm"))) expect_true(file.exists(file.path(docfolder, "html", "01_fancymodule.htm"))) expect_true(file.exists(file.path(docfolder, "html", "02_crazymodule.htm"))) + expect_true(file.exists(file.path(docfolder, "html", "crazy.htm"))) + expect_true(file.exists(file.path(docfolder, "html", "settings.htm"))) }) test_that("cache and unknown output", { @@ -44,5 +50,7 @@ test_that("extract documentation from simple dummy model", { expect_true(file.exists(file.path(docfolder, "html", "index.htm"))) expect_true(file.exists(file.path(docfolder, "html", "modules_01_fancymodule_default_calculations.htm"))) expect_true(file.exists(file.path(docfolder, "html", "modules_02_crazymodule_module.htm"))) + expect_true(file.exists(file.path(docfolder, "html", "crazy.htm"))) + expect_true(file.exists(file.path(docfolder, "html", "settings.htm"))) expect_true(file.exists(file.path(docfolder, "documentation.tex"))) }) From 227e9703e4c89175a442db6d721c81f5ebd7d574 Mon Sep 17 00:00:00 2001 From: Falk Benke Date: Wed, 15 Nov 2023 17:51:03 +0100 Subject: [PATCH 09/16] minor documentation correction --- R/sortExtraPages.R | 2 +- man/sortExtraPages.Rd | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/R/sortExtraPages.R b/R/sortExtraPages.R index fac0c75..4aa318c 100644 --- a/R/sortExtraPages.R +++ b/R/sortExtraPages.R @@ -5,7 +5,7 @@ #' #' @param extraPage flat list of blocks with names indicating identifier and #' extrapage (e.g. 'description-settings') -#' @author Jan Philipp Dietrich +#' @author Falk Benke sortExtraPages <- function(extraPage) { out <- list() attrPattern <- "^(\\w+)-(\\w+)" diff --git a/man/sortExtraPages.Rd b/man/sortExtraPages.Rd index 6c2fef5..758cc58 100644 --- a/man/sortExtraPages.Rd +++ b/man/sortExtraPages.Rd @@ -15,5 +15,5 @@ helper transforming a flat list of extra page blocks into sublists per page } \author{ -Jan Philipp Dietrich +Falk Benke } From 62a3341899d6eae2f353243feeed72935003dd1a Mon Sep 17 00:00:00 2001 From: Falk Benke Date: Wed, 15 Nov 2023 18:35:51 +0100 Subject: [PATCH 10/16] specify minimum gms version dependency --- DESCRIPTION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index 7dc1b6d..eda12ff 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -13,7 +13,7 @@ Description: A collection of tools which extract a model documentation from 'GAM Imports: pander, stringi, - gms, + gms (>= 0.26.3), citation, withr, yaml From dd502c7eef1329eb1bb3f8743b93cc958e6b4c78 Mon Sep 17 00:00:00 2001 From: Falk Benke Date: Fri, 17 Nov 2023 16:03:42 +0100 Subject: [PATCH 11/16] implement yaml-like specification of extra attributes --- R/appendExtraPageBlocks.R | 11 ++++++++ R/createListModularCode.R | 32 ++++++++------------- R/createListSimpleCode.R | 18 ++++++------ R/extractDocumentation.R | 59 ++++++++++++++++----------------------- R/flattenPageBlockList.R | 46 ++++++++++++++++++++++++++++++ R/sortExtraPages.R | 20 ------------- 6 files changed, 101 insertions(+), 85 deletions(-) create mode 100644 R/appendExtraPageBlocks.R create mode 100644 R/flattenPageBlockList.R delete mode 100644 R/sortExtraPages.R diff --git a/R/appendExtraPageBlocks.R b/R/appendExtraPageBlocks.R new file mode 100644 index 0000000..9956511 --- /dev/null +++ b/R/appendExtraPageBlocks.R @@ -0,0 +1,11 @@ +appendExtraPageBlocks <- function(blocks, add) { + for (k in seq_along(add)) { + page <- names(add[k]) + if (page %in% names(blocks)) { + blocks[[page]] <- append(blocks[[page]], add[[k]]) + } else { + blocks[[page]] <- add[[k]] + } + } + return(blocks) +} diff --git a/R/createListModularCode.R b/R/createListModularCode.R index c2a8c2e..f81eebc 100644 --- a/R/createListModularCode.R +++ b/R/createListModularCode.R @@ -158,13 +158,7 @@ createListModularCode <- function(cc, interfaces, path = ".", citation = NULL, u files <- list.files(path = "core", pattern = "\\.gms") paths <- file.path("core", files) - documentation <- extractDocumentation(paths, start_type = "equations") - - # move extrapage items to toplevel - outSub$extrapage <- append(outSub$extrapage, documentation$extrapage) - documentation$extrapage <- NULL - - outSub$realizations[["core"]] <- documentation + outSub$realizations[["core"]] <- extractDocumentation(paths, start_type = "equations") } else { rea <- strsplit(cc$modulesInfo[m, "realizations"], ",")[[1]] @@ -186,13 +180,7 @@ createListModularCode <- function(cc, interfaces, path = ".", citation = NULL, u # mentioned files will be added at the end. paths <- union(intersect(mentionedPaths, existingPaths), existingPaths) - documentation <- extractDocumentation(paths, start_type = "equations") - - # move extrapage items to toplevel - outSub$extrapage <- append(outSub$extrapage, documentation$extrapage) - documentation$extrapage <- NULL - - outSub$realizations[[r]] <- documentation + outSub$realizations[[r]] <- extractDocumentation(paths, start_type = "equations") } } return(outSub) @@ -212,10 +200,12 @@ createListModularCode <- function(cc, interfaces, path = ".", citation = NULL, u full <- list() data <- extractDocumentation(mainfile) - extraPage <- data$extrapage - data$extrapage <- NULL + data <- flattenPageBlockList(data) + extraPageBlocks <- data$extraPageBlocks + data <- data$blocks data$citation <- citation + full[["index"]] <- createIndexPage(data) # take only all modules into account or also core @@ -227,17 +217,17 @@ createListModularCode <- function(cc, interfaces, path = ".", citation = NULL, u for (m in mLoop) { realizations <- collectRealizations(m, cc) - extraPage <- append(extraPage, realizations$extrapage) - realizations$extrapage <- NULL + extract <- flattenPageBlockList(realizations) + realizations <- extract$blocks + extraPageBlocks <- appendExtraPageBlocks(extraPageBlocks, extract$extraPageBlocks) data <- append(out[[m]], realizations) data$name <- m data$seealso <- collectSeealso(interfaces[[m]], m, cc$modulesInfo) full[[m]] <- createModulePage(data, docfolder = docfolder) } - extraPage <- sortExtraPages(extraPage) - for (i in names(extraPage)) { - data <- mergeDocumentation(extraPage[[i]]) + for (i in names(extraPageBlocks)) { + data <- mergeDocumentation(extraPageBlocks[[i]]) data$name <- i full[[i]] <- createModulePage(data, docfolder = docfolder) } diff --git a/R/createListSimpleCode.R b/R/createListSimpleCode.R index 13249b4..a3ffd09 100644 --- a/R/createListSimpleCode.R +++ b/R/createListSimpleCode.R @@ -15,9 +15,11 @@ createListSimpleCode <- function(path = ".", citation = NULL, mainfile = "main.g # write doc files full <- list() + data <- extractDocumentation(mainfile) - extraPage <- data$extrapage - data$extrapage <- NULL + data <- flattenPageBlockList(data) + extraPageBlocks <- data$extraPageBlocks + data <- data$blocks data$citation <- citation full[["index"]] <- createIndexPage(data) @@ -26,9 +28,10 @@ createListSimpleCode <- function(path = ".", citation = NULL, mainfile = "main.g for (f in files) { fname <- sub("\\.gms$", "", gsub("/", "_", f, fixed = TRUE)) data <- extractDocumentation(f) + extract <- flattenPageBlockList(data) + data <- extract$blocks - extraPage <- append(extraPage, data$extrapage) - data$extrapage <- NULL + extraPageBlocks <- appendExtraPageBlocks(extraPageBlocks, extract$extraPageBlocks) if (length(data) > 0) { if (is.null(data$title)) data$title <- fname @@ -37,14 +40,11 @@ createListSimpleCode <- function(path = ".", citation = NULL, mainfile = "main.g } } - extraPage <- sortExtraPages(extraPage) - - for (i in names(extraPage)) { - data <- mergeDocumentation(extraPage[[i]]) + for (i in names(extraPageBlocks)) { + data <- mergeDocumentation(extraPageBlocks[[i]]) data$name <- i full[[i]] <- createSimplePage(data) } - return(full) } diff --git a/R/extractDocumentation.R b/R/extractDocumentation.R index 776e514..666f3d1 100644 --- a/R/extractDocumentation.R +++ b/R/extractDocumentation.R @@ -28,18 +28,9 @@ extractDocumentation <- function(path, start_type = NULL, comment = "*'") { # no if (length(path) > 1) { out <- list() - extraPage <- list() - for (p in path) { - documentation <- extractDocumentation(p, start_type = start_type, comment = comment) - extraPage <- append(extraPage, documentation$extrapage) - documentation$extrapage <- NULL - out <- append(out, documentation) + out <- append(out, extractDocumentation(p, start_type = start_type, comment = comment)) } - - out <- mergeDocumentation(out) - out$extrapage <- extraPage - return(out) } @@ -55,7 +46,23 @@ extractDocumentation <- function(path, start_type = NULL, comment = "*'") { # no code <- "@(\\w*) ?(.*)$" pattern <- paste0("^(", escapeRegex(comment), ") *", code) type <- sub(pattern, "\\2", x[1]) - attribute <- sub(pattern, "\\3", x[1]) + + # extract attributes + rest <- sub(pattern, "\\3", x[1]) + attrPattern <- "^(\\{.+\\})(.*)$" + attributes <- NULL + if (grepl(attrPattern, rest)) { + tryCatch( + expr = { + attributes <- yaml::yaml.load(sub(attrPattern, "\\1", rest)) + x[1] <- sub("\\{.+\\}", "", x[1]) + }, + error = function(e) { + stop(paste0("Failed to parse yaml expression ", rest, "\n", e)) + } + ) + + } if (type == "equations") { x[1] <- sub(pattern, "\\1 \\3", x[1]) @@ -100,18 +107,10 @@ extractDocumentation <- function(path, start_type = NULL, comment = "*'") { # no if (type == "description") x <- c(x, "") out <- list() - # mark blocks with 'extrapage' attribute - attrPattern <- "^(\\w+)=\"(\\w+)\"[ \t]*$" - if (grepl(attrPattern, attribute)) { - attrName <- sub(attrPattern, "\\1", attribute) - page <- sub(attrPattern, "\\2", attribute) - if (attrName == "extrapage") { - x <- x[-1] - type <- paste0("extrapage-", type, "-", page) - } - } - - out[[type]] <- x + out[[type]] <- list( + content = x, + cfg = attributes + ) return(out) } @@ -122,7 +121,8 @@ extractDocumentation <- function(path, start_type = NULL, comment = "*'") { # no x <- c(paste0(comment, " @", start_type, " "), x) } - blocksStart <- suppressWarnings(grep(paste0("^", escapeRegex(comment), " @[a-z]*( |$)"), x)) + regex <- paste0("^", escapeRegex(comment), " @[a-z]*( |(\\{.+\\})|$)") + blocksStart <- suppressWarnings(grep(regex, x)) if (length(blocksStart) == 0) return(list()) blocksEnd <- c(blocksStart[-1] - 1, length(x)) @@ -132,17 +132,6 @@ extractDocumentation <- function(path, start_type = NULL, comment = "*'") { # no blocks <- c(blocks, extractBlock(x[blocksStart[i]:blocksEnd[i]], comment)) } - blocks <- mergeDocumentation(blocks) - - # extract and move extrapages to top-level list - extraPages <- blocks[grepl("^extrapage-", names(blocks))] - names(extraPages) <- sub("^extrapage-", "", names(extraPages)) - - blocks <- append( - blocks[!grepl("^extrapage-", names(blocks))], - list("extrapage" = extraPages) - ) - return(blocks) } diff --git a/R/flattenPageBlockList.R b/R/flattenPageBlockList.R new file mode 100644 index 0000000..96bc05e --- /dev/null +++ b/R/flattenPageBlockList.R @@ -0,0 +1,46 @@ +flattenPageBlockList <- function(data) { + extraPageBlocks <- list() + tmp <- data + data <- list() + + for (i in seq_along(tmp)) { + + # recursive handling of realizations sublists + if (names(tmp[i]) == "realizations") { + + for (j in seq_along(tmp[[i]])) { + extract <- flattenList(tmp[i]$realizations[[j]]) + l <- list(extract$blocks) + names(l) <- names(tmp[i]$realizations[j]) + data <- append(data, l) + extraPageBlocks <- appendExtraPageBlocks(extraPageBlocks, extract$extraPageBlocks) + } + + # handle base case + # separate extra page blocks from the rest and flatten the lists + } else { + + cfg <- tmp[[i]]$cfg + + # cfg entries other than extrapage are ignored for now + if (length(setdiff(names(cfg), "extrapage") > 0)) { + warning(paste0("The following settings are not supported and will be ignored: ", + paste0(setdiff(names(cfg), "extrapage"), collapse = ", "))) + } + + l <- list(tmp[[i]]$content) + names(l) <- names(tmp)[i] + + if (!is.null(cfg$extrapage)) { + # extra page blocks are sorted in sublists per page + extraPageBlocks[[cfg$extrapage]] <- append(extraPageBlocks[[cfg$extrapage]], l) + } else { + data <- append(data, l) + } + } + } + + data <- mergeDocumentation(data) + + return(list(blocks = data, extraPageBlocks = extraPageBlocks)) +} diff --git a/R/sortExtraPages.R b/R/sortExtraPages.R deleted file mode 100644 index 4aa318c..0000000 --- a/R/sortExtraPages.R +++ /dev/null @@ -1,20 +0,0 @@ -#' sortExtraPages -#' -#' helper transforming a flat list of extra page blocks into -#' sublists per page -#' -#' @param extraPage flat list of blocks with names indicating identifier and -#' extrapage (e.g. 'description-settings') -#' @author Falk Benke -sortExtraPages <- function(extraPage) { - out <- list() - attrPattern <- "^(\\w+)-(\\w+)" - for (i in seq_along(extraPage)) { - type <- sub(attrPattern, "\\1", names(extraPage)[i]) - page <- sub(attrPattern, "\\2", names(extraPage)[i]) - l <- list(extraPage[[i]]) - names(l) <- type - out[[page]] <- append(out[[page]], l) - } - return(out) -} From 347b8cc33a79d02c3f14d0036cef494fec9de822 Mon Sep 17 00:00:00 2001 From: Falk Benke Date: Mon, 20 Nov 2023 15:12:43 +0100 Subject: [PATCH 12/16] update documentation --- .buildlibrary | 2 +- CITATION.cff | 2 +- DESCRIPTION | 2 +- R/extractDocumentation.R | 5 ++++- R/flattenPageBlockList.R | 16 +++++++++++++++- man/extractDocumentation.Rd | 4 +++- man/flattenPageBlockList.Rd | 27 +++++++++++++++++++++++++++ man/sortExtraPages.Rd | 19 ------------------- vignettes/goxygen.Rmd | 20 +++++++++++--------- 9 files changed, 63 insertions(+), 34 deletions(-) create mode 100644 man/flattenPageBlockList.Rd delete mode 100644 man/sortExtraPages.Rd diff --git a/.buildlibrary b/.buildlibrary index 991e5ab..db0171c 100644 --- a/.buildlibrary +++ b/.buildlibrary @@ -1,4 +1,4 @@ -ValidationKey: '2754640' +ValidationKey: '2755340' AutocreateReadme: yes AcceptedWarnings: - 'Warning: package ''.*'' was built under R version' diff --git a/CITATION.cff b/CITATION.cff index 4d3c4d9..3e676ca 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -3,7 +3,7 @@ message: If you use this software, please cite it using the metadata from this f type: software title: 'goxygen: In-Code Documentation for ''GAMS''' version: 1.4.0 -date-released: '2023-11-15' +date-released: '2023-11-20' abstract: A collection of tools which extract a model documentation from 'GAMS' code and comments. In order to use the package you need to install 'pandoc' and 'pandoc-citeproc' first (). diff --git a/DESCRIPTION b/DESCRIPTION index eda12ff..5eb43b8 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -2,7 +2,7 @@ Package: goxygen Type: Package Title: In-Code Documentation for 'GAMS' Version: 1.4.0 -Date: 2023-11-15 +Date: 2023-11-20 Authors@R: c(person("Jan Philipp", "Dietrich", email = "dietrich@pik-potsdam.de", role = c("aut","cre")), person("Kristine", "Karstens", email = "karstens@pik-potsdam.de", role = "aut"), person("David", "Klein", email = "dklein@pik-potsdam.de", role = "aut"), diff --git a/R/extractDocumentation.R b/R/extractDocumentation.R index 666f3d1..3dabca6 100644 --- a/R/extractDocumentation.R +++ b/R/extractDocumentation.R @@ -4,12 +4,15 @@ #' of the line. In case of @realization also GAMS code is read and interpreted, in all other cases #' only the specific documentation comment is evaluated. #' +#' #' @param path path to the file(s) which should be evaluated #' @param start_type set type for first line of code. This can be useful #' to extract documentation even if no documentation type has been set (e.g #' reading equations.gms as type realization) #' @param comment comment chars used for documentation comments -#' @return a list of documentation pieces with type as name of each element +#' @return a nested list of documentation pieces with type as name of each element. +#' Each element contains two lists `content` containing the actual documentation +#' and `cfg` containing optional attributes passed with the type. #' @author Jan Philipp Dietrich #' @importFrom stringi stri_extract_all_regex stri_replace_all_regex #' @seealso \code{\link{goxygen}} diff --git a/R/flattenPageBlockList.R b/R/flattenPageBlockList.R index 96bc05e..e61c1cc 100644 --- a/R/flattenPageBlockList.R +++ b/R/flattenPageBlockList.R @@ -1,3 +1,17 @@ +#' flattenPageBlockList +#' +#' A helper that processes additional attributes for a given list of code documentation +#' blocks. Code documentation blocks are described as lists consisting of `content` +#' containing the documentation and a `cfg` list containing attributes. +#' +#' Supports sublists with code documentation per realization.#' +#' +#' @param data a list of documentation pieces with type as name of each element +#' @return a list with two elements (1) `blocks` containing the documentation elements +#' with type as name of the element and (2) `extraPageBlocks` containing lists for +#' blocks to be put on an extra pages, sorted by page names +#' +#' @author Falk Benke flattenPageBlockList <- function(data) { extraPageBlocks <- list() tmp <- data @@ -9,7 +23,7 @@ flattenPageBlockList <- function(data) { if (names(tmp[i]) == "realizations") { for (j in seq_along(tmp[[i]])) { - extract <- flattenList(tmp[i]$realizations[[j]]) + extract <- flattenPageBlockList(tmp[i]$realizations[[j]]) l <- list(extract$blocks) names(l) <- names(tmp[i]$realizations[j]) data <- append(data, l) diff --git a/man/extractDocumentation.Rd b/man/extractDocumentation.Rd index b8c7486..934d373 100644 --- a/man/extractDocumentation.Rd +++ b/man/extractDocumentation.Rd @@ -16,7 +16,9 @@ reading equations.gms as type realization)} \item{comment}{comment chars used for documentation comments} } \value{ -a list of documentation pieces with type as name of each element +a nested list of documentation pieces with type as name of each element. +Each element contains two lists `content` containing the actual documentation +and `cfg` containing optional attributes passed with the type. } \description{ Extracts doxygen-like GAMS documentation. Entries are introduced with an @type at the beginning diff --git a/man/flattenPageBlockList.Rd b/man/flattenPageBlockList.Rd new file mode 100644 index 0000000..03ab2b7 --- /dev/null +++ b/man/flattenPageBlockList.Rd @@ -0,0 +1,27 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/flattenPageBlockList.R +\name{flattenPageBlockList} +\alias{flattenPageBlockList} +\title{flattenPageBlockList} +\usage{ +flattenPageBlockList(data) +} +\arguments{ +\item{data}{a list of documentation pieces with type as name of each element} +} +\value{ +a list with two elements (1) `blocks` containing the documentation elements +with type as name of the element and (2) `extraPageBlocks` containing lists for +blocks to be put on an extra pages, sorted by page names +} +\description{ +A helper that processes additional attributes for a given list of code documentation +blocks. Code documentation blocks are described as lists consisting of `content` +containing the documentation and a `cfg` list containing attributes. +} +\details{ +Supports sublists with code documentation per realization.#' +} +\author{ +Falk Benke +} diff --git a/man/sortExtraPages.Rd b/man/sortExtraPages.Rd deleted file mode 100644 index 758cc58..0000000 --- a/man/sortExtraPages.Rd +++ /dev/null @@ -1,19 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/sortExtraPages.R -\name{sortExtraPages} -\alias{sortExtraPages} -\title{sortExtraPages} -\usage{ -sortExtraPages(extraPage) -} -\arguments{ -\item{extraPage}{flat list of blocks with names indicating identifier and -extrapage (e.g. 'description-settings')} -} -\description{ -helper transforming a flat list of extra page blocks into -sublists per page -} -\author{ -Falk Benke -} diff --git a/vignettes/goxygen.Rmd b/vignettes/goxygen.Rmd index 4830ef9..a9748f9 100644 --- a/vignettes/goxygen.Rmd +++ b/vignettes/goxygen.Rmd @@ -73,19 +73,21 @@ The short example GAMS file contains all identifiers available in goxygen. The r ``` until the `@code` tag resumes the documentation. -### extrapage attribute +### Additional attributes -Any of the identifiers , except for `@stop`, can be combined with the `extrapage` attribute as follows: `@identifier extrapage="page"`, for example: +Any of the identifiers can be combined with additional attributes, passed as a comma separated list of key/values within curly braces right after the identifier: `@identifier{attribute: value}`. - ``` - *' @description extrapage="settings" - *' The macro-economic core of REMIND is a Ramsey-type optimal growth model ... - ``` +As of now, the only supported attribute is `extrapage`. + +#### extrapage + +The `extrapage` attribute indicates that the documentation should be moved to a new page +as specified. -The documentation will be moved to a new page as specified (e.g. "settings" in the example above). -**Important:** The page name can only consist of characters and numbers and the rest of the documentation must follow on a new comment line. + ``` + *' @description{extrapage: "settings"} The macro-economic core of REMIND is a Ramsey-type optimal growth model ... + ``` - ## Further features * **Markdown syntax:** Since goxygen translates the code comments into Markdown before creating the other output formats from it, it is possible to use Markdown syntax in your goxygen comments. The markdown output itself is also stored in the documentation folder in the subfolder `markdown`. From ffe09579dd82b15270c86000e8d4c80f131ab4c4 Mon Sep 17 00:00:00 2001 From: Falk Benke Date: Mon, 20 Nov 2023 16:20:40 +0100 Subject: [PATCH 13/16] fix bug and improve documentation --- R/appendExtraPageBlocks.R | 7 +++++++ R/flattenPageBlockList.R | 17 ++++++++++++++--- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/R/appendExtraPageBlocks.R b/R/appendExtraPageBlocks.R index 9956511..54e9d6e 100644 --- a/R/appendExtraPageBlocks.R +++ b/R/appendExtraPageBlocks.R @@ -1,3 +1,10 @@ +#' appendExtraPageBlocks +#' +#' A helper to merge two nested lists for extra page blocks. +#' The lists are nested with lists for each page name on the first level and +#' all flattened documentation blocks on the second level. +#' +#' @author Falk Benke appendExtraPageBlocks <- function(blocks, add) { for (k in seq_along(add)) { page <- names(add[k]) diff --git a/R/flattenPageBlockList.R b/R/flattenPageBlockList.R index e61c1cc..fda05e3 100644 --- a/R/flattenPageBlockList.R +++ b/R/flattenPageBlockList.R @@ -4,7 +4,18 @@ #' blocks. Code documentation blocks are described as lists consisting of `content` #' containing the documentation and a `cfg` list containing attributes. #' -#' Supports sublists with code documentation per realization.#' +#' If a block entry has the `cgf` attribute `extrapage`, it is moved to a separate list +#' `extraPageBlocks`. +#' +#' Blocks without the `extrapage`attribute are moved to a list `blocks` and multiple +#' blocks with the same name are merged into one block. +#' +#' Cfg attributes other than `extrapage` are currently not supported and therefore ignored. +#' +#' Code documentation blocks are flattened, i.e. a list consisting of `content` and `cfg` +#' entries is replaced by the data in `cfg`. +#' +#' Supports nesting of blocks in `realizations` with code documentation per realization. #' #' @param data a list of documentation pieces with type as name of each element #' @return a list with two elements (1) `blocks` containing the documentation elements @@ -21,12 +32,12 @@ flattenPageBlockList <- function(data) { # recursive handling of realizations sublists if (names(tmp[i]) == "realizations") { - + data[["realizations"]] <- list() for (j in seq_along(tmp[[i]])) { extract <- flattenPageBlockList(tmp[i]$realizations[[j]]) l <- list(extract$blocks) names(l) <- names(tmp[i]$realizations[j]) - data <- append(data, l) + data[["realizations"]] <- append(data[["realizations"]], l) extraPageBlocks <- appendExtraPageBlocks(extraPageBlocks, extract$extraPageBlocks) } From 2f177dfa1fc8d4cce2771ad387a31a99a8422365 Mon Sep 17 00:00:00 2001 From: Falk Benke Date: Mon, 20 Nov 2023 17:00:06 +0100 Subject: [PATCH 14/16] add tests --- R/appendExtraPageBlocks.R | 8 ++++++-- tests/testthat/test-appendExtraPageBlocks.R | 21 +++++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 tests/testthat/test-appendExtraPageBlocks.R diff --git a/R/appendExtraPageBlocks.R b/R/appendExtraPageBlocks.R index 54e9d6e..41fe45b 100644 --- a/R/appendExtraPageBlocks.R +++ b/R/appendExtraPageBlocks.R @@ -1,8 +1,12 @@ #' appendExtraPageBlocks #' #' A helper to merge two nested lists for extra page blocks. -#' The lists are nested with lists for each page name on the first level and -#' all flattened documentation blocks on the second level. +#' The lists have the page name on the first level and flattened documentation +#' blocks on the second level. It is ensured that elements for the +#' same page are grouped in the same list. +#' +#' @param blocks a nested list for extra page blocks per page +#' @param add a seccond nested list for extra page blocks per page to be appended #' #' @author Falk Benke appendExtraPageBlocks <- function(blocks, add) { diff --git a/tests/testthat/test-appendExtraPageBlocks.R b/tests/testthat/test-appendExtraPageBlocks.R new file mode 100644 index 0000000..e4cb503 --- /dev/null +++ b/tests/testthat/test-appendExtraPageBlocks.R @@ -0,0 +1,21 @@ +test_that("appending works for a new page", { + l1 <- list(page1 = list(title = "page1", description = "bar"), page2 = list(title = "page2")) + l2 <- list(page3 = list(title = "page3")) + out <- appendExtraPageBlocks(l1, l2) + expect_true(length(out) == 3) + expect_true("page1" %in% names(out)) + expect_true("page2" %in% names(out)) + expect_true("page3" %in% names(out)) +}) + + +test_that("appending blocks for an exisiting page works", { + l1 <- list(page1 = list(title = "page1", description = "bar"), page2 = list(title = "page2")) + l2 <- list(page2 = list(description = "baz")) + out <- appendExtraPageBlocks(l1, l2) + expect_true(length(out) == 2) + expect_true("page1" %in% names(out)) + expect_true("page2" %in% names(out)) + expect_true(out[["page1"]][["description"]] == "bar") + expect_true(out[["page2"]][["description"]] == "baz") +}) From 7d7449ccadafb299f529da7f48691fd82daedde8 Mon Sep 17 00:00:00 2001 From: Falk Benke Date: Wed, 22 Nov 2023 15:32:44 +0100 Subject: [PATCH 15/16] add tests and documentation --- .buildlibrary | 2 +- CITATION.cff | 2 +- DESCRIPTION | 2 +- R/appendExtraPageBlocks.R | 5 +- R/createListModularCode.R | 1 + R/extractDocumentation.R | 1 - R/flattenPageBlockList.R | 28 ++++---- man/appendExtraPageBlocks.Rd | 23 ++++++ man/flattenPageBlockList.Rd | 19 ++++- tests/testthat/test-flattenPageBlockList.R | 82 ++++++++++++++++++++++ 10 files changed, 143 insertions(+), 22 deletions(-) create mode 100644 man/appendExtraPageBlocks.Rd create mode 100644 tests/testthat/test-flattenPageBlockList.R diff --git a/.buildlibrary b/.buildlibrary index db0171c..1f6f496 100644 --- a/.buildlibrary +++ b/.buildlibrary @@ -1,4 +1,4 @@ -ValidationKey: '2755340' +ValidationKey: '2755620' AutocreateReadme: yes AcceptedWarnings: - 'Warning: package ''.*'' was built under R version' diff --git a/CITATION.cff b/CITATION.cff index 3e676ca..c507c81 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -3,7 +3,7 @@ message: If you use this software, please cite it using the metadata from this f type: software title: 'goxygen: In-Code Documentation for ''GAMS''' version: 1.4.0 -date-released: '2023-11-20' +date-released: '2023-11-22' abstract: A collection of tools which extract a model documentation from 'GAMS' code and comments. In order to use the package you need to install 'pandoc' and 'pandoc-citeproc' first (). diff --git a/DESCRIPTION b/DESCRIPTION index 5eb43b8..c83e16e 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -2,7 +2,7 @@ Package: goxygen Type: Package Title: In-Code Documentation for 'GAMS' Version: 1.4.0 -Date: 2023-11-20 +Date: 2023-11-22 Authors@R: c(person("Jan Philipp", "Dietrich", email = "dietrich@pik-potsdam.de", role = c("aut","cre")), person("Kristine", "Karstens", email = "karstens@pik-potsdam.de", role = "aut"), person("David", "Klein", email = "dklein@pik-potsdam.de", role = "aut"), diff --git a/R/appendExtraPageBlocks.R b/R/appendExtraPageBlocks.R index 41fe45b..22aa713 100644 --- a/R/appendExtraPageBlocks.R +++ b/R/appendExtraPageBlocks.R @@ -1,12 +1,13 @@ #' appendExtraPageBlocks #' -#' A helper to merge two nested lists for extra page blocks. +#' A helper to merge two nested lists describing extra page blocks. #' The lists have the page name on the first level and flattened documentation #' blocks on the second level. It is ensured that elements for the #' same page are grouped in the same list. #' #' @param blocks a nested list for extra page blocks per page -#' @param add a seccond nested list for extra page blocks per page to be appended +#' @param add a seccond nested list for extra page blocks per page to be +#' appended to the first one #' #' @author Falk Benke appendExtraPageBlocks <- function(blocks, add) { diff --git a/R/createListModularCode.R b/R/createListModularCode.R index f81eebc..5632e83 100644 --- a/R/createListModularCode.R +++ b/R/createListModularCode.R @@ -218,6 +218,7 @@ createListModularCode <- function(cc, interfaces, path = ".", citation = NULL, u for (m in mLoop) { realizations <- collectRealizations(m, cc) extract <- flattenPageBlockList(realizations) + realizations <- extract$blocks extraPageBlocks <- appendExtraPageBlocks(extraPageBlocks, extract$extraPageBlocks) data <- append(out[[m]], realizations) diff --git a/R/extractDocumentation.R b/R/extractDocumentation.R index 3dabca6..11757bc 100644 --- a/R/extractDocumentation.R +++ b/R/extractDocumentation.R @@ -64,7 +64,6 @@ extractDocumentation <- function(path, start_type = NULL, comment = "*'") { # no stop(paste0("Failed to parse yaml expression ", rest, "\n", e)) } ) - } if (type == "equations") { diff --git a/R/flattenPageBlockList.R b/R/flattenPageBlockList.R index fda05e3..2c92888 100644 --- a/R/flattenPageBlockList.R +++ b/R/flattenPageBlockList.R @@ -5,22 +5,24 @@ #' containing the documentation and a `cfg` list containing attributes. #' #' If a block entry has the `cgf` attribute `extrapage`, it is moved to a separate list -#' `extraPageBlocks`. +#' `extraPageBlocks` in the output, as these need to be rendered separately later. #' -#' Blocks without the `extrapage`attribute are moved to a list `blocks` and multiple +#' Regular blocks without the `extrapage` attribute are moved to a list `blocks` and multiple #' blocks with the same name are merged into one block. #' -#' Cfg attributes other than `extrapage` are currently not supported and therefore ignored. +#' Cfg attributes other than `extrapage` are currently not supported and therefore +#' ignored, but a warning is thrown. #' -#' Code documentation blocks are flattened, i.e. a list consisting of `content` and `cfg` -#' entries is replaced by the data in `cfg`. +#' After processing the `cfg` attributes, the code documentation blocks are flattened, +#' i.e. a list consisting of a `content` and `cfg` entry is replaced by the data in `cfg`. #' -#' Supports nesting of blocks in `realizations` with code documentation per realization. +#' This helper supports nesting of blocks in `realizations` with code documentation +#' per realization. #' #' @param data a list of documentation pieces with type as name of each element -#' @return a list with two elements (1) `blocks` containing the documentation elements +#' @return a list with two element (1) `blocks` containing the documentation elements #' with type as name of the element and (2) `extraPageBlocks` containing lists for -#' blocks to be put on an extra pages, sorted by page names +#' blocks to be put on an extra pages, sorted by page names. #' #' @author Falk Benke flattenPageBlockList <- function(data) { @@ -30,9 +32,11 @@ flattenPageBlockList <- function(data) { for (i in seq_along(tmp)) { - # recursive handling of realizations sublists if (names(tmp[i]) == "realizations") { + + # recursive handling of realizations sublists data[["realizations"]] <- list() + for (j in seq_along(tmp[[i]])) { extract <- flattenPageBlockList(tmp[i]$realizations[[j]]) l <- list(extract$blocks) @@ -41,10 +45,8 @@ flattenPageBlockList <- function(data) { extraPageBlocks <- appendExtraPageBlocks(extraPageBlocks, extract$extraPageBlocks) } - # handle base case - # separate extra page blocks from the rest and flatten the lists } else { - + # handle base case cfg <- tmp[[i]]$cfg # cfg entries other than extrapage are ignored for now @@ -57,7 +59,7 @@ flattenPageBlockList <- function(data) { names(l) <- names(tmp)[i] if (!is.null(cfg$extrapage)) { - # extra page blocks are sorted in sublists per page + # extra page blocks are separated from the rest and sorted in sublists per page extraPageBlocks[[cfg$extrapage]] <- append(extraPageBlocks[[cfg$extrapage]], l) } else { data <- append(data, l) diff --git a/man/appendExtraPageBlocks.Rd b/man/appendExtraPageBlocks.Rd new file mode 100644 index 0000000..9b4cd48 --- /dev/null +++ b/man/appendExtraPageBlocks.Rd @@ -0,0 +1,23 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/appendExtraPageBlocks.R +\name{appendExtraPageBlocks} +\alias{appendExtraPageBlocks} +\title{appendExtraPageBlocks} +\usage{ +appendExtraPageBlocks(blocks, add) +} +\arguments{ +\item{blocks}{a nested list for extra page blocks per page} + +\item{add}{a seccond nested list for extra page blocks per page to be +appended to the first one} +} +\description{ +A helper to merge two nested lists describing extra page blocks. +The lists have the page name on the first level and flattened documentation +blocks on the second level. It is ensured that elements for the +same page are grouped in the same list. +} +\author{ +Falk Benke +} diff --git a/man/flattenPageBlockList.Rd b/man/flattenPageBlockList.Rd index 03ab2b7..87b91ae 100644 --- a/man/flattenPageBlockList.Rd +++ b/man/flattenPageBlockList.Rd @@ -10,9 +10,9 @@ flattenPageBlockList(data) \item{data}{a list of documentation pieces with type as name of each element} } \value{ -a list with two elements (1) `blocks` containing the documentation elements +a list with two element (1) `blocks` containing the documentation elements with type as name of the element and (2) `extraPageBlocks` containing lists for -blocks to be put on an extra pages, sorted by page names +blocks to be put on an extra pages, sorted by page names. } \description{ A helper that processes additional attributes for a given list of code documentation @@ -20,7 +20,20 @@ blocks. Code documentation blocks are described as lists consisting of `content` containing the documentation and a `cfg` list containing attributes. } \details{ -Supports sublists with code documentation per realization.#' +If a block entry has the `cgf` attribute `extrapage`, it is moved to a separate list +`extraPageBlocks` in the output, as these need to be rendered separately later. + +Regular blocks without the `extrapage` attribute are moved to a list `blocks` and multiple +blocks with the same name are merged into one block. + +Cfg attributes other than `extrapage` are currently not supported and therefore +ignored, but a warning is thrown. + +After processing the `cfg` attributes, the code documentation blocks are flattened, +i.e. a list consisting of a `content` and `cfg` entry is replaced by the data in `cfg`. + +This helper supports nesting of blocks in `realizations` with code documentation +per realization. } \author{ Falk Benke diff --git a/tests/testthat/test-flattenPageBlockList.R b/tests/testthat/test-flattenPageBlockList.R new file mode 100644 index 0000000..9c515c1 --- /dev/null +++ b/tests/testthat/test-flattenPageBlockList.R @@ -0,0 +1,82 @@ +test_that("flattening evaluates cfg attributes correctly", { + + l <- list( + title = list( + content = "foo", + cfg = NULL + ), + description = list( + content = "bar", + cfg = list( + extrapage = "page1", + size = "1" + ) + ) + ) + + expect_warning(flattenPageBlockList(l), + "The following settings are not supported and will be ignored: size") + f <- suppressWarnings(flattenPageBlockList(l)) + expect_true(all(c("blocks", "extraPageBlocks") %in% names(f))) + expect_true(f$blocks$title == "foo") + expect_true(names(f$extraPageBlocks) == "page1") + expect_true(f$extraPageBlocks$page1$description == "bar") +}) + +test_that("flattening works for realizations sublist", { + + l <- list( + title = list( + content = "foo", + cfg = NULL + ), + description = list( + content = "bar", + cfg = NULL + ), + realizations = list( + firstRealization = list( + description = list( + content = "baz", + cfg = NULL + ), + description = list( + content = "bam", + cfg = NULL + ) + ), + secondRealization = list( + description = list( + content = "bom", + cfg = NULL + ), + description = list( + content = "bim", + cfg = list( + extrapage = "page1" + ) + ), + description = list( + content = "bem", + cfg = list( + extrapage = "page1" + ) + ) + ) + ) + ) + f <- flattenPageBlockList(l) + + expect_true(all(c("blocks", "extraPageBlocks") %in% names(f))) + expect_true(f$blocks$title == "foo") + expect_true(f$blocks$description == "bar") + + expect_true(all(c("firstRealization", "secondRealization") %in% names(f$blocks$realizations))) + + expect_true(all(c("baz", "bam") %in% f$blocks$realizations$firstRealization$description)) + expect_true(all(c("bom") %in% f$blocks$realizations$secondRealization$description)) + + expect_true(length(f$extraPageBlocks$page1) == 2) + expect_true(f$extraPageBlocks$page1[1] == "bim") + expect_true(f$extraPageBlocks$page1[2] == "bem") +}) From 78ed6386d8bf755e064ccdc89382c833726d4421 Mon Sep 17 00:00:00 2001 From: Falk Benke Date: Thu, 23 Nov 2023 11:32:08 +0100 Subject: [PATCH 16/16] update author list --- .buildlibrary | 2 +- CITATION.cff | 5 ++++- DESCRIPTION | 5 +++-- README.md | 4 ++-- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/.buildlibrary b/.buildlibrary index 1f6f496..c2b2989 100644 --- a/.buildlibrary +++ b/.buildlibrary @@ -1,4 +1,4 @@ -ValidationKey: '2755620' +ValidationKey: '2755760' AutocreateReadme: yes AcceptedWarnings: - 'Warning: package ''.*'' was built under R version' diff --git a/CITATION.cff b/CITATION.cff index c507c81..008b9c2 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -3,7 +3,7 @@ message: If you use this software, please cite it using the metadata from this f type: software title: 'goxygen: In-Code Documentation for ''GAMS''' version: 1.4.0 -date-released: '2023-11-22' +date-released: '2023-11-23' abstract: A collection of tools which extract a model documentation from 'GAMS' code and comments. In order to use the package you need to install 'pandoc' and 'pandoc-citeproc' first (). @@ -20,6 +20,9 @@ authors: - family-names: Baumstark given-names: Lavinia email: lavinia@pik-potsdam.de +- family-names: Benke + given-names: Falk + email: benke@pik-potsdam.de license: BSD-2-Clause repository-code: https://github.com/pik-piam/goxygen doi: 10.5281/zenodo.1411404 diff --git a/DESCRIPTION b/DESCRIPTION index c83e16e..d17231c 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -2,11 +2,12 @@ Package: goxygen Type: Package Title: In-Code Documentation for 'GAMS' Version: 1.4.0 -Date: 2023-11-22 +Date: 2023-11-23 Authors@R: c(person("Jan Philipp", "Dietrich", email = "dietrich@pik-potsdam.de", role = c("aut","cre")), person("Kristine", "Karstens", email = "karstens@pik-potsdam.de", role = "aut"), person("David", "Klein", email = "dklein@pik-potsdam.de", role = "aut"), - person("Lavinia", "Baumstark", email = "lavinia@pik-potsdam.de", role = "aut")) + person("Lavinia", "Baumstark", email = "lavinia@pik-potsdam.de", role = "aut"), + person("Falk", "Benke", email = "benke@pik-potsdam.de", role = "aut")) Description: A collection of tools which extract a model documentation from 'GAMS' code and comments. In order to use the package you need to install 'pandoc' and 'pandoc-citeproc' first (). diff --git a/README.md b/README.md index aeb10e7..b65e1fa 100644 --- a/README.md +++ b/README.md @@ -48,14 +48,14 @@ In case of questions / problems please contact Jan Philipp Dietrich , R package version 1.4.0, . +Dietrich J, Karstens K, Klein D, Baumstark L, Benke F (2023). _goxygen: In-Code Documentation for 'GAMS'_. doi:10.5281/zenodo.1411404 , R package version 1.4.0, . A BibTeX entry for LaTeX users is ```latex @Manual{, title = {goxygen: In-Code Documentation for 'GAMS'}, - author = {Jan Philipp Dietrich and Kristine Karstens and David Klein and Lavinia Baumstark}, + author = {Jan Philipp Dietrich and Kristine Karstens and David Klein and Lavinia Baumstark and Falk Benke}, year = {2023}, note = {R package version 1.4.0}, doi = {10.5281/zenodo.1411404},