roxygen2/0000755000176200001440000000000015175154672012044 5ustar liggesusersroxygen2/tests/0000755000176200001440000000000015174650071013177 5ustar liggesusersroxygen2/tests/testthat/0000755000176200001440000000000015175154672015046 5ustar liggesusersroxygen2/tests/testthat/test-rd-family.R0000644000176200001440000001040115166171276020025 0ustar liggesuserstest_that("long families are wrapped", { out <- roc_proc_text( rd_roclet(), " #' Title #' @family Long family name #' long_function_name_________________________1 <- function() {} #' Title #' @family Long family name long_function_name_________________________2 <- function() {} #' Title #' @family Long family name #' long_function_name_________________________3 <- function() {} #' Title #' @family Long family name long_function_name_________________________4 <- function() {} " )[[1]] seealso <- out$get_value("seealso") expect_true(grepl("^Other Long family name:", seealso)) expect_equal(re_count(seealso, "\n"), 3) }) test_that("special names escaped in family tag", { out <- roc_proc_text( rd_roclet(), " #' Title #' @family Long family name f <- function() {} #' Title #' @family Long family name '%+%' <- function(a, b) {} " )[[1]] seealso <- out$get_value("seealso") expect_true(grepl("^Other Long family name:", seealso)) expect_match(seealso, "\\\\%\\+\\\\%") }) test_that("family links to name only, not all aliases", { out <- roc_proc_text( rd_roclet(), " #' Title #' @aliases f2 f3 #' #' @family many aliases f <- function() {} #' Title #' @aliases g2 g3 #' #' @family many aliases g <- function() {} " )[[1]] seealso <- out$get_value("seealso") expect_true(grepl("^Other many aliases:", seealso)) expect_match(seealso, r"(\code{\link)", fixed = TRUE) }) test_that("families listed in same order as input", { out <- roc_proc_text( rd_roclet(), " #' foo #' @family a foo <- function() {} #' foo #' @family b #' @family a bar <- function() {} #' foo #' @family b baz <- function() {} " )[[2]] seealso <- out$get_value("seealso") expect_match(seealso[1], "^Other b") expect_match(seealso[2], "^Other a") }) test_that("only functions get () suffix", { out <- roc_proc_text( rd_roclet(), " #' foo #' @family a foo <- function() {} #' bar #' @family a bar <- 1:10 " ) expect_equal( out[[1]]$get_value("seealso"), "Other a:\n\\code{\\link{bar}}" ) expect_equal( out[[2]]$get_value("seealso"), "Other a:\n\\code{\\link[=foo]{foo()}}" ) }) test_that("family also included in concepts", { out <- roc_proc_text( rd_roclet(), " #' foo #' @family a foo <- function() {} " )[[1]] expect_equal(out$get_value("concept"), "a") }) test_that("custom family prefixes can get colon if needed", { seealso <- function() { out <- roc_proc_text( rd_roclet(), " #' foo #' @family a foo <- function() {} #' bar #' @family a bar <- function() {} " )[[1]] out$get_value("seealso") } local_roxy_meta_set("rd_family_title", list(a = "Custom prefix")) expect_match(seealso(), "^Custom prefix:") local_roxy_meta_set("rd_family_title", list(a = "Custom prefix:")) expect_match(seealso(), "^Custom prefix:[^:]") }) test_that("custom family prefixes can include Markdown", { local_roxy_meta_set( "rd_family_title", list(a = "Custom ***strongly emphasized*** prefix") ) out <- roc_proc_text( rd_roclet(), " #' foo #' @family a foo <- function() {} #' bar #' @family a bar <- function() {} " )[[1]] expect_match( out$get_value("seealso"), "^Custom \\\\emph\\{\\\\strong\\{strongly emphasized\\}} prefix:" ) }) test_that("@family with @rdname doesn't produce duplicate seealso (#1530)", { out <- roc_proc_text( rd_roclet(), " #' Title #' @family a foo <- function() {} #' @rdname foo #' @family a bar <- function() {} #' baz #' @family a baz <- function() {} " ) seealso <- out[["foo.Rd"]]$get_value("seealso") expect_length(seealso, 1) expect_match(seealso, "^Other a:") }) test_that("careful ordering", { out <- roc_proc_text( rd_roclet(), " #' foo1 #' @family a foo1 <- function() {} #' foo2 #' @family a foo2 <- function() {} #' Foo3 #' @family a Foo3 <- function() {} #' foo #' @family a foo <- function() {} " ) expect_snapshot({ out }) }) roxygen2/tests/testthat/test-roxygenize.R0000644000176200001440000000023614527170577020354 0ustar liggesuserstest_that("can regenerate NAMESPACE even if its broken", { path <- local_package_copy(test_path("broken-namespace")) expect_snapshot(roxygenise(path)) }) roxygen2/tests/testthat/test-roxygenize-needs.R0000644000176200001440000000411015154546447021443 0ustar liggesuserstest_that("needs_roxygenize returns FALSE when all docs are current", { path <- local_package_copy(test_path("up-to-date")) suppressMessages(roxygenize(path)) expect_false(needs_roxygenize(path)) }) test_that("needs_roxygenize returns TRUE when source is newer", { path <- local_package_copy(test_path("up-to-date")) suppressMessages(roxygenize(path)) # Backdate the Rd file so source appears newer Sys.setFileTime(file.path(path, "man", "foo.Rd"), as.POSIXct("2000-01-01")) expect_snapshot(out <- needs_roxygenize(path)) expect_true(out) }) # rd_outdated ------------------------------------------------------------------ test_that("rd_outdated returns TRUE when source file is deleted", { path <- local_package_copy(test_path("up-to-date")) suppressMessages(roxygenize(path)) file.remove(file.path(path, "R", "foo.R")) expect_true(rd_outdated(file.path(path, "man", "foo.Rd"), path)) }) # rd_backref_sources ----------------------------------------------------------- test_that("rd_backref_sources parses single source file", { lines <- c( "% Generated by roxygen2: do not edit by hand", "% Please edit documentation in R/foo.R", "\\name{foo}" ) expect_equal(rd_backref_sources(lines, "/base"), "/base/R/foo.R") }) test_that("rd_backref_sources parses multiple source files", { lines <- c( "% Generated by roxygen2: do not edit by hand", "% Please edit documentation in R/foo.R, R/bar.R", "\\name{foo}" ) expect_equal( rd_backref_sources(lines, "/base"), c("/base/R/foo.R", "/base/R/bar.R") ) }) test_that("rd_backref_sources handles wrapped backref lines", { lines <- c( "% Generated by roxygen2: do not edit by hand", "% Please edit documentation in R/foo.R,", "% R/bar.R", "\\name{foo}" ) expect_equal( rd_backref_sources(lines, "/base"), c("/base/R/foo.R", "/base/R/bar.R") ) }) test_that("rd_backref_sources handles missing source file", { lines <- c( "% Generated by roxygen2: do not edit by hand", "\\name{foo}" ) expect_equal(rd_backref_sources(lines, "/base"), character()) }) roxygen2/tests/testthat/test-package_files.R0000644000176200001440000000133714527136371020723 0ustar liggesuserstest_that("respects order in Collate (#790)", { expect_equal( package_files('testCollateParse'), normalizePath(c("testCollateParse/R/b.R", "testCollateParse/R/c.R")) ) }) test_that("roxygen ignores files with matching pattern in .Rbuildignore", { path <- local_package_copy(test_path("testRbuildignore")) expect_equal(basename(package_files(path)), c("a.R", "ignore_me.R")) write_lines("^R/ignore_me.R$", file.path(path, ".Rbuildignore")) expect_equal(basename(package_files(path)), "a.R") # Continues to work even if empty lines or missing files write_lines( c("^R/ignore_me.R$", "", ".nonexistentfile"), file.path(path, ".Rbuildignore") ) expect_equal(basename(package_files(path)), "a.R") }) roxygen2/tests/testthat/collate/0000755000176200001440000000000015174650071016462 5ustar liggesusersroxygen2/tests/testthat/collate/pants.R0000644000176200001440000000003713523072405017725 0ustar liggesusers#' @include undershorts.R NULL roxygen2/tests/testthat/collate/belt.R0000644000176200001440000000013613523072405017526 0ustar liggesusers# The space before @ is missing on purpose (#342). #'@include pants.R #'@include shirt.R NULL roxygen2/tests/testthat/collate/watch.R0000644000176200001440000000001513523072405017702 0ustar liggesusersNULL x <- 2 roxygen2/tests/testthat/collate/jacket.R0000644000176200001440000000005213523072405020036 0ustar liggesusers#' @include tie.R #' @include belt.R NULL roxygen2/tests/testthat/collate/tie.R0000644000176200001440000000003113523072405017353 0ustar liggesusers#' @include shirt.R NULL roxygen2/tests/testthat/collate/socks.R0000644000176200001440000000000513523072405017715 0ustar liggesusersNULL roxygen2/tests/testthat/collate/shirt.R0000644000176200001440000000000513523072405017724 0ustar liggesusersNULL roxygen2/tests/testthat/collate/undershorts.R0000644000176200001440000000000513523072405021153 0ustar liggesusersNULL roxygen2/tests/testthat/collate/shoes.R0000644000176200001440000000010713523072405017717 0ustar liggesusers#' @include socks.R #' @include undershorts.R #' @include pants.R NULL roxygen2/tests/testthat/helper-test.R0000644000176200001440000000370715170121342017413 0ustar liggesusersexpect_equivalent_rd <- function(out1, out2) { out1$sections$backref <- NULL out2$sections$backref <- NULL expect_equal(out1, out2) } expect_equal_strings <- function(s1, s2, ignore_ws = TRUE) { if (ignore_ws) { s1 <- gsub("\\s", "", s1, perl = TRUE) s2 <- gsub("\\s", "", s2, perl = TRUE) } expect_equal(s1, s2) } expect_parse_failure <- function(code) { (expect_condition(expect_null(code))) } r6_doc <- function(text, env = new.env(parent = globalenv()), n = NULL) { eval(parse(text = text, keep.source = TRUE), envir = env) blocks <- merge_external_r6methods(parse_text(text, env = env)) # Pass 1: extract local docs (no inheritance) r6_blocks <- Filter( function(b) inherits(b, "roxy_block_r6class"), blocks ) all_docs <- lapply(r6_blocks, r6_class_from_block, env = env) names(all_docs) <- map_chr( r6_blocks, \(b) b$object$value$classname %||% b$object$alias %||% "" ) # Pass 2: resolve inheritance in dependency order target <- n %||% length(r6_blocks) parent_docs <- list() for (i in seq_along(all_docs)) { docs <- all_docs[[i]] topic_name <- names(all_docs)[[i]] docs$fields <- r6_resolve_fields(docs$fields, parent_docs, topic_name) docs$active_bindings <- r6_resolve_fields( docs$active_bindings, parent_docs, topic_name ) docs$methods$self <- lapply(docs$methods$self, function(method) { r6_resolve_method_params(method, parent_docs, topic_name) }) all_docs[[i]] <- docs parent_docs[[topic_name]] <- docs } all_docs[[target]] } local_package_copy <- function(path, env = caller_env(), set_version = TRUE) { temp_path <- withr::local_tempdir(.local_envir = env) file.copy(path, temp_path, recursive = TRUE) pkg_path <- dir(temp_path, full.names = TRUE)[[1]] if (set_version) { desc::desc_set( file = pkg_path, RoxygenNote = as.character(packageVersion("roxygen2")) ) } normalizePath(pkg_path, winslash = "/") } roxygen2/tests/testthat/test-rd-r6-methods-inherited.R0000644000176200001440000000225115156325073022504 0ustar liggesuserstest_that("can extract inherited methods", { text <- " A <- R6::R6Class('A', public = list( shared = function() 1, only_a = function() 2 ) ) #' Class B. B <- R6::R6Class('B', inherit = A, public = list( #' @description Method from B. shared = function() 3 ) )" docs <- r6_doc(text) expect_s3_class(docs$methods$inherited, "rd_r6_inherited") expect_equal(docs$methods$inherited$name, "only_a") }) test_that("no inherited methods when none exist", { text <- " C1 <- R6::R6Class('C1', cloneable = FALSE) #' Class C2 <- R6::R6Class('C2', inherit = C1, public = list( #' @description method1 meth1 = function() 1 ) )" docs <- r6_doc(text) expect_equal(docs$methods$inherited, rd_r6_inherited()) }) test_that("format.rd_r6_inherited renders method list", { inherited <- rd_r6_inherited( package = c("pkg", "pkg"), classname = c("A", "A"), name = c("foo", "bar") ) expect_snapshot(cat(format(inherited), sep = "\n")) }) test_that("format.rd_r6_inherited returns nothing when empty", { expect_null(format(rd_r6_inherited())) }) roxygen2/tests/testthat/test-options-config/0000755000176200001440000000000015163737466020766 5ustar liggesusersroxygen2/tests/testthat/test-options-config/DESCRIPTION0000644000176200001440000000014415163737466022473 0ustar liggesusersPackage: testOptionsConfig Config/roxygen2/old_usage: TRUE Config/roxygen2/packages: mypkg1, mypkg2 roxygen2/tests/testthat/test-rd-params.R0000644000176200001440000000425015154537013020024 0ustar liggesuserstest_that("@param documents arguments", { out <- roc_proc_text( rd_roclet(), " #' A #' @param a first #' @param z last a <- function(a=1, z=2) {} " )[[1]] expect_equal(out$get_value("param"), c(a = "first", z = "last")) }) test_that("backtick-quoted @param names are parsed correctly (#1696)", { out <- roc_proc_text( rd_roclet(), " #' A #' @param `arg 1` first #' @param `arg 2` last a <- function(`arg 1` = 1, `arg 2` = 2) {} " )[[1]] expect_equal( out$get_value("param"), c("arg 1" = "first", "arg 2" = "last") ) }) test_that("grouped args get spaces", { out <- roc_proc_text( rd_roclet(), " #' A #' @param a,z Two arguments a <- function(a=1, z=2) {} " )[[1]] expect_match(out$get_rd("param"), "a, z") }) test_that("empty @param generates warning", { block <- " #' A #' @param #' a <- function() {} " expect_snapshot(. <- roc_proc_text(rd_roclet(), block)) }) test_that("indented @param bullet list is not nested (#1102)", { out <- roc_proc_text( rd_roclet(), " #' Foo #' @md #' @param foo #' * A #' * B #' * C a <- function(foo) {} " )[[1]] expect_match(out$get_rd("param"), "\\item A", fixed = TRUE) expect_match(out$get_rd("param"), "\\item B", fixed = TRUE) expect_match(out$get_rd("param"), "\\item C", fixed = TRUE) expect_no_match(out$get_rd("param"), "itemize\\{\n\\\\item B") }) test_that("data objects don't get params", { out <- roc_proc_text( rd_roclet(), " #' x #' @rdname xy x <- 'x' " )[[1]] expect_equal(out$get_value("param"), NULL) }) test_that("arguments ordered by usage", { out <- roc_proc_text( rd_roclet(), " #' A #' #' @param y Y #' @param x X #' @rdname rd a <- function(x, y) {} " )[[1]] expect_named(out$get_value("param"), c("x", "y")) }) test_that("multiple arguments ordered by first", { out <- roc_proc_text( rd_roclet(), " #' Title #' #' @param y Y #' @param x,z X,Z #' @param w W b <- function(x, y, z, w) {} " )[[1]] expect_named(out$get_value("param"), c("x,z", "y", "w")) }) roxygen2/tests/testthat/test-utils.R0000644000176200001440000000656515166171276017321 0ustar liggesuserstest_that("nice_name leaves ok chars unchanged", { expect_equal(nice_name("abc"), "abc") expect_equal(nice_name("a_b-c.R"), "a_b-c.R") }) test_that("nice_name protects against invalid characters", { expect_equal(nice_name("a<-"), "a-set") expect_equal(nice_name("[.a"), "sub-.a") }) test_that("nice_name strips ::", { expect_equal(nice_name("print,pkg::Class-method"), "print-pkg-Class-method") }) test_that("is_namespaced works as expected", { expect_true(is_namespaced("a::b")) expect_false(is_namespaced("b::")) expect_false(is_namespaced("'::'")) }) test_that("write_if_different produces informative messages", { dir <- withr::local_tempdir() path <- file.path(dir, "test.R") write_lines(made_by("#"), path) expect_snapshot(write_if_different(path, "a <- 2")) write_lines("a <- 1", path) # strip temp path expect_snapshot( write_if_different(path, "a <- 2"), transform = function(x) gsub(dir, "", x, fixed = TRUE) ) path <- file.path(dir, "+.R") expect_snapshot(write_if_different(path, "a <- 2")) }) test_that("write_if_different and end of line", { cnt_unix <- c("foo\nbar\nbaz", "foobar") cnt_win <- c("foo\r\nbar\r\nbaz", "foobar") cnt_mix <- c("foo\nbar\r\nbaz", "foobar") tmp <- tempfile("roxy-", fileext = ".Rd") on.exit(unlink(tmp), add = TRUE) # do not change unix le write_lines(cnt_unix, tmp, line_ending = "\n") expect_message(write_if_different(tmp, cnt_unix, check = FALSE), NA) expect_message(write_if_different(tmp, cnt_win, check = FALSE), NA) expect_message(write_if_different(tmp, cnt_mix, check = FALSE), NA) # do not change windows le write_lines(cnt_win, tmp, line_ending = "\r\n") expect_message(write_if_different(tmp, cnt_unix, check = FALSE), NA) expect_message(write_if_different(tmp, cnt_win, check = FALSE), NA) expect_message(write_if_different(tmp, cnt_mix, check = FALSE), NA) # change mixed le to windows tmp_win <- tempfile("roxy-", fileext = ".Rd") on.exit(unlink(tmp_win), add = TRUE) write_lines(cnt_win, tmp_win, line_ending = "\r\n") # write_lines changes line endings, so we use writeBin to create a file with mixed # line endings raw_mix <- charToRaw(paste0(paste0(cnt_mix, collapse = "\r\n"), "\r\n")) writeBin(raw_mix, tmp) expect_message(write_if_different(tmp, cnt_unix, check = FALSE), "Writing ") expect_identical(readBin(tmp, "raw", 100), readBin(tmp_win, "raw", 100)) writeBin(raw_mix, tmp) expect_message(write_if_different(tmp, cnt_win, check = FALSE), "Writing ") expect_identical(readBin(tmp, "raw", 100), readBin(tmp_win, "raw", 100)) writeBin(raw_mix, tmp) expect_message(write_if_different(tmp, cnt_mix, check = FALSE), "Writing ") expect_identical(readBin(tmp, "raw", 100), readBin(tmp_win, "raw", 100)) }) test_that("write_if_different touches unchanged files", { dir <- withr::local_tempdir() path <- file.path(dir, "test.R") write_lines(made_by("#"), path) old_time <- as.POSIXct("2000-01-01") Sys.setFileTime(path, old_time) write_if_different(path, made_by("#")) expect_gt(file.mtime(path), old_time) }) test_that("write_if_different produces correct command hyperlink", { testthat::local_reproducible_output(hyperlinks = TRUE) dir <- withr::local_tempdir() path <- file.path(dir, "test.R") write_lines(made_by("#"), path) expect_snapshot(write_if_different( path, "a <- 2", command = "rlang::inform('hi')" )) }) roxygen2/tests/testthat/test-object-s3.R0000644000176200001440000000350415157043740017732 0ustar liggesuserstest_that("primitive generics detected", { expect_true(is_s3_generic("[")) expect_true(is_s3_method("[.data.frame")) expect_true(is_s3_generic("mean")) expect_true(is_s3_method("mean.default")) expect_true(is_s3_generic("c")) expect_true(is_s3_method("c.Date")) }) test_that("non-functions are not generics", { a <- TRUE b <- NULL expect_false(is_s3_generic("a")) expect_false(is_s3_generic("b")) }) test_that("ignores non-function objects when looking for generics", { c <- data.frame() expect_true(is_s3_generic("c")) }) test_that("user defined generics & methods detected", { my_method <- function(x) UseMethod("mymethod") my_method.character <- function(x) x expect_true(is_s3_generic("my_method")) expect_true(is_s3_method("my_method.character")) }) test_that("methods for group generics detected", { Ops.myclass <- function(x) x expect_false(is_s3_generic("Ops.myclass")) expect_true(is_s3_method("Ops.myclass")) }) test_that("user defined generics detected even if use non-standard", { my_method <- function(x) { x <- 1 if (x > 2) UseMethod("mymethod") } expect_true(is_s3_generic("my_method")) }) test_that("S4 generics are not treated as S3 methods", { env <- new.env(parent = globalenv()) env$all.equal <- { methods::setGeneric("all.equal") all.equal } on.exit(methods::removeGeneric("all.equal"), add = TRUE) expect_false(is_s3_method("all.equal", env)) }) test_that("all.equal is matched correctly", { expect_equal(find_generic("all.equal.numeric"), c("all.equal", "numeric")) expect_equal( find_generic("all.equal.data.frame"), c("all.equal", "data.frame") ) }) test_that("user defined functions override primitives", { c <- function(x) x + 1 c.test <- function(x) x + 3 expect_false(is_s3_generic("c")) expect_false(is_s3_method("c")) }) roxygen2/tests/testthat/test-tag-parser.R0000644000176200001440000000657715172443612020222 0ustar liggesuserstest_that("tags containing only whitespace generate warning", { expect_snapshot({ tag <- roxy_test_tag(" ") expect_parse_failure(tag_value(tag)) expect_parse_failure(tag_inherit(tag)) expect_parse_failure(tag_name(tag)) expect_parse_failure(tag_name_description(tag)) expect_parse_failure(tag_code(tag)) expect_parse_failure(tag_examples(tag)) expect_parse_failure(tag_markdown(tag)) expect_parse_failure(tag_markdown_with_sections(tag)) }) }) test_that("tags check for mismatched parents gives useful warnings", { expect_snapshot({ tag <- roxy_test_tag("a {") expect_parse_failure(tag_value(tag)) expect_parse_failure(tag_inherit(tag)) expect_parse_failure(tag_name(tag)) expect_parse_failure(tag_two_part(tag)) expect_parse_failure(tag_words(tag)) expect_parse_failure(tag_examples(tag)) "markdown tags return empty values" tag_markdown(tag) tag_markdown_with_sections(tag) }) local_markdown() markdown_on() expect_snapshot({ tag <- roxy_test_tag("# one\ntwo\n# three\nfour {") tag_markdown_with_sections(tag) }) }) test_that("tag_inhert checks for valid inherits", { expect_snapshot({ tag <- roxy_test_tag("foo params section") . <- tag_inherit(tag) }) }) test_that("tag_name() checks for valid names", { expect_snapshot({ tag <- roxy_test_tag("a b c") expect_parse_failure(tag_name(tag)) }) }) test_that("tag_two_part() gives useful warnings", { local_markdown() expect_snapshot({ tag <- roxy_test_tag("") expect_parse_failure(tag_two_part( tag, "a name", "a value", required = FALSE )) expect_parse_failure(tag_two_part(tag, "a name", "a value")) }) }) test_that("tag_two_part() falls back to space split for unclosed backtick", { tag <- roxy_test_tag("`unclosed description") out <- tag_two_part(tag, "a name", "a value", required = FALSE) expect_equal(out$val, list(name = "`unclosed", description = "description")) }) test_that("tag_words() gives useful warnings", { expect_snapshot({ tag <- roxy_test_tag("a b") expect_parse_failure(tag_words(tag, 3, 3)) expect_parse_failure(tag_words(tag, 1, 1)) }) }) test_that("tag_words() warns on multi-line content and preserves value", { expect_snapshot({ tag <- roxy_test_tag("a\nb") out <- tag_words(tag) }) expect_equal(out$val, c("a", "b")) }) test_that("tag_two_part() warns on multi-line content and preserves value", { expect_snapshot({ tag <- roxy_test_tag("foo bar\nbaz") out <- tag_two_part(tag, "a name", "a value") }) expect_equal(out$val, list(name = "foo", description = "bar\nbaz")) tag <- roxy_test_tag("foo bar\nbaz") out <- expect_silent(tag_two_part(tag, "a name", "a value", multiline = TRUE)) expect_equal(out$val, list(name = "foo", description = "bar\nbaz")) }) test_that("tag_value() warns on multi-line content and preserves value", { expect_snapshot({ tag <- roxy_test_tag("a\nb") out <- tag_value(tag) }) expect_equal(out$val, "a\nb") }) test_that("tag_words_line() is deprecated", { expect_snapshot({ tag <- roxy_test_tag("a b") tag_words_line(tag) }) }) test_that("tag_toggle() gives useful warnings", { expect_snapshot({ tag <- roxy_test_tag("x") tag_toggle(tag) }) }) test_that("tag_code() gives useful warnings", { expect_snapshot({ tag <- roxy_test_tag("a + ") tag_code(tag) }) }) roxygen2/tests/testthat/test-rd-s7.R0000644000176200001440000000301215166171276017075 0ustar liggesuserstest_that("@prop creates Additional properties section", { text <- " #' Important class. #' #' @prop a prop a #' @prop b prop b a <- function() {} " out <- roc_proc_text(rd_roclet(), text)[[1]] value <- out$get_value("prop") expect_equal(value$name, c("a", "b")) expect_equal(value$description, c("prop a", "prop b")) expect_snapshot(out$get_section("prop")) }) test_that("@prop class@name groups by class", { text <- " #' Classes. #' #' @prop Parent@x prop x #' @prop Child@y prop y a <- function() {} " out <- roc_proc_text(rd_roclet(), text)[[1]] value <- out$get_value("prop") expect_equal(value$class, c("Parent", "Child")) expect_equal(value$name, c("x", "y")) expect_snapshot(out$get_section("prop")) }) test_that("@prop with mismatched braces warns and doesn't crash", { text <- " #' A class. #' #' @prop a prop a #' } a <- function() {} " expect_snapshot(. <- roc_proc_text(rd_roclet(), text)) }) test_that("@prop class@name warns on invalid spec", { text <- " #' A class. #' #' @prop @x prop x a <- function() {} " expect_snapshot(. <- roc_proc_text(rd_roclet(), text)) }) test_that("@prop without class doesn't use subsections", { text <- " #' A class. #' #' @prop a prop a #' @prop b prop b MyClass <- function() {} " out <- roc_proc_text(rd_roclet(), text)[[1]] rd <- format(out$get_section("prop")) expect_no_match(rd, "subsection") expect_match(rd, "Additional properties") }) roxygen2/tests/testthat/testR6/0000755000176200001440000000000015160334463016225 5ustar liggesusersroxygen2/tests/testthat/testR6/R/0000755000176200001440000000000015157043550016426 5ustar liggesusersroxygen2/tests/testthat/testR6/R/classes.R0000644000176200001440000000444115157043550020211 0ustar liggesusers#' Class A #' #' @description #' Class A description. #' #' @details #' Class A details #' #' @param Z zzzzzzz #' @example example.R A <- R6::R6Class( "A", public = list( #' @field field1 A field 1. field1 = NULL, #' @description A method 1. #' @examples #' ## Example for meth1 meth1 = function(Z) {}, #' @description Method 2 description. #' @details Method 2 details. #' @param Z Overriding Z argument for meth2. #' @param ... Rest. #' @return A value. #' @examples #' ## Example for meth2 #' ## Second line #' ## Third line meth2 = function(Z = 10, ...) {}, #' @field field2 A field 2. field2 = "foobar", #' @description Method 3. #' @param x An argument. meth3 = function(x) {}, #' @field field3 A field 3. field3 = "baz" ), active = list( #' @field active1 A binding 1. active1 = function(x) {}, #' @field active2 A binding 2. active2 = function(x) {}, #' @field active3 A binding 3. active3 = function(x) {} ) ) #' Class B #' #' @description #' Class B Description. #' #' @details #' Class B details. B <- R6::R6Class( "B", inherit = A, public = list( #' @field field1 B field 1. field1 = NULL, #' @field field4 B field 4. field4 = NULL, #' @description B method 1. #' @param Z Still zzzzzzzz. meth1 = function(Z) {}, #' @description B method 4. meth4 = function() {} ), active = list( #' @field active1 B binding 1. active1 = function(x) {}, #' @field active4 B binding 4. active4 = function(x) {}, #' @field active5 B binding 5. active5 = function(x) {} ) ) #' Class C #' #' @description #' Class C Description. #' #' @details #' Class C details. C <- R6::R6Class( "C", inherit = B, cloneable = FALSE, public = list( #' @field field2 C field 2. field2 = NULL, #' @description C method 2. #' @param Z zzzzz #' @param ... etc meth2 = function(Z = 10, ...) {}, #' @field field5 C field 5. field5 = "foobar", #' @description C method 5. meth5 = function() {} ), active = list( #' @field active2 C binding 2. active2 = function(x) {}, #' @field active4 C binding 4. active4 = function(x) {}, #' @field active6 C binding 6. active6 = function(x) {} ) ) roxygen2/tests/testthat/testR6/example.R0000644000176200001440000000001515156561537020010 0ustar liggesusersa <- A$new() roxygen2/tests/testthat/testR6/DESCRIPTION0000644000176200001440000000015115156257756017746 0ustar liggesusersPackage: testR6 Title: Test R6 class documentation License: MIT Version: 0.1 Encoding: UTF-8 Imports: R6 roxygen2/tests/testthat/test-rd-name-alias.R0000644000176200001440000000767415166171276020575 0ustar liggesusers# name -------------------------------------------------------------------- test_that("name captured from assignment", { out <- roc_proc_text( rd_roclet(), " #' Title. a <- function() {} " )[[1]] expect_equal(out$get_value("name"), "a") expect_equal(out$get_value("alias"), "a") expect_equal(out$get_value("title"), "Title.") }) test_that("name also captured from assignment by =", { out <- roc_proc_text( rd_roclet(), " #' Title. a = function() {} " )[[1]] expect_equal(out$get_value("name"), "a") expect_equal(out$get_value("alias"), "a") expect_equal(out$get_value("title"), "Title.") }) test_that("`$` not to be parsed as assignee in foo$bar(a = 1)", { out <- roc_proc_text( rd_roclet(), " #' foo object foo <- list(bar = function(a) a) foo$bar(a = 1) " )[[1]] expect_equal(out$get_value("name"), "foo") }) test_that("names escaped, not quoted", { out <- roc_proc_text( rd_roclet(), " #' Title '%a%' <- function(x, y) x + y " )[[1]] expect_equal(out$get_rd("name"), "\\name{\\%a\\%}") }) test_that("quoted names captured from assignment", { out <- roc_proc_text( rd_roclet(), " #' Title. \"myfunction\" <- function(...) {} " )[[1]] expect_equal(out$get_value("name"), "myfunction") expect_equal(out$get_value("alias"), "myfunction") out <- roc_proc_text( rd_roclet(), " #' Title. `myfunction` <- function(...) {} " )[[1]] expect_equal(out$get_value("name"), "myfunction") expect_equal(out$get_value("alias"), "myfunction") out <- roc_proc_text( rd_roclet(), " #' Title. \"my function\" <- function(...) {} " )[[1]] expect_equal(out$get_value("name"), "my function") expect_equal(out$get_value("alias"), "my function") }) test_that("@name overides default", { out <- roc_proc_text( rd_roclet(), " #' A #' @name b a <- function() {} " )[[1]] expect_equal(out$get_value("name"), "b") expect_setequal(out$get_value("alias"), c("a", "b")) }) # alias ------------------------------------------------------------------- test_that("aliases split into pieces", { out <- roc_proc_text( rd_roclet(), " #' @aliases a b #' @title a #' @name a NULL" )[[1]] expect_match(out$get_value("alias"), "a", all = FALSE, fixed = TRUE) expect_match(out$get_value("alias"), "b", all = FALSE, fixed = TRUE) }) test_that("aliases escaped, not quoted", { out1 <- roc_proc_text( rd_roclet(), " #' @name %a% #' @aliases a #' @title a NULL" )[[1]] expect_equal(out1$get_rd("alias"), c("\\alias{\\%a\\%}", "\\alias{a}")) out2 <- roc_proc_text( rd_roclet(), " #' @name a #' @aliases %a% #' @title a NULL" )[[1]] expect_equal(out2$get_rd("alias"), c("\\alias{a}", "\\alias{\\%a\\%}")) }) test_that("can use NULL to suppress default aliases", { out <- roc_proc_text( rd_roclet(), " #' @aliases NULL #' @title a #' @name a NULL" )[[1]] expect_equal(out$get_value("alias"), character()) }) test_that("aliases get deduplicated", { out <- roc_proc_text( rd_roclet(), " #' @aliases a b a #' @title a #' @name a NULL" )[[1]] expect_equal(out$get_rd("alias"), c("\\alias{a}", "\\alias{b}")) }) test_that("aliases get deduplicated with defaults suppressed", { out <- roc_proc_text( rd_roclet(), " #' @aliases NULL b c b #' @title a #' @name a NULL" )[[1]] expect_equal(out$get_rd("alias"), c("\\alias{b}", "\\alias{c}")) }) test_that("refclass with assignment gets both aliases", { out <- roc_proc_text( rd_roclet(), " #' Title B3 <- setRefClass('B3') " )[[1]] expect_equal(out$get_value("alias"), c("B3-class", "B3")) }) test_that("refclass gets -class alias", { out <- roc_proc_text( rd_roclet(), " #' Title setRefClass('B2') " )[[1]] expect_equal(out$get_value("alias"), "B2-class") }) roxygen2/tests/testthat/test-safety.R0000644000176200001440000000051514262717326017437 0ustar liggesuserstest_that("determine if file is autogenerated", { expect_true(made_by_roxygen("made-by-roxygen/with-header.Rd")) expect_false(made_by_roxygen("made-by-roxygen/without-header.Rd")) expect_false(made_by_roxygen("made-by-roxygen/empty.Rd")) expect_true(check_made_by(made_by("#"))) expect_false(check_made_by(character())) }) roxygen2/tests/testthat/testUtf8Escape/0000755000176200001440000000000014520730024017675 5ustar liggesusersroxygen2/tests/testthat/testUtf8Escape/R/0000755000176200001440000000000014520730024020076 5ustar liggesusersroxygen2/tests/testthat/testUtf8Escape/R/a.R0000644000176200001440000000007714520730024020445 0ustar liggesusers#' Title #' #' @param b Some label a <- function(b = '7°C') 1 roxygen2/tests/testthat/testUtf8Escape/DESCRIPTION0000644000176200001440000000032113523072405021403 0ustar liggesusersPackage: testUtf8Escape Title: Check that utf8 escapes are round tripped ok License: GPL-2 Description: Author: Hadley Maintainer: Hadley Encoding: UTF-8 Version: 0.1 roxygen2/tests/testthat/test-rd-inherit.R0000644000176200001440000004715715166171276020230 0ustar liggesusers# Rd parsing -------------------------------------------------------------- test_that("can round-trip Rd", { rd <- tools::parse_Rd(test_path("escapes.Rd")) field <- find_field(rd, "description") lines <- strsplit(field, "\n")[[1]] expect_equal( lines, c( "% Comment", # Latex comments shouldn't be escaped "\\code{\\\\}" # Backslashes in code should be ) ) }) test_that("\\links are transformed", { out <- roc_proc_text( rd_roclet(), " #' Title #' #' @inheritParams digest::sha1 wrapper <- function(algo) {}" )[[1]] # \\link{} should include [digest] expect_snapshot_output(out$get_section("param")) }) test_that("markdown doesn't get get extra parens", { expect_equal(rd2text(parse_rd("\\href{a}{b}")), "\\href{a}{b}\n") expect_equal(rd2text(parse_rd("\\ifelse{a}{b}{c}")), "\\ifelse{a}{b}{c}\n") expect_equal(rd2text(parse_rd("\\if{a}{b}")), "\\if{a}{b}\n") }) test_that("relative links converted to absolute", { link_to_base <- function(x) { rd2text(parse_rd(x), package = "base") } expect_equal( link_to_base("\\link{abbreviate}"), "\\link[base]{abbreviate}\n" ) expect_equal( link_to_base("\\link[=abbreviate]{abbr}"), "\\link[base:abbreviate]{abbr}\n" ) # Doesn't affect links that already have expect_equal( link_to_base("\\link[foo]{abbreviate}"), "\\link[foo]{abbreviate}\n" ) expect_equal( link_to_base("\\link[foo::abbreviate]{abbr}"), "\\link[foo::abbreviate]{abbr}\n" ) # linkS4class converted to absolute link (#1634) link_to_methods <- function(x) { rd2text(parse_rd(x), package = "methods") } expect_equal( link_to_methods("\\linkS4class{genericFunction}"), "\\link[methods:genericFunction-class]{genericFunction}\n" ) }) # tag parsing ------------------------------------------------------------- test_that("invalid syntax gives useful warning", { block <- " #' @inheritDotParams #' @inheritSection NULL " expect_snapshot(. <- roc_proc_text(rd_roclet(), block)) }) test_that("warns on unknown inherit type", { text <- " #' @inherit fun blah NULL " expect_snapshot(parse_text(text)) }) test_that("no options gives default values", { block <- parse_text( " #' @inherit fun NULL " )[[1]] expect_equal(block_get_tag_value(block, "inherit")$fields, inherit_components) }) test_that("some options overrides defaults", { block <- parse_text( " #' @inherit fun return NULL " )[[1]] expect_equal(block_get_tag_value(block, "inherit")$fields, "return") }) # Inherit return values --------------------------------------------------- test_that("can inherit return values from roxygen topic", { out <- roc_proc_text( rd_roclet(), " #' A. #' #' @return ABC a <- function(x) {} #' B #' #' @inherit a b <- function(y) {} " )[[2]] expect_equal(out$get_value("value"), "ABC") }) test_that("takes value from first with return", { out <- roc_proc_text( rd_roclet(), " #' A1 #' @return A a1 <- function(x) {} #' A2 a2 <- function() {} #' B #' @return B b <- function(x) {} #' C #' @inherit a2 #' @inherit b #' @inherit a1 c <- function(y) {} " )[[3]] expect_equal(out$get_value("value"), "B") }) test_that("can inherit return value from external function", { out <- roc_proc_text( rd_roclet(), " #' A1 #' @inherit base::mean a1 <- function(x) {} " )[[1]] expect_match(out$get_value("value"), "before the mean is computed.$") expect_match(out$get_value("value"), "^If \\\\code") }) # Inherit seealso --------------------------------------------------------- test_that("can inherit return values from roxygen topic", { out <- roc_proc_text( rd_roclet(), " #' A. #' #' @seealso ABC a <- function(x) {} #' B #' #' @inherit a b <- function(y) {} " )[[2]] expect_equal(out$get_value("seealso"), "ABC") }) # Inherit description and details ----------------------------------------- test_that("can inherit description from roxygen topic", { out <- roc_proc_text( rd_roclet(), " #' A. #' #' B #' #' @return ABC a <- function(x) {} #' @title C #' @inherit a description b <- function(y) {} " )[[2]] expect_equal(out$get_value("description"), "B") }) test_that("inherits description if omitted", { out <- roc_proc_text( rd_roclet(), " #' A. #' #' B #' #' @return ABC a <- function(x) {} #' C #' @inherit a description b <- function(y) {} " )[[2]] expect_equal(out$get_value("description"), "B") }) test_that("can inherit details from roxygen topic", { out <- roc_proc_text( rd_roclet(), " #' A. #' #' B #' #' C #' #' @return ABC a <- function(x) {} #' D #' #' E #' #' @inherit a details b <- function(y) {} " )[[2]] expect_equal(out$get_value("description"), "E") expect_equal(out$get_value("details"), "C") }) # Inherit sections -------------------------------------------------------- test_that("inherits missing sections", { out <- roc_proc_text( rd_roclet(), " #' A. #' @section A:1 #' @section B:1 a <- function(x) {} #' D #' #' @section A:2 #' @inherit a sections b <- function(y) {} " )[[2]] section <- out$get_value("section") expect_equal(section$title, c("A", "B")) expect_equal(section$content, c("2", "1")) }) test_that("can inherit single section", { out <- roc_proc_text( rd_roclet(), " #' A. #' @section A:1 #' @section B:1 a <- function(x) {} #' D #' #' @inheritSection a B b <- function(y) {} " )[[2]] section <- out$get_value("section") expect_equal(section$title, "B") expect_equal(section$content, "1") }) test_that("warns if can't find section", { code <- " #' a a <- function(x) {} #' b #' #' @inheritSection a A b <- function(y) {} " expect_snapshot(. <- roc_proc_text(rd_roclet(), code)) }) # Inherit parameters ------------------------------------------------------ test_that("match_params can ignore . prefix", { expect_equal(match_param("a", c("x", "y", "z")), NULL) expect_equal(match_param("x", c("x", "y", "z")), "x") expect_equal(match_param(".x", c("x", "y", "z")), "x") expect_equal(match_param("x", c(".x", ".y", ".z")), ".x") expect_equal(match_param(".x", c(".x", ".y", ".z")), ".x") expect_equal(match_param(c(".x", "y"), c(".x", ".y", ".z")), c(".x", ".y")) expect_equal(match_param(c(".x", "x"), c("x", ".x")), c(".x", "x")) expect_equal(match_param(c(".x", "x"), "x"), "x") expect_equal(match_param("x", c(".x", "x")), c("x", ".x")) }) test_that("multiple @inheritParam tags gathers all params", { out <- roc_proc_text( rd_roclet(), " #' A. #' #' @param x X a <- function(x) {} #' B #' #' @param y Y b <- function(y) {} #' C #' #' @inheritParams a #' @inheritParams b c <- function(x, y) {} " ) params <- out[["c.Rd"]]$get_value("param") expect_equal(length(params), 2) expect_equal(params[["x"]], "X") expect_equal(params[["y"]], "Y") }) test_that("multiple @inheritParam tags gathers all params", { out <- roc_proc_text( rd_roclet(), " #' A. #' #' @param x X a <- function(x) {} #' B #' #' @param .y Y b <- function(.y) {} #' C #' #' @inheritParams a #' @inheritParams b c <- function(.x, y) {} " )[[3]] expect_equal(out$get_value("param"), c(.x = "X", y = "Y")) }) test_that("@inheritParam preserves mixed names", { out <- roc_proc_text( rd_roclet(), " #' A. #' @param .x,x X a <- function(x, .x) {} #' B #' @inheritParams a b <- function(x, .x) {} " )[[2]] expect_equal(out$get_value("param"), c(".x,x" = "X")) }) test_that("can inherit from same arg twice", { out <- roc_proc_text( rd_roclet(), " #' A. #' #' @param x X a <- function(x) {} #' B #' #' @inheritParams a b <- function(x) {} #' C #' #' @inheritParams a #' @rdname b c <- function(.x) {} " )[[2]] expect_equal(out$get_value("param"), c("x,.x" = "X")) }) test_that("@inheritParams can inherit from inherited params", { out <- roc_proc_text( rd_roclet(), " #' C #' #' @inheritParams b c <- function(x) {} #' B #' #' @inheritParams a b <- function(x) {} #' A. #' #' @param x X a <- function(x) {} " ) expect_equal(out[["c.Rd"]]$get_value("param"), c(x = "X")) }) test_that("multiple @inheritParam inherits from existing topics", { out <- roc_proc_text( rd_roclet(), " #' My mean #' #' @inheritParams base::mean mymean <- function(x, trim) {}" )[[1]] params <- out$get_value("param") expect_equal(length(params), 2) expect_equal(sort(names(params)), c("trim", "x")) }) test_that("@inheritParam can inherit multivariable arguments", { out <- roc_proc_text( rd_roclet(), " #' A #' @param x,y X and Y A <- function(x, y) {} #' B #' #' @inheritParams A B <- function(x, y) {}" )[[2]] expect_equal(out$get_value("param"), c("x,y" = "X and Y")) # Even when the names only match without . out <- roc_proc_text( rd_roclet(), " #' A #' @param x,y X and Y A <- function(x, y) {} #' B #' #' @inheritParams A B <- function(.x, .y) {}" )[[2]] expect_equal(out$get_value("param"), c(".x,.y" = "X and Y")) }) test_that("@inheritParam only inherits exact multiparam matches", { out <- roc_proc_text( rd_roclet(), " #' A #' @param x,y X and Y A <- function(x, y) {} #' B #' #' @inheritParams A B <- function(x) {}" )[[2]] expect_equal(out$get_value("param"), NULL) }) test_that("@inheritParams inherits params documented with dots (#1718)", { out <- roc_proc_text( rd_roclet(), " #' A #' @param a A variable. #' @param b,\\dots Parameters to pass. A <- function(a, b, ...) {} #' B #' @inheritParams A B <- function(a, b, ...) {} " ) expect_equal( out[[2]]$get_value("param"), c("a" = "A variable.", "b,..." = "Parameters to pass.") ) }) test_that("@inheritParam understands compound docs", { out <- roc_proc_text( rd_roclet(), " #' Title #' #' @param x x #' @param y x x <- function(x, y) {} #' Title #' #' @inheritParams x #' @param y y y <- function(x, y) {}" )[[2]] params <- out$get_value("param") expect_equal(params, c(x = "x", y = "y")) }) test_that("warned if no params need documentation", { code <- " #' Title #' #' @param x x #' @param y x #' @inheritParams foo x <- function(x, y) {} " expect_snapshot(. <- roc_proc_text(rd_roclet(), code)) }) test_that("argument order, also for incomplete documentation", { out <- roc_proc_text( rd_roclet(), " #' A. #' #' @param y Y #' @param x X a <- function(x, y) {} #' B #' #' @param y Y b <- function(x, y) {} #' C #' #' @param x X c <- function(x, y) {} #' D #' #' @inheritParams b #' @param z Z d <- function(x, y, z) {} #' E #' #' @inheritParams c #' @param y Y e <- function(x, y, z) {} " ) expect_equal(out[["a.Rd"]]$get_value("param"), c(x = "X", y = "Y")) expect_equal(out[["b.Rd"]]$get_value("param"), c(y = "Y")) expect_equal(out[["c.Rd"]]$get_value("param"), c(x = "X")) expect_equal(out[["d.Rd"]]$get_value("param"), c(y = "Y", z = "Z")) expect_equal(out[["e.Rd"]]$get_value("param"), c(x = "X", y = "Y")) }) test_that("argument order with @inheritParam", { out <- roc_proc_text( rd_roclet(), " #' A. #' #' @param x X #' @param y Y a <- function(x, y) {} #' B1 #' #' @param y B #' @inheritParams a b1 <- function(x, y) {} #' B2 #' #' @inheritParams a #' @param y B b2 <- function(x, y) {} #' C1 #' #' @param x C #' @inheritParams a c1 <- function(x, y) {} #' C2 #' #' @inheritParams a #' @param x C c2<- function(x, y) {} " ) expect_equal(out[["b1.Rd"]]$get_value("param"), c(x = "X", y = "B")) expect_equal(out[["b2.Rd"]]$get_value("param"), c(x = "X", y = "B")) expect_equal(out[["c1.Rd"]]$get_value("param"), c(x = "C", y = "Y")) expect_equal(out[["c2.Rd"]]$get_value("param"), c(x = "C", y = "Y")) }) test_that("inherit params ... named \\dots", { out <- roc_proc_text( rd_roclet(), " #' Foo #' #' @param x x #' @param \\dots foo foo <- function(x, ...) {} #' Bar #' #' @inheritParams foo #' @param \\dots bar bar <- function(x=1, ...) {} " )[[2]] expect_equal( out$get_value("param"), c(x = "x", "\\dots" = "bar") ) }) # inheritParams argument filtering ---------------------------------------- test_that("@inheritParams can include specific args", { out <- roc_proc_text( rd_roclet(), " #' A. #' #' @param x X #' @param y Y #' @param z Z a <- function(x, y, z) {} #' B #' #' @inheritParams a x z b <- function(x, y, z) {} " )[[2]] params <- out$get_value("param") expect_equal(params, c(x = "X", z = "Z")) }) test_that("@inheritParams can exclude specific args", { out <- roc_proc_text( rd_roclet(), " #' A. #' #' @param x X #' @param y Y #' @param z Z a <- function(x, y, z) {} #' B #' #' @inheritParams a -y b <- function(x, y, z) {} " )[[2]] params <- out$get_value("param") expect_equal(params, c(x = "X", z = "Z")) }) test_that("@inheritParams filtering works with external packages", { out <- roc_proc_text( rd_roclet(), " #' My mean #' #' @inheritParams base::mean -na.rm mymean <- function(x, trim, na.rm) {}" )[[1]] params <- out$get_value("param") expect_true("trim" %in% names(params)) expect_false("na.rm" %in% names(params)) }) test_that("@inheritParams without args still works", { out <- roc_proc_text( rd_roclet(), " #' A. #' #' @param x X #' @param y Y a <- function(x, y) {} #' B #' #' @inheritParams a b <- function(x, y) {} " )[[2]] params <- out$get_value("param") expect_equal(params, c(x = "X", y = "Y")) }) # inheritDotParams -------------------------------------------------------- test_that("can inherit all from single function", { out <- roc_proc_text( rd_roclet(), " #' Foo #' #' @param x x #' @param y y foo <- function(x, y) {} #' Bar #' #' @inheritDotParams foo bar <- function(...) {} " )[[2]] expect_snapshot_output(test_path("test-rd-inherit-dots.txt")) }) test_that("does not produce multiple ... args", { out <- roc_proc_text( rd_roclet(), " #' Foo #' #' @inheritParams bar #' @inheritDotParams baz foo <- function(x, ...) {} #' Bar #' #' @param x x #' @param ... dots bar <- function(x, ...) {} #' Baz #' #' @param y y #' @param z z baz <- function(y, z) {} " )[[1]] expect_snapshot_output(test_path("test-rd-inherit-dots-inherit.txt")) }) test_that("can inherit dots from several functions", { out <- roc_proc_text( rd_roclet(), " #' Foo #' #' @param x x #' @param y y1 foo <- function(x, y) {} #' Bar #' #' @param y y2 #' @param z z bar <- function(z) {} #' Foobar #' #' @inheritDotParams foo #' @inheritDotParams bar foobar <- function(...) {} " )[[3]] expect_snapshot_output(out$get_section("param")) }) test_that("inheritDotParams does not add already-documented params", { out <- roc_proc_text( rd_roclet(), " #' Wrapper around original #' #' @inherit original #' @inheritDotParams original #' @param y some more specific description #' @export wrapper <- function(x = 'some_value', y = 'some other value', ...) { original(x = x, y = y, ...) } #' Original function #' #' @param x x description #' @param y y description #' @param z z description #' @export original <- function(x, y, z, ...) {} " )[[1]] params <- out$get_value("param") dot_param <- params[["..."]] expect_named(params, c("x", "y", "...")) expect_false(grepl("item{x}{x description}", dot_param, fixed = TRUE)) expect_false(grepl("item{y}{y description}", dot_param, fixed = TRUE)) expect_match(dot_param, "item{\\code{z}}{z description}", fixed = TRUE) }) test_that("useful error for bad inherits", { text <- " #' Foo #' #' @param x x #' @param y y foo <- function(x, y) {} #' Bar #' #' @inheritDotParams foo -z bar <- function(...) {} " expect_snapshot(. <- roc_proc_text(rd_roclet(), text)) }) test_that("warns when no params to inherit (#1671)", { text <- " #' Foo #' #' @param ... not used foo <- function(...) {} #' Bar #' #' @param y y #' @inheritDotParams foo bar <- function(y, ...) {} " expect_snapshot(out <- roc_proc_text(rd_roclet(), text)) expect_false("..." %in% names(out[["bar.Rd"]]$get_value("param"))) }) test_that("inheritDotParams matches when doc uses dotted name but formal doesn't (#1826)", { out <- roc_proc_text( rd_roclet(), " #' Foo #' #' @param x x #' @param .y,y doc for y foo <- function(x, y) {} #' Bar #' #' @inheritDotParams foo x y bar <- function(...) {} " )[[2]] dot_param <- out$get_value("param")[["..."]] expect_match(dot_param, "item{\\code{y}}", fixed = TRUE) }) test_that("inheritDotParams uses all documented params, not just formals (#1840)", { out <- roc_proc_text( rd_roclet(), " #' My mean #' #' @inheritDotParams base::mean mymean <- function(x, ...) {} " )[[1]] dot_param <- out$get_value("param")[["..."]] expect_match(dot_param, "\\code{trim}", fixed = TRUE) expect_match(dot_param, "\\code{na.rm}", fixed = TRUE) }) test_that("inheritDotParams warns when source not found (#1602)", { text <- " #' Test #' @inheritDotParams format test = function(...) {} " expect_snapshot(. <- roc_proc_text(rd_roclet(), text)) }) # inherit everything ------------------------------------------------------ test_that("can inherit all from single function", { out <- roc_proc_text( rd_roclet(), " #' Foo #' #' Description #' #' Details #' #' @param x x #' @param y y #' @author Hadley #' @source my mind #' @note my note #' @format my format #' @examples #' x <- 1 foo <- function(x, y) {} #' @inherit foo bar <- function(x, y) {} " )[[2]] expect_named(out$get_value("param"), c("x", "y")) expect_equal(out$get_value("title"), "Foo") expect_equal(out$get_value("description"), "Description") expect_equal(out$get_value("details"), "Details") expect_equal(out$get_value("examples"), rd("x <- 1")) expect_equal(out$get_value("author"), "Hadley") expect_equal(out$get_value("source"), "my mind") expect_equal(out$get_value("format"), "my format") expect_equal(out$get_value("note"), "my note") }) # get_rd() ----------------------------------------------------------------- test_that("useful warnings if can't find topics", { expect_snapshot({ get_rd("not_installed::pkg", source = "source") get_rd("base::doesntexist", source = "source") get_rd("doesntexist", RoxyTopics$new(), source = "source") }) }) test_that("can find section in existing docs", { out <- find_sections(get_rd("base::attach")) expect_equal(out$title, "Good practice") }) roxygen2/tests/testthat/test-isComplete.R0000644000176200001440000000672315165177023020254 0ustar liggesusers# rdComplete --------------------------------------------------------------- test_that("braces must balance", { expect_true(rdComplete("{}", is_code = FALSE)) expect_true(rdComplete("{{}}", is_code = FALSE)) expect_false(rdComplete("{", is_code = FALSE)) expect_false(rdComplete("}", is_code = FALSE)) }) test_that("can't end with escape", { expect_false(rdComplete("\\", is_code = FALSE)) }) test_that("escaped brackets are ignored", { expect_true(rdComplete("\\{", is_code = FALSE)) expect_true(rdComplete("\\}", is_code = FALSE)) expect_false(rdComplete("{\\}", is_code = FALSE)) }) test_that("brackets in comments are ignored", { expect_true(rdComplete("% {", is_code = FALSE)) expect_true(rdComplete("% }", is_code = FALSE)) }) test_that("R comments don't close latex-like tags", { expect_true(rdComplete(r"(A comment \code{#}.)", is_code = FALSE)) }) test_that("newline ends comment", { expect_false(rdComplete("%\n{", is_code = FALSE)) }) test_that("escape disables comment", { expect_false(rdComplete(r"(\%{)", is_code = FALSE)) }) test_that("strings must be closed in code", { expect_false(rdComplete("'", is_code = TRUE)) expect_false(rdComplete('"', is_code = TRUE)) }) test_that("strings respect escapes", { expect_false(rdComplete("'\\'", is_code = TRUE)) # '\' expect_true(rdComplete("'\\''", is_code = TRUE)) # '\'' }) test_that("braces in strings don't need to match in code", { expect_true(rdComplete("'{{'", is_code = TRUE)) }) test_that("raw strings are handled in code", { expect_true(rdComplete('r"(text)"', is_code = TRUE)) expect_true(rdComplete('r"{text}"', is_code = TRUE)) expect_true(rdComplete('r"["text]"', is_code = TRUE)) expect_true(rdComplete('R"(text)"', is_code = TRUE)) expect_true(rdComplete('r"--(text)--"', is_code = TRUE)) expect_true(rdComplete("r'(text)'", is_code = TRUE)) }) test_that("incomplete raw strings are detected in code", { expect_false(rdComplete('r"', is_code = TRUE)) expect_false(rdComplete('r"(', is_code = TRUE)) expect_false(rdComplete('r"(text', is_code = TRUE)) expect_false(rdComplete('r"(text)', is_code = TRUE)) expect_false(rdComplete('r"(text)\'', is_code = TRUE)) expect_false(rdComplete('r"--(text)--', is_code = TRUE)) expect_false(rdComplete('r"--(text)-"', is_code = TRUE)) expect_false(rdComplete('r"--(text)-', is_code = TRUE)) expect_false(rdComplete('r"--(text)"', is_code = TRUE)) }) test_that("braces in raw strings don't need to match in code", { expect_true(rdComplete('r"({)"', is_code = TRUE)) }) test_that("strings in code comments don't need to be closed", { expect_true(rdComplete("# '", is_code = TRUE)) }) test_that("braces in code must match", { expect_false(rdComplete("# {", is_code = TRUE)) expect_true(rdComplete("# {}", is_code = TRUE)) expect_false(rdComplete("# {} {", is_code = TRUE)) # Allow user to close a brace in a comment (needed by `@examplesIf`) expect_true(rdComplete("{ # }", is_code = TRUE)) }) # findEndOfTag ------------------------------------------------------------- test_that("handles simple cases", { expect_equal(findEndOfTag("{abc}", is_code = FALSE, start = 0L), 4) expect_equal(findEndOfTag("{abc} rest", is_code = FALSE, start = 0L), 4) expect_equal(findEndOfTag("{a}{b} rest", is_code = FALSE, start = 0L), 5) expect_equal(findEndOfTag("{a{b}c} rest", is_code = FALSE, start = 0L), 6) }) test_that("returns -1 for incomplete input", { expect_equal(findEndOfTag("{abc", is_code = FALSE, start = 0L), -1) }) roxygen2/tests/testthat/test-rd-r6-methods-self.R0000644000176200001440000002330315170121342021450 0ustar liggesuserstest_that("r6_method_from_row extracts all components", { text <- " #' Class C <- R6::R6Class('C', cloneable = FALSE, public = list( #' @description Say hello. #' @details Be polite. #' @param who Name. #' @return A greeting. #' @examples c$greet('world') greet = function(who) paste('hi', who) ) )" docs <- r6_doc(text) method <- docs$methods$self[[1]] expect_equal(method$name, "greet") expect_equal(method$class, "C") expect_equal(method$description, "Say hello.") expect_equal(method$details, "Be polite.") expect_equal(method$params, list(list(name = "who", description = "Name."))) expect_equal(method$return, "A greeting.") expect_equal(method$examples, "c$greet('world')") }) test_that("@returns works as method-level tag (#1148)", { text <- " #' Class C <- R6::R6Class('C', cloneable = FALSE, public = list( #' @description Run. #' @returns The result. run = function() 1 ) )" docs <- r6_doc(text) expect_equal(docs$methods$self[[1]]$return, "The result.") }) test_that("warns about multiple @return(s) tags", { text <- " #' Class C <- R6::R6Class('C', cloneable = FALSE, public = list( #' @description Run. #' @return First. #' @returns Second. run = function() 1 ) )" expect_snapshot(docs <- r6_doc(text)) expect_equal(docs$methods$self[[1]]$return, "First.") }) test_that("class-level @param x,y inherited by methods (#1600)", { text <- " #' Class #' @param x,y Numbers. C <- R6::R6Class('C', cloneable = FALSE, public = list( #' @description Method. meth = function(x, y) {} ) )" expect_silent(docs <- r6_doc(text)) expect_equal( docs$methods$self[[1]]$params, list(list(name = "x, y", description = "Numbers.")) ) }) test_that("warns about undocumented params", { text <- " #' Class C <- R6::R6Class('C', cloneable = FALSE, public = list( #' @description Run. run = function(x, y) NULL ) )" expect_snapshot(docs <- r6_doc(text)) }) test_that("warns about duplicated params", { text <- " #' Class C <- R6::R6Class('C', cloneable = FALSE, public = list( #' @description Run. #' @param x First. #' @param x Second. run = function(x) NULL ) )" expect_snapshot(docs <- r6_doc(text)) }) test_that("initialize() inherits params from @field", { text <- " #' Class C <- R6::R6Class('C', cloneable = FALSE, public = list( #' @field x A field. x = NULL, #' @field y Another field. y = NULL, #' @description Create. initialize = function(x, y) { self$x <- x self$y <- y } ) )" docs <- r6_doc(text) init <- docs$methods$self[[1]] expect_equal( init$params, list( list(name = "x", description = "A field."), list(name = "y", description = "Another field.") ) ) }) test_that("initialize() @param takes precedence over @field", { text <- " #' Class C <- R6::R6Class('C', cloneable = FALSE, public = list( #' @field x A field. x = NULL, #' @description Create. #' @param x Custom description. initialize = function(x) { self$x <- x } ) )" docs <- r6_doc(text) init <- docs$methods$self[[1]] expect_equal( init$params, list(list(name = "x", description = "Custom description.")) ) }) test_that("format.rd_r6_method produces method subsection", { method <- rd_r6_method( name = "greet", class = "Person", formals = as.pairlist(alist(who = , how = "nicely")), description = "Say hello.", params = list( list(name = "who", description = "Name to greet."), list(name = "how", description = "Greeting style.") ) ) expect_snapshot(cat(format(method), sep = "\n")) }) test_that("format.rd_r6_method renames initialize to new", { method <- rd_r6_method( name = "initialize", class = "Foo", formals = NULL, description = "Create object." ) expect_snapshot(cat(format(method), sep = "\n")) }) test_that("format.rd_r6_method includes optional sections", { method <- rd_r6_method( name = "run", class = "Job", formals = NULL, description = "Run the job.", details = "Some details.", return = "The result.", examples = "job$run()" ) expect_snapshot(cat(format(method), sep = "\n")) }) test_that("format.rd_r6_method strips \\dontrun etc from examples (#1072)", { method <- rd_r6_method( name = "run", class = "Job", formals = NULL, description = "Run the job.", examples = " \\dontrun{donrun()} \\donttest{donttest()} \\dontshow{dontshow()} " ) expect_snapshot(cat(format(method), sep = "\n")) }) test_that("format.rd_r6_method omits empty optional sections", { method <- rd_r6_method( name = "run", class = "Job", formals = NULL, description = "Run." ) expect_snapshot(cat(format(method), sep = "\n")) }) # Superclass param inheritance ----------------------------------------------- test_that("method inherits params from superclass method", { text <- " #' Parent A <- R6::R6Class('A', cloneable = FALSE, public = list( #' @description Do something. #' @param x An x value. #' @param y A y value. run = function(x, y) NULL ) ) #' Child B <- R6::R6Class('B', cloneable = FALSE, inherit = A, public = list( #' @description Do something else. run = function(x, y) NULL ) )" docs <- r6_doc(text) method <- docs$methods$self[[1]] expect_equal( method$params, list( list(name = "x", description = "An x value."), list(name = "y", description = "A y value.") ) ) }) test_that("method inherits only missing params from superclass", { text <- " #' Parent A <- R6::R6Class('A', cloneable = FALSE, public = list( #' @description Do something. #' @param x An x value. #' @param y A y value. run = function(x, y) NULL ) ) #' Child B <- R6::R6Class('B', cloneable = FALSE, inherit = A, public = list( #' @description Do something else. #' @param x Overridden x. run = function(x, y) NULL ) )" docs <- r6_doc(text) method <- docs$methods$self[[1]] expect_equal( method$params, list( list(name = "x", description = "Overridden x."), list(name = "y", description = "A y value.") ) ) }) test_that("method inherits class-level params from superclass", { text <- " #' Parent #' @param x An x value. A <- R6::R6Class('A', cloneable = FALSE, public = list( #' @description Do something. run = function(x) NULL ) ) #' Child B <- R6::R6Class('B', cloneable = FALSE, inherit = A, public = list( #' @description Do something else. run = function(x) NULL ) )" docs <- r6_doc(text) method <- docs$methods$self[[1]] expect_equal( method$params, list(list(name = "x", description = "An x value.")) ) }) test_that("initialize() inherits params from superclass initialize()", { text <- " #' Parent A <- R6::R6Class('A', cloneable = FALSE, public = list( #' @description Create. #' @param x An x value. #' @param y A y value. initialize = function(x, y) NULL ) ) #' Child B <- R6::R6Class('B', cloneable = FALSE, inherit = A, public = list( #' @description Create child. #' @param z A z value. initialize = function(x, y, z) NULL ) )" docs <- r6_doc(text) init <- docs$methods$self[[1]] expect_equal( init$params, list( list(name = "x", description = "An x value."), list(name = "y", description = "A y value."), list(name = "z", description = "A z value.") ) ) }) test_that("child initialize() inherits params from superclass @field", { text <- " #' Parent A <- R6::R6Class('A', cloneable = FALSE, public = list( #' @field x An x value. x = NULL, #' @field y A y value. y = NULL, #' @description Create. initialize = function(x, y) { self$x <- x self$y <- y } ) ) #' Child B <- R6::R6Class('B', cloneable = FALSE, inherit = A, public = list( #' @description Create child. #' @param z A z value. initialize = function(x, y, z) { super$initialize(x, y) } ) )" docs <- r6_doc(text) init <- docs$methods$self[[1]] expect_equal( init$params, list( list(name = "x", description = "An x value."), list(name = "y", description = "A y value."), list(name = "z", description = "A z value.") ) ) }) test_that("method inherits params through multiple levels", { text <- " #' Grandparent A <- R6::R6Class('A', cloneable = FALSE, public = list( #' @description Do something. #' @param x An x value. run = function(x) NULL ) ) #' Parent B <- R6::R6Class('B', cloneable = FALSE, inherit = A, public = list( #' @description Do something. #' @param x An x value. run = function(x) NULL ) ) #' Child C <- R6::R6Class('C', cloneable = FALSE, inherit = B, public = list( #' @description Do something else. run = function(x) NULL ) )" docs <- r6_doc(text) method <- docs$methods$self[[1]] expect_equal( method$params, list(list(name = "x", description = "An x value.")) ) }) roxygen2/tests/testthat/test-object-rc.R0000644000176200001440000000402015151411610017770 0ustar liggesusers# Docstrings ------------------------------------------------------------------- test_that("base functions don't have docstrings", { expect_equal(docstring(`[`), NULL) expect_equal(docstring(mean), NULL) }) test_that("function return string doesn't have docstring", { expect_equal(docstring(function() "a"), NULL) expect_equal( docstring(function() { "a" }), NULL ) }) test_that("first string in function is docstring", { expect_equal( docstring(function() { "a" 1 }), "a" ) }) test_that("trim_docstring handles indentation correctly", { expect_equal(trim_docstring("a\n b\n c"), "a\nb\nc") expect_equal(trim_docstring("a\nb\nc"), "a\nb\nc") expect_equal(trim_docstring("a\n b\n c"), "a\nb\n c") expect_equal(trim_docstring(" a\n b\n c"), "a\nb\n c") }) # Method documentation --------------------------------------------------------- env <- pkg_env() A1 <- setRefClass( "A1", methods = list( f = function() { "This function has a docstring" 1 }, g = function() { "This function doesn't" } ), where = env ) B1 <- setRefClass( "B1", contains = "A1", methods = list( f1 = function() { "This function has a docstring" 1 }, g1 = function() { "This function doesn't" } ), where = env ) classA <- getClass("A1", where = env) classB <- getClass("B1", where = env) test_that("rc_methods only lists methods belong to class (not parents)", { expect_equal(length(rc_methods(classA)), 2) expect_equal(length(rc_methods(classB)), 2) }) test_that("RC methods included included in own section", { out <- roc_proc_text( rd_roclet(), " #' Class ABC setRefClass('ABC', methods = list( f = function() { 'This function has a docstring' 1 } )) " )[[1]] methods <- out$get_value("rcmethods") expect_equal(names(methods), "f()") expect_match(methods[[1]], "This function has a docstring") }) removeClass("B1", where = env) removeClass("A1", where = env) roxygen2/tests/testthat/test-markdown-code.R0000644000176200001440000001577615171700424020704 0ustar liggesuserstest_that("`Rd` prefix produces \\Sexpr", { out1 <- roc_proc_text( rd_roclet(), " #' @title Title #' @description Description `Rd foo()` #' @md foo <- function() NULL " )[[1]] expect_equal( out1$get_value("description"), "Description \\Sexpr[stage=render,results=rd]{foo()}" ) }) test_that("can eval inline code", { out1 <- roc_proc_text( rd_roclet(), " #' @title Title `r 1 + 1` #' @description Description `r 2 + 2` #' @md foo <- function() NULL " )[[1]] expect_equal(out1$get_value("title"), "Title 2") expect_equal(out1$get_value("description"), "Description 4") }) test_that("can eval fenced code", { out1 <- roc_proc_text( rd_roclet(), " #' @title Title #' @details Details #' ```{r lorem} #' 1+1 #' ``` #' @md foo <- function() NULL " )[[1]] expect_match(out1$get_value("details"), "2") }) test_that("use same env within, but not across blocks", { example <- " #' Title `r baz <- 420` `r baz` #' #' Description `r exists('baz', inherits = FALSE)` #' @md bar <- function() NULL #' Title #' #' Description `r exists('baz', inherits = FALSE)` #' @md zap <- function() NULL " out1 <- roc_proc_text(rd_roclet(), example)[[1]] out2 <- roc_proc_text(rd_roclet(), example)[[2]] expect_equal(out1$get_value("title"), "Title 420") expect_equal(out1$get_value("description"), "Description TRUE") expect_equal(out2$get_value("description"), "Description FALSE") }) test_that("appropriate knit print method for fenced and inline is applied", { rlang::local_bindings( knit_print.foo = function(x, inline = FALSE, ...) { knitr::asis_output(ifelse(inline, "inline", "fenced")) }, .env = globalenv() ) out1 <- roc_proc_text( rd_roclet(), " #' @title Title `r structure('default', class = 'foo')` #' #' @details Details #' #' ```{r} #' structure('default', class = 'foo') #' ``` #' #' @md #' @name bar NULL " ) expect_match(out1$bar.Rd$get_value("details"), "fenced", fixed = TRUE) expect_match(out1$bar.Rd$get_value("title"), "inline", fixed = TRUE) }) test_that("can create markdown markup", { expect_identical( markdown("Description `r paste0('_', 'keyword', '_')`"), "Description \\emph{keyword}" ) }) test_that("can create markdown markup piecewise", { expect_identical( markdown( "Description [`r paste0('https://url')`](`r paste0('link text')`)" ), "Description \\link{https://url}(link text)" ) }) test_that("can create escaped markdown markup", { # this workaround is recommended by @yihui # "proper" escaping for inline knitr tracked in https://github.com/yihui/knitr/issues/1704 out1 <- roc_proc_text( rd_roclet(), " #' Title #' Description `r paste0('\\x60', 'bar', '\\x60')` #' @md foo <- function() NULL " )[[1]] expect_match(out1$get_value("title"), "\\code{bar}", fixed = TRUE) }) test_that("NULL creates no text", { expect_identical( markdown("Description --`r NULL`--"), "Description ----" ) }) test_that("multi-line inline code gives useful warning", { block <- " #' Title #' #' `r 1 + #' 1` #' @md foo <- function() {} " expect_snapshot( out <- roc_proc_text(rd_roclet(), block)[[1]] ) expect_equal(out$get_value("description"), r"(\verb{r 1 + 1})") }) test_that("inline code gives useful warning", { block <- " #' Title #' #' `r 1 + ` #' @md foo <- function() {} " expect_snapshot( out <- roc_proc_text(rd_roclet(), block)[[1]], transform = function(x) { line <- grep("~~~", x)[1] if (!is.na(line)) { x <- x[1:(line - 1)] } x } ) expect_equal(out$get_value("description"), r"(\verb{r 1 + })") }) test_that("interleaving fences and inline code", { out1 <- roc_proc_text( rd_roclet(), " #' Title #' #' @details Details `r x <- 10; x` #' #' ```{r} #' y <- x + 10 #' y #' ``` #' #' @md #' @name dummy NULL" )[[1]] expect_snapshot(cat(out1$get_value("details"))) }) test_that("preserves white space", { out1 <- roc_proc_text( rd_roclet(), " #' Title #' #' @details #' #' ```{r} #' a <- 1 #' #' b <- 2 #' ``` #' #' ```{r} #' c <- 3 #' ``` #' #' @md #' @name dummy NULL" )[[1]] expect_snapshot(cat(out1$get_value("details"))) }) test_that("fence options are used", { out1 <- roc_proc_text( rd_roclet(), " #' Title #' #' @details Details #' #' ```{r eval = FALSE} #' this - would - fail - to - eval #' ``` #' #' @md #' @name dummy NULL" )[[1]] details <- out1$get_value("details") expect_false(grepl("Error", details)) }) test_that("dynamic code in fragile tags still runs", { out <- markdown("foo \\out{`r 1+1`} bar") expect_equal(out, "foo \\out{2} bar") }) test_that("fragile tags in dynamic code are left alone", { out <- markdown("foo `r substr('\\\\out{xxx}', 2, 4)` bar") expect_equal(out, "foo out bar") }) test_that("fragile tags in generated code", { out <- markdown("foo `r '\\\\out{*1*}'` bar") expect_equal(out, "foo \\out{*1*} bar") expect_silent(out2 <- markdown("foo `r '\\\\out{}'` bar")) expect_equal(out2, "foo \\out{} bar") }) test_that("workaround for cmark sourcepos bug (#1353) works", { out <- roc_proc_text( rd_roclet(), " #' Title #' #' line1 #' pre `r \"1\"` 2 `r 1+2` post #' #' no workaround needed `r 'here'` #' @md foo <- function() {} " )[[1]] expect_equal(out$get_section("description")$value, "line1\npre 1 2 3 post") expect_equal(out$get_section("details")$value, "no workaround needed here") }) test_that("alternative knitr engines", { expect_snapshot( print( out1 <- roc_proc_text( rd_roclet(), " #' Title #' #' Description. #' #' ```{verbatim} #' #| file = testthat::test_path(\"example.Rmd\") #' ``` #' @md #' @name x NULL " ) ) ) }) test_that("can override default options", { local_roxy_meta_set("knitr_chunk_options", list(comment = "###")) out <- roc_proc_text( rd_roclet(), " #' Title #' #' ```{r} #' 1+1 #' ``` #' @md foo <- function() { } " )[[1]] expect_match(out$get_section("description")$value, "###", fixed = TRUE) }) test_that("doesn't generate NA language", { out <- roc_proc_text( rd_roclet(), " #' Title #' #' ``` #' r <- 1:10 #' ``` #' @md foo <- function() {}" )[[1]] expect_false(grepl("NA", out$get_section("description")$value)) }) test_that("inline code in non-indented list continuation doesn't error (#1651)", { out <- roc_proc_text( rd_roclet(), " #' Title #' #' - a list #' with non-indented `r 1+1` text #' @md foo <- function() {} " )[[1]] expect_match(out$get_value("description"), "2") }) roxygen2/tests/testthat/test-rd-markdown-escaping.R0000644000176200001440000000550115166171276022162 0ustar liggesuserstag_df <- function(tag, start, end, argend = NULL) { df <- data.frame( tag = tag, start = start, end = end ) if (!is.null(argend)) { df$argend <- argend } df } test_that("find_all_tag_names", { text <- r"(blah blah \mytag blah blah)" expect_equal( find_all_tag_names(text), tag_df(r"(\mytag)", 11, 16) ) }) test_that("find_all_rd_tags", { cases <- list( ## No tags list("", character(), numeric(), numeric(), numeric()), list("nothing to see here", character(), numeric(), numeric(), numeric()), list("\nstill\nnothing\n", character(), numeric(), numeric(), numeric()), ## One tag list(r"(blah blah \mytag blah blah)", r"(\mytag)", 11, 16, 16), list(r"(blah blah \mytag{arg1} blah blah)", r"(\mytag)", 11, 16, 22), list(r"(blah blah \mytag{arg1}{arg2} blah blah)", r"(\mytag)", 11, 16, 28), list(r"(blah\mytag)", r"(\mytag)", 5, 10, 10), list(r"(blah \mytag)", r"(\mytag)", 6, 11, 11), list(r"(blah\mytag{arg})", r"(\mytag)", 5, 10, 15), list(r"(\mytag hoohoo)", r"(\mytag)", 1, 6, 6), list(r"(\mytag)", r"(\mytag)", 1, 6, 6), list(r"(\mytag{arg})", r"(\mytag)", 1, 6, 11), list("blah \\mytag\nblah blah", r"(\mytag)", 6, 11, 11), ## Multiple tags list( r"(blah \tag1 \tag2{arg} blah)", c(r"(\tag1)", r"(\tag2)"), c(6, 12), c(10, 16), c(10, 21) ), list( r"(blah \tag1{ \tag2{arg} } blah)", c(r"(\tag1)", r"(\tag2)"), c(6, 13), c(10, 17), c(24, 22) ), list( "blah \\tag1{\n\\tag2{arg}\n} blah", c(r"(\tag1)", r"(\tag2)"), c(6, 13), c(10, 17), c(24, 22) ) ) for (case in cases) { expect_equal( find_all_rd_tags(case[[1]]), do.call(tag_df, case[-1]), info = case[[1]] ) } }) test_that("find_fragile_rd_tags", { fragile <- c(r"(\frag)", r"(\frag1)", r"(\frag2)") cases <- list( list(r"(This is \frag{here}, \this{arg} not)", r"(\frag)"), list(r"(Embedded \frag{ into \frag1{arg} plus })", r"(\frag)"), list( r"(blah \cmd{ \frag{arg} \frag{arg} } \frag2 blah)", c(r"(\frag)", r"(\frag)", r"(\frag2)") ) ) for (case in cases) { expect_equal( find_fragile_rd_tags(case[[1]], fragile)$tag, case[[2]], info = case[[1]] ) } }) test_that("re_sub_same", { expect_equal( re_sub_same( "123456789ab", data.frame(start = c(1, 6), end = c(2, 10), argend = c(2, 10)), "xxx" ), "xxx-1-345xxx-2-b" ) expect_equal( re_sub_same( "123456789ab", data.frame(start = c(1, 8), end = c(7, 10), argend = c(7, 10)), "xxx" ), "xxx-1-xxx-2-b" ) expect_equal( re_sub_same( "123456789ab", data.frame(start = numeric(), end = numeric(), argend = numeric()), "xxx" ), "123456789ab" ) }) roxygen2/tests/testthat/testCollateOverwrite/0000755000176200001440000000000014520730024021220 5ustar liggesusersroxygen2/tests/testthat/testCollateOverwrite/R/0000755000176200001440000000000015151411610021417 5ustar liggesusersroxygen2/tests/testthat/testCollateOverwrite/R/a.R0000644000176200001440000000002714520730024021763 0ustar liggesusers#' @include b.R a <- 1 roxygen2/tests/testthat/testCollateOverwrite/R/b.R0000644000176200001440000000000715151411610021760 0ustar liggesusersb <- 2 roxygen2/tests/testthat/testCollateOverwrite/DESCRIPTION0000644000176200001440000000032514520730024022726 0ustar liggesusersPackage: testCollateMissing Title: Tools to make developing R code easier License: GPL-2 Description: Author: Hadley Maintainer: Hadley Version: 0.1 Collate: 'b.R' roxygen2/tests/testthat/testRbuildignore/0000755000176200001440000000000013523072405020357 5ustar liggesusersroxygen2/tests/testthat/testRbuildignore/R/0000755000176200001440000000000013523072405020560 5ustar liggesusersroxygen2/tests/testthat/testRbuildignore/R/a.R0000644000176200001440000000003013523072405021114 0ustar liggesusers#' function a #' a <- 1 roxygen2/tests/testthat/testRbuildignore/R/ignore_me.R0000644000176200001440000000007013523072405022644 0ustar liggesusers#' Ignore me, I'm not ready #' ignore_me <- "ignore me" roxygen2/tests/testthat/testRbuildignore/DESCRIPTION0000644000176200001440000000032713523072405022067 0ustar liggesusersPackage: testRbuildignore Title: Tools to make developing R code easier License: GPL-2 Description: Author: Francois Maintainer: Francois Version: 0.1 roxygen2/tests/testthat/test-topics.R0000644000176200001440000000023114262717326017440 0ustar liggesuserstest_that("missing title generates useful message", { block <- " #' @name foo NULL " expect_snapshot(roc_proc_text(rd_roclet(), block)) }) roxygen2/tests/testthat/test-rd-raw.R0000644000176200001440000000145715151411610017327 0ustar liggesuserstest_that("rawRd inserted unchanged", { out <- roc_proc_text( rd_roclet(), " #' @rawRd #this is a comment #' @name a #' @title a NULL" )[[1]] lines <- strsplit(format(out), "\n")[[1]] expect_equal(lines[[9]], "#this is a comment") }) test_that("evalRd inserted unchanged", { out <- roc_proc_text( rd_roclet(), " z <- 10 #' @evalRd as.character(z * 2) #' @name a #' @title a NULL" )[[1]] expect_equal(out$get_value("rawRd"), "20") }) test_that("error-ful evalRd generates warning", { expect_snapshot({ expect_parse_failure(roxy_tag_eval(roxy_test_tag(val = 1))) expect_parse_failure(roxy_tag_eval(roxy_test_tag(val = NA_character_))) expect_parse_failure(roxy_tag_eval(roxy_test_tag( val = quote(stop('Uhoh')) ))) }) }) roxygen2/tests/testthat/test-options/0000755000176200001440000000000015175154672017516 5ustar liggesusersroxygen2/tests/testthat/test-options/meta-error.R0000644000176200001440000000003214262677627021717 0ustar liggesusersstop("This is an error!") roxygen2/tests/testthat/test-options/meta-character.R0000644000176200001440000000000414262677627022521 0ustar liggesusers"a" roxygen2/tests/testthat/test-options/man/0000755000176200001440000000000014262677627020277 5ustar liggesusersroxygen2/tests/testthat/test-options/man/roxygen/0000755000176200001440000000000014262677676021776 5ustar liggesusersroxygen2/tests/testthat/test-options/man/roxygen/meta.R0000644000176200001440000000003214262677676023042 0ustar liggesuserslist( markdown = TRUE ) roxygen2/tests/testthat/test-options/DESCRIPTION0000644000176200001440000000006515163737466021232 0ustar liggesusersPackage: testOptions Roxygen: list(old_usage = TRUE) roxygen2/tests/testthat/test-tokenize.R0000644000176200001440000000764415151411610017767 0ustar liggesusers# tokenise_block ---------------------------------------------------------- test_that("parses into tag and value", { x <- tokenise_block("#' @xyz abc", file = "", offset = 0) expect_equal(length(x), 1) expect_equal(x[[1]]$tag, "xyz") expect_equal(x[[1]]$raw, "abc") }) test_that("description block gets empty tag", { x <- tokenise_block("#' abc", file = "", offset = 0L) expect_equal(length(x), 1) expect_equal(x[[1]]$tag, "") expect_equal(x[[1]]$raw, "abc") }) test_that("multi line tags collapsed into one", { x <- tokenise_block( c( "#' @tag abc", "#' def" ), file = "", offset = 0L ) expect_equal(length(x), 1) expect_equal(x[[1]]$raw, "abc\n def") }) test_that("description block gets empty tag when followed by tag", { x <- tokenise_block( c( "#' abc", "#' @xyz abc" ), file = "", offset = 0L ) expect_equal(length(x), 2) expect_equal(x[[1]]$tag, "") expect_equal(x[[1]]$raw, "abc") expect_equal(x[[2]]$tag, "xyz") expect_equal(x[[2]]$raw, "abc") }) test_that("leading whitespace is ignored", { ref <- tokenise_block("#' abc", file = "", offset = 0L) expect_equal(tokenise_block(" #' abc", file = "", offset = 0L), ref) }) test_that("need one or more #", { ref <- tokenise_block("#' abc", file = "", offset = 0L) expect_equal(tokenise_block("##' abc", file = "", offset = 0L), ref) expect_equal(tokenise_block("###' abc", file = "", offset = 0L), ref) }) test_that("@@ becomes @", { expect_equal( tokenise_block("#' @tag @@", file = "", offset = 0L)[[1]]$raw, "@" ) }) # Inline comments --------------------------------------------------------- test_that("Inline comments are supported", { out <- roc_proc_text( rd_roclet(), " #' Description a <- function(x) { #' @param x an integer stopifnot(is.integer(x)) }" )[[1]] expect_equal(out$get_value("param"), c(x = "an integer")) }) test_that("Inline comments just before the closing brace are allowed", { out <- roc_proc_text( rd_roclet(), " #' Description a <- function(x) { #' @param x an integer stopifnot(is.integer(x)) #' @seealso somewhere }" )[[1]] expect_equal(out$get_value("seealso"), "somewhere") }) test_that("Inline comments do not extend past the closing brace", { out <- roc_proc_text( rd_roclet(), " #' Description a <- function(x) { #' @param x an integer stopifnot(is.integer(x)) }; #' @seealso somewhere" )[[1]] expect_null(out$get_value("seealso")) }) test_that("Line numbers are ok", { check_line_nums <- function(block, lines) { for (t in names(lines)) { expect_equal(block_get_tag(block, t)$line, lines[[t]], info = t) } } text <- "#' @title #' Foo #' #' @description #' Description #' #' @details #' Details #' #' @param x xyz #' @export NULL" block <- parse_text(text)[[1]] ls <- c(title = 1, description = 4, details = 7, param = 10, export = 11) check_line_nums(block, ls) text <- "#' @title Foo #' #' @description Description #' #' @details Details #' #' @param x xy #' z #' #' @export NULL" block <- parse_text(text)[[1]] ls <- c(title = 1, description = 3, details = 5, param = 7, export = 10) check_line_nums(block, ls) text <- "#' @title Foo #' #' @description Description #' #' @details Details # not - a - roxy - comment #' @param x xy #' z quote(neither - is - #' @export this)" block <- parse_text(text)[[1]] ls <- c(title = 1, description = 3, details = 5, param = 7, export = 10) check_line_nums(block, ls) text <- "# 1 # 2 #' foo #' #' Description # 6 # 7 #' @param x xyz NULL" block <- parse_text(text)[[1]] ls <- c(title = 3, description = 5, param = 8) check_line_nums(block, ls) }) roxygen2/tests/testthat/test-rd-s4.R0000644000176200001440000000145215160335315017066 0ustar liggesuserstest_that("invalid syntax generates useful warning", { block <- " #' A #' @field #' @slot a <- function() {} " expect_snapshot(. <- roc_proc_text(rd_roclet(), block)) }) test_that("@fields creates a new section and lists fields", { out <- roc_proc_text( rd_roclet(), " #' Important class. #' #' @field a field a #' #' @field b field b #' setRefClass('test') " )[[1]] expect_equal(out$get_value("field"), c(a = "field a", b = "field b")) }) test_that("@slot creates a new section and lists slots", { out <- roc_proc_text( rd_roclet(), " #' Important class. #' #' @slot a slot a #' @slot b slot b setClass('test') " )[[1]] expect_equal(out$get_value("slot"), c(a = "slot a", b = "slot b")) }) roxygen2/tests/testthat/test-object-from-call.R0000644000176200001440000002627515174453534021300 0ustar liggesuserstest_that("undocumentable things return null", { expect_null(call_to_object(NULL)) expect_null(call_to_object(10)) expect_null(call_to_object(1 + 2)) }) # data / package ------------------------------------------------------- test_that("recommends use of _PACKAGE", { path <- local_package_copy(test_path("empty")) block <- " #' @docType package NULL " withr::with_dir(path, expect_snapshot(out <- parse_text(block)[[1]])) expect_s3_class(out$object, "package") expect_equal(out$object$value$desc$get_field("Package"), "empty") }) test_that("finds package description", { obj <- call_to_object("_PACKAGE", file = test_path("testEagerData/R/a.r")) expect_s3_class(obj, "package") expect_equal(obj$value$desc$get_field("Package"), "testEagerData") }) test_that("finds datasets given by name", { obj <- call_to_object({ df <- data.frame(x = 1, y = 2) "df" }) expect_s3_class(obj, "data") expect_equal(obj$alias, "df") expect_s3_class(obj$value, "data.frame") }) test_that("can document eager data", { path <- local_package_copy(test_path('testEagerData')) suppressMessages(roxygenise(path)) withr::defer(pkgload::unload("testEagerData")) expect_true(file.exists(file.path(path, "man/a.Rd"))) }) test_that("can document lazy data", { path <- local_package_copy(test_path('testLazyData')) suppressMessages(roxygenise(path)) withr::defer(pkgload::unload("testLazyData")) expect_true(file.exists(file.path(path, "man/a.Rd"))) }) # imports ----------------------------------------------------------------- test_that("find function imported from another package", { obj <- call_to_object(desc::desc_get) expect_s3_class(obj, "import") expect_equal(obj$alias, "desc_get") expect_equal(obj$value$pkg, "desc") }) # assignment ------------------------------------------------------------ test_that("finds function created with assignment", { obj <- call_to_object({ foo <- function(x, y, z) {} }) expect_s3_class(obj, "function") }) test_that("finds S3 generic created with assignment", { obj <- call_to_object({ foo <- function(x, y, z) UseMethod("foo") }) expect_s3_class(obj, "s3generic") }) test_that("finds S3 method created with assignment", { obj <- call_to_object({ foo <- function(x, y, z) UseMethod("foo") foo.method <- function(x, y, z) {} }) expect_s3_class(obj, "s3method") }) test_that("finds value created with assignment", { obj <- call_to_object({ foo <- 1:10 }) expect_s3_class(obj, "value") }) test_that("finds class generator", { obj <- call_to_object({ newFoo <- setClass("Foo") }) expect_s3_class(obj, "s4class") expect_equal(obj$alias, "newFoo") expect_s4_class(obj$value, "classRepresentation") obj <- call_to_object({ newFoo <- setRefClass("Foo") }) expect_s3_class(obj, "rcclass") expect_equal(obj$alias, "newFoo") expect_s4_class(obj$value, "classRepresentation") }) test_that("ignored compound assignment", { obj <- call_to_object({ foo <- list() foo[[1]] <- function(x, y, z) {} }) expect_null(obj) }) test_that("finds function created with delayed assignment", { obj <- call_to_object({ delayedAssign("foo", function(x, y, z) {}) }) expect_s3_class(obj, "function") }) # S3 ---------------------------------------------------------------------- test_that("can derive S3 metadata for base generics", { block <- " #' @export mean.foo <- function(...) 1 " out <- parse_text(block)[[1]] expect_equal(s3_method_info(out$object$value), c("mean", "foo")) }) test_that("@method overrides auto-detection", { block <- " #' @export #' @method all.equal data.frame all.equal.data.frame <- function(...) 1 " out <- parse_text(block)[[1]] expect_equal(s3_method_info(out$object$value), c("all.equal", "data.frame")) }) test_that("exportS3Method registers S3 metadata", { block <- " #' @exportS3Method stats::median median.foo <- function(...) 1 " out <- parse_text(block)[[1]] expect_equal(s3_method_info(out$object$value), c("median", "foo")) }) # S4 ---------------------------------------------------------------------- test_that("finds S4 and RC classes", { obj <- call_to_object(setClass("Foo")) expect_s3_class(obj, "s4class") expect_equal(obj$topic, "Foo-class") expect_equal(obj$alias, NULL) obj <- call_to_object(setRefClass("Foo")) expect_s3_class(obj, "rcclass") expect_equal(obj$topic, "Foo-class") obj <- call_to_object({ setClass("Foo") setClassUnion("Foo2", "Foo") }) expect_s3_class(obj, "s4class") expect_equal(obj$topic, "Foo2-class") }) test_that("finds S4 generics and methods", { obj <- call_to_object({ setGeneric("bar", function(x) standardGeneric("bar")) }) expect_s3_class(obj, "s4generic") obj <- call_to_object({ setGeneric("bar", function(x) standardGeneric("bar")) setMethod('bar', 'Foo', function(x) {}) }) expect_s3_class(obj, "s4method") obj <- call_to_object({ setGeneric("bar<-", function(x, value) standardGeneric("bar<-")) setReplaceMethod("bar", "Foo", function(x, value) {}) }) expect_s3_class(obj, "s4method") }) test_that("finds correct parser even when namespaced", { obj <- call_to_object({ setClass("Foo") setGeneric("baz", function(x) standardGeneric("baz")) methods::setMethod('baz', 'Foo', function(x) {}) }) expect_s3_class(obj, "s4method") }) test_that("finds arguments when S4 method wrapped inside .local()", { obj <- call_to_object({ setClass("Foo") setMethod('subset', 'Foo', function(x, foo, ...) {}) }) expect_s3_class(obj, "s4method") expect_named(formals(obj$value@.Data), c("x", "foo", "...")) }) # S7 ---------------------------------------------------------------------- test_that("finds S7 classes", { skip_unless_r(">= 4.3.0") obj <- call_to_object({ Dog <- S7::new_class( "Dog", properties = list( name = S7::class_character, age = S7::class_numeric ) ) }) expect_s3_class(obj, "s7class") expect_equal(obj$topic, "Dog") expect_equal(obj$alias, "Dog") }) test_that("finds S7 generics", { skip_unless_r(">= 4.3.0") obj <- call_to_object({ speak <- S7::new_generic("speak", "x") }) expect_s3_class(obj, "s7generic") expect_equal(obj$topic, "speak") expect_equal(obj$alias, "speak") }) test_that("finds S7 methods", { skip_unless_r(">= 4.3.0") obj <- call_to_object({ Dog <- S7::new_class("Dog") speak <- S7::new_generic("speak", "x") S7::method(speak, Dog) <- function(x) "Woof" }) expect_s3_class(obj, "s7method") expect_equal(obj$topic, "speak,Dog-method") expect_equal(obj$value$generic, "speak") expect_equal(obj$value$classes, list("Dog")) }) test_that("finds S7 multi-dispatch methods", { skip_unless_r(">= 4.3.0") obj <- call_to_object({ Dog <- S7::new_class("Dog") Cat <- S7::new_class("Cat") greet <- S7::new_generic("greet", c("x", "y")) S7::method(greet, list(Dog, Cat)) <- function(x, y) "hi" }) expect_s3_class(obj, "s7method") expect_equal(obj$topic, "greet,Dog,Cat-method") expect_equal(obj$value$classes, list("Dog", "Cat")) }) test_that("S7 union method has aliases for individual classes", { skip_unless_r(">= 4.3.0") obj <- call_to_object({ Dog <- S7::new_class("Dog") Cat <- S7::new_class("Cat") Pet <- S7::new_union(Dog, Cat) speak <- S7::new_generic("speak", "x") S7::method(speak, Pet) <- function(x) "hi" }) expect_s3_class(obj, "s7method") expect_equal(obj$topic, "speak,Dog/Cat-method") expect_equal(obj$alias, c("speak,Dog-method", "speak,Cat-method")) }) test_that("S7 non-union method has no extra aliases", { skip_unless_r(">= 4.3.0") obj <- call_to_object({ Dog <- S7::new_class("Dog") speak <- S7::new_generic("speak", "x") S7::method(speak, Dog) <- function(x) "Woof" }) expect_equal(obj$topic, "speak,Dog-method") # alias is _extra_ aliases, apart from topic name expect_null(obj$alias) }) test_that("finds S7 methods for S3 generics", { skip_unless_r(">= 4.3.0") obj <- call_to_object({ Dog <- S7::new_class("Dog") S7::method(print, Dog) <- function(x, ...) cat("Dog\n") }) expect_s3_class(obj, "s7method") expect_equal(obj$topic, "print,Dog-method") expect_equal(obj$value$generic, "print") }) test_that("S7 method topic includes package prefix in class name", { skip_unless_r(">= 4.3.0") obj <- call_to_object({ Dog <- S7::new_class("Dog", package = "mypkg") speak <- S7::new_generic("speak", "x") S7::method(speak, Dog) <- function(x) "Woof" }) expect_equal(obj$topic, "speak,mypkg::Dog-method") expect_equal(obj$value$classes, list("mypkg::Dog")) }) test_that("S7 method on S3 generic includes package prefix in class name", { skip_unless_r(">= 4.3.0") obj <- call_to_object({ Dog <- S7::new_class("Dog", package = "mypkg") S7::method(print, Dog) <- function(x, ...) cat("Dog\n") }) expect_equal(obj$topic, "print,mypkg::Dog-method") expect_equal(obj$value$generic, "print") }) test_that("S7 method with special classes any and missing", { skip_unless_r(">= 4.3.0") block <- roxy_block(tags = list(), file = "test.R", line = 1, call = quote(x)) expect_equal(s7_class_name(S7::class_any, block), "any") expect_equal(s7_class_name(S7::class_missing, block), "missing") }) test_that("S7 method with unknown class type warns", { skip_unless_r(">= 4.3.0") block <- roxy_block(tags = list(), file = "test.R", line = 1, call = quote(x)) expect_snapshot(s7_class_name(42L, block)) }) # R.oo / R.methodsS3 ------------------------------------------------------ test_that("can define constructor with R.oo", { skip_if_not_installed("R.oo") obj <- call_to_object({ R.oo::setConstructorS3("Foo", function(x, y, z) {}) }) expect_s3_class(obj, "function") expect_equal(obj$alias, "Foo") }) test_that("can define method for R.methodsS3", { skip_if_not_installed("R.methodsS3") obj <- call_to_object({ R.methodsS3::setMethodS3("foo", "default", function(x, ...) {}) }) expect_s3_class(obj, "s3method") expect_equal(obj$alias, "foo.default") }) # extract_method_fun ------------------------------------------------------ test_that("fails gracefully on bad inputs", { fun1 <- function() {} fun2 <- function() 1 + 2 fun3 <- function() { 1 + 2 } fun4 <- function() { x <- 1 } fun5 <- function() { .local <- 1 } expect_equal(extract_method_fun(fun1), fun1) expect_equal(extract_method_fun(fun2), fun2) expect_equal(extract_method_fun(fun3), fun3) expect_equal(extract_method_fun(fun4), fun4) expect_equal(extract_method_fun(fun5), fun5) }) # R6 $set() --------------------------------------------------------------- test_that("recognises R6$set() calls", { obj <- call_to_object({ C <- R6::R6Class("C", public = list(m = function() {})) C$set("public", "meth", function(x) {}) }) expect_s3_class(obj, "r6method") expect_equal(obj$value, list(class = "C", method = "meth")) }) test_that("ignores non-R6 $ calls", { obj <- call_to_object({ x <- list(set = function(...) {}) x$set(x) }) expect_null(obj) }) test_that("ignores $set() with non-string method name", { obj <- call_to_object({ C <- R6::R6Class("C", public = list(m = function() {})) name <- "meth" C$set("public", name, function(x) {}) }) expect_null(obj) }) roxygen2/tests/testthat/test-utils-warn.R0000644000176200001440000000025115152035772020243 0ustar liggesuserstest_that("warn_roxy_function works when fun doesn't have a srcref", { fun <- zap_srcref(function() {}) expect_snapshot(warn_roxy_function(fun, "test message")) }) roxygen2/tests/testthat/Rd-example-1.R0000644000176200001440000000002613523072405017307 0ustar liggesusersexample <- 'example1' roxygen2/tests/testthat/test-options.R0000644000176200001440000000234215163737466017647 0ustar liggesuserstest_that("can load options from old Roxygen field", { opts <- load_options(test_path("test-options")) expect_equal(opts$old_usage, TRUE) # from DESCRIPTION expect_equal(opts$markdown, TRUE) # from meta.R }) test_that("can load options from Config/roxygen2/ fields", { opts <- load_options(test_path("test-options-config")) expect_equal(opts$old_usage, TRUE) expect_equal(opts$packages, c("mypkg1", "mypkg2")) }) test_that("Config/roxygen2/ fields take priority over Roxygen field", { opts <- load_options(test_path("test-options-both")) expect_equal(opts$old_usage, TRUE) # from Roxygen field expect_equal(opts$markdown, FALSE) # Config overrides Roxygen }) test_that("parse_config_value handles scalars and vectors", { expect_equal(parse_config_value("TRUE"), TRUE) expect_equal(parse_config_value("FALSE"), FALSE) expect_equal(parse_config_value("installed"), "installed") expect_equal(parse_config_value("a, b, c"), c("a", "b", "c")) }) test_that("warns on invalid meta.R files", { expect_warning( load_options_meta(test_path("test-options"), "meta-error.R"), "Failed to source" ) expect_warning( load_options_meta(test_path("test-options"), "meta-character.R"), "yield a named list" ) }) roxygen2/tests/testthat/test-rd-examples.R0000644000176200001440000001221715166171276020371 0ustar liggesuserstest_that("@example loads from specified files", { out <- roc_proc_text( rd_roclet(), " #' @name a #' @title a #' #' @example Rd-example-1.R #' @example Rd-example-2.R NULL" )[[1]] examples <- out$get_value("examples") expect_match(examples, "example <- 'example1'", all = FALSE, fixed = TRUE) expect_match(examples, "example <- 'example2'", all = FALSE, fixed = TRUE) }) test_that("@example captures examples (#470)", { out <- roc_proc_text( rd_roclet(), " #' @name a #' @title a #' @examples #' TRUE #' @examples #' FALSE NULL" )[[1]] examples <- out$get_value("examples") expect_equal(examples, rd(c("TRUE", "FALSE"))) }) test_that("@examples and @example interleave", { out <- roc_proc_text( rd_roclet(), " #' @name a #' @title a #' @example Rd-example-1.R #' @examples a <- 2 #' @example Rd-example-2.R NULL" )[[1]] expect_snapshot_output(out$get_section("examples")) }) test_that("@example does not introduce extra empty lines", { out <- roc_proc_text( rd_roclet(), " #' @name a #' @title a #' @example Rd-example-3.R NULL" )[[1]] expect_equal(re_count(out$get_value("examples"), "\n"), 1L) }) test_that("@example gives warning if used instead of @examples", { block <- " #' @name a #' @title a #' @example #' a <- 1 #' a + b NULL " expect_snapshot(out <- roc_proc_text(rd_roclet(), block)[[1]]) expect_null(out$get_value("examples")) }) test_that("warns if path doesn't exist", { block <- " #' @name a #' @title a #' @example this-path-doesnt-exist.R NULL " expect_snapshot(. <- roc_proc_text(rd_roclet(), block)) }) test_that("@examplesIf", { out <- roc_proc_text( rd_roclet(), " #' @name a #' @title a #' @examplesIf foo::bar() #' maybe-run-this-code #' @examplesIf foobar() #' and-this #' and-that NULL" )[[1]] expect_snapshot_output(out$get_section("examples")) }) test_that("@examplesIf warns about unparseable condition", { block <- " #' @name a #' @title a #' @examplesIf 1 + #' maybe-run-this-code NULL " expect_snapshot(out <- roc_proc_text(rd_roclet(), block)) expect_equal(out[[1]]$get_section("examples"), NULL) }) test_that("@examplesIf warns on empty body (#1695)", { block <- " #' @name a #' @title a #' @examplesIf interactive() NULL " expect_snapshot(out <- roc_proc_text(rd_roclet(), block)) expect_equal(out[[1]]$get_section("examples"), NULL) }) test_that("strings in R comments don't affect brace matching (#1492)", { block <- " #' @name a #' @title a #' @examples #' # {greeting}' NULL" expect_silent(out <- roc_proc_text(rd_roclet(), block)[[1]]) expect_equal(out$get_value("examples"), rd("# {greeting}'")) }) test_that("braces don't need to match inside of raw strings (#1492)", { block <- " #' @name a #' @title a #' @examples #' r'( '{{ )' NULL " expect_silent(out <- roc_proc_text(rd_roclet(), block)[[1]]) expect_equal(out$get_value("examples"), rd("r'( '{{ )'")) }) test_that("% in @examples escaped before matching braces test (#213)", { block <- " #' @name a #' @title a #' @examples #' {a %% b} NULL " out <- roc_proc_text(rd_roclet(), block)[[1]] expect_equal(out$get_value("examples"), rd("{a \\%\\% b}")) }) # strip_rd_example_tags ----------------------------------------------------- test_that("strip_rd_example_tags unwraps \\dontrun and \\donttest", { expect_equal(strip_rd_example_tags("\\dontrun{\n1 + 1\n}"), "1 + 1") expect_equal(strip_rd_example_tags("\\donttest{\n1 + 1\n}"), "1 + 1") }) test_that("strip_rd_example_tags removes \\dontshow entirely", { expect_equal(strip_rd_example_tags("\\dontshow{\n1 + 1\n}"), "") }) test_that("strip_rd_example_tags preserves surrounding code", { expect_equal( strip_rd_example_tags("x <- 1\n\\dontrun{\n1 + 1\n}\ny <- 2"), "x <- 1\n1 + 1\ny <- 2" ) }) test_that("strip_rd_example_tags handles nested tags", { expect_equal( strip_rd_example_tags("\\dontrun{\n\\dontshow{setup}\n1 + 1\n}"), "1 + 1" ) }) test_that("strip_rd_example_tags handles examplesIf wrappers", { input <- paste0( "\\dontshow{if (TRUE) withAutoprint(\\{ # examplesIf}\n", "1 + 1\n", "\\dontshow{\\}) # examplesIf}" ) expect_equal(strip_rd_example_tags(input), "1 + 1") }) test_that("strip_rd_example_tags is no-op with no tags", { expect_equal(strip_rd_example_tags("job$run()"), "job$run()") }) # escapes ------------------------------------------------------------------ test_that("only % escaped in @examples", { expect_equal(escape_examples("x %*% y"), rd("x \\%*\\% y")) expect_equal(escape_examples("# \\x"), rd("# \\x")) expect_equal(escape_examples("'34.00\\'"), rd("'34.00\\'")) }) test_that("multi-line macros in @example", { # https://github.com/r-lib/roxygen2/issues/974 out <- roc_proc_text( rd_roclet(), " #' @name a #' @title a #' #' @example Rd-example-4.txt NULL" )[[1]] expect_equal( format(out$get_section("examples")), "\\examples{\n\\dontrun{\n1 + 1\n}\n}" ) }) roxygen2/tests/testthat/test-namespace.R0000644000176200001440000003425015172453522020077 0ustar liggesuserstest_that("end-to-end NAMESPACE generation works", { path <- local_package_copy(test_path("testNamespace")) suppressMessages(roxygenise(path)) withr::defer(pkgload::unload("testNamespace")) ns <- read_lines(file.path(path, "NAMESPACE")) expect_equal( ns, c( "# Generated by roxygen2: do not edit by hand", "", "export(f)", "export(g)" ) ) }) test_that("parsing warnings only fire once", { path <- local_package_copy(test_path("testNamespace")) write_lines( c("#' @importFrom stats median", "#' ave", "NULL"), file.path(path, "R", "multiline.R") ) withr::defer(pkgload::unload("testNamespace")) # Warning fires once (during the main parse_package() pass), # not also during update_namespace_imports()'s early pass. expect_snapshot(roxygenise(path)) }) # @export ----------------------------------------------------------------- test_that("export quote object name appropriate", { out <- roc_proc_text(namespace_roclet(), "#' @export\na <- function(){}") expect_equal(out, 'export(a)') out <- roc_proc_text(namespace_roclet(), "#' @export\n`+` <- function(){}") expect_equal(out, 'export("+")') out <- roc_proc_text(namespace_roclet(), "#' @export\n`\\`` <- function(){}") expect_equal(out, 'export("`")') }) test_that("export parameter overrides default", { out <- roc_proc_text(namespace_roclet(), "#' @export b\na <- function(){}") expect_equal(out, 'export(b)') }) test_that("multiple export parameters generate multiple exports", { out <- roc_proc_text( namespace_roclet(), " #' @export a b a <- function(){}" ) expect_equal(out, c('export(a)', 'export(b)')) }) test_that("export trimmed before line test", { out <- roc_proc_text( namespace_roclet(), " #' @export #' a <- function(){}" ) expect_equal(out, 'export(a)') }) test_that("export detects S4 class", { out <- roc_proc_text(namespace_roclet(), "#' @export\nsetClass('a')") expect_equal(out, 'exportClasses(a)') }) test_that("exports constructor function if created", { out <- roc_proc_text(namespace_roclet(), "#' @export\na <- setClass('a')") expect_equal(out, c('export(a)', 'exportClasses(a)')) }) test_that("export detects S4 generic", { out <- roc_proc_text( namespace_roclet(), " #' @export setGeneric('foo', function(x) standardGeneric('foo')) " ) expect_equal(out, 'export(foo)') }) test_that("export detects S3 method", { out <- roc_proc_text( namespace_roclet(), "#' @export\nmean.foo <- function(x) 'foo'" ) expect_equal(out, 'S3method(mean,foo)') }) test_that("export handles non-syntactic names", { out <- roc_proc_text( namespace_roclet(), " #' @export `mean.foo-bar` <- function(x) 'foo' " ) expect_equal(out, "S3method(mean,\"foo-bar\")") out <- roc_proc_text( namespace_roclet(), " `foo-bar` <- function(x) UseMethod('foo-bar') #' @export `foo-bar.integer` <- function(x) 'foo' " ) expect_equal(out, "S3method(\"foo-bar\",integer)") }) test_that("@exportS3Method generates fully automatically", { out <- roc_proc_text( namespace_roclet(), " #' @exportS3Method mean.foo <- function(x) 'foo' " ) expect_equal(out, "S3method(mean,foo)") block <- " #' @exportS3Method f <- function(x) 'foo' " expect_snapshot(. <- roc_proc_text(namespace_roclet(), block)) }) test_that("@exportS3Method can create literal directive", { out <- roc_proc_text( namespace_roclet(), "#' @exportS3Method base::mean foo NULL " ) expect_equal(out, "S3method(base::mean,foo)") }) test_that("@exportS3Method can extract class from generic", { out <- roc_proc_text( namespace_roclet(), " #' @exportS3Method pkg::foo foo.bar <- function(x) 'foo' " ) expect_equal(out, "S3method(pkg::foo,bar)") block <- " #' @exportS3Method pkg_foo foo.bar <- function(x) 'foo' " expect_snapshot(. <- roc_proc_text(namespace_roclet(), block)) block <- " #' @exportS3Method pkg::foo foo1.bar <- 10 " expect_snapshot(. <- roc_proc_text(namespace_roclet(), block)) block <- " #' @exportS3Method pkg::foo foo1.bar <- function(x) 'foo' " expect_snapshot(. <- roc_proc_text(namespace_roclet(), block)) }) test_that("exportClass overrides default class name", { out <- roc_proc_text(namespace_roclet(), "#' @exportClass b\nsetClass('a')") expect_equal(out, 'exportClasses(b)') }) test_that("export detects method name", { out <- roc_proc_text( namespace_roclet(), " #' @export\n setMethod('max', 'a', function(x, ...) x[1])" ) expect_equal(out, 'exportMethods(max)') }) test_that("export method escapes if needed", { out <- roc_proc_text( namespace_roclet(), " setGeneric('x<-', function(x, value) standardGeneric('x<-')) #' @export\n setMethod('x<-', 'a', function(x, value) value)" ) expect_equal(out, 'exportMethods("x<-")') }) test_that("export uses name if no object present", { out <- roc_proc_text( namespace_roclet(), " #' Title #' #' @export #' @name x NULL " ) expect_equal(out, 'export(x)') }) test_that("default export uses exportClass for RC objects", { out <- roc_proc_text( namespace_roclet(), " #' Title #' #' @export x <- setRefClass('X') " ) expect_equal(out, 'exportClasses(X)') }) test_that("exportMethod overrides default method name", { out <- roc_proc_text( namespace_roclet(), " #' @exportMethod c setMethod('max', 'a', function(x, ...) x[1])" ) expect_equal(out, 'exportMethods(c)') }) test_that("other namespace tags produce correct output", { out <- roc_proc_text( namespace_roclet(), " #' @exportPattern test #' @import test #' @importFrom test test1 test2 #' @importClassesFrom test test1 test2 #' @importMethodsFrom test test1 test2 NULL" ) expect_equal( sort(out), sort(c( "exportPattern(test)", "import(test)", "importFrom(test,test1)", "importFrom(test,test2)", "importClassesFrom(test,test1)", "importClassesFrom(test,test2)", "importMethodsFrom(test,test1)", "importMethodsFrom(test,test2)" )) ) }) test_that("import directives for current package are ignored", { withr::local_envvar(c("ROXYGEN_PKG" = "ignored")) out <- roc_proc_text( namespace_roclet(), " #' @import ignored #' @import test ignored test2 #' @importFrom ignored test1 test2 #' @importClassesFrom ignored test1 test2 #' @importMethodsFrom ignored test1 test2 NULL" ) expect_equal( sort(out), sort(c( "import(test)", "import(test2)" )) ) }) test_that("poorly formed importFrom throws error", { block <- " #' @importFrom test NULL " expect_snapshot(. <- roc_proc_text(namespace_roclet(), block)) }) test_that("multiline importFrom generates warning", { block <- " #' @importFrom test test1 #' test2 NULL " expect_snapshot(. <- roc_proc_text(namespace_roclet(), block)) }) test_that("useDynLib imports only selected functions", { out <- roc_proc_text( namespace_roclet(), " #' @useDynLib test #' @useDynLib test a #' @useDynLib test a b NULL" ) expect_equal( sort(out), sort( c("useDynLib(test)", "useDynLib(test,a)", "useDynLib(test,b)") ) ) }) test_that("useDynLib doesn't quote if comma present", { out <- roc_proc_text( namespace_roclet(), " #' @useDynLib test, .registration = TRUE NULL" ) expect_equal(sort(out), "useDynLib(test, .registration = TRUE)") }) test_that("empty NAMESPACE generates zero-length vector", { base_path <- test_path("empty") env <- pkgload::load_all(base_path, quiet = TRUE)$env withr::defer(pkgload::unload("empty")) blocks <- parse_package(base_path, env = env) results <- roclet_process(namespace_roclet(), blocks, env = env, base_path) expect_equal(results, character()) }) test_that("can regenerate NAMESPACE even if its broken", { path <- local_package_copy(test_path("broken-namespace")) expect_snapshot(update_namespace_imports(path)) expect_equal( read_lines(file.path(path, "NAMESPACE")), c( "# Generated by roxygen2: do not edit by hand", "", "importFrom(stats,median)" ) ) }) # Raw --------------------------------------------------------------------- test_that("rawNamespace must be valid code", { block <- " #' @rawNamespace a + NULL " expect_snapshot(. <- roc_proc_text(namespace_roclet(), block)) }) test_that("rawNamespace inserted unchanged", { out <- roc_proc_text( namespace_roclet(), " #' @name a #' @rawNamespace xyz #' abc NULL" ) expect_equal(out, "xyz\n abc") }) test_that("rawNamespace does not break idempotency", { test_pkg <- local_package_copy(test_path("testRawNamespace")) NAMESPACE <- file.path(test_pkg, "NAMESPACE") lines_orig <- read_lines(NAMESPACE) expect_no_error(suppressMessages(roxygenize(test_pkg, "namespace"))) # contents unchanged expect_equal(read_lines(NAMESPACE), lines_orig) }) # @evalNamespace ---------------------------------------------------------- test_that("evalNamespace warns for bad code", { block <- " #' @evalNamespace a + #' @name a #' @title a NULL " expect_snapshot(. <- roc_proc_text(namespace_roclet(), block)) block <- " #' @evalNamespace stop('Uhoh') #' @name a #' @title a NULL " expect_snapshot(. <- roc_proc_text(namespace_roclet(), block)) block <- " #' @evalNamespace 1 #' @name a #' @title a NULL " expect_snapshot(. <- roc_proc_text(namespace_roclet(), block)) }) test_that("evalNamespace code is inserted when its value is a string", { out1 <- roc_proc_text( namespace_roclet(), " nms <- paste(letters[1:3], collapse = ',') #' @evalNamespace sprintf('export(%s)', nms) #' @name a #' @title a NULL" ) out2 <- roc_proc_text( namespace_roclet(), " nms <- paste(letters[1:3], collapse = ',') #' @evalNamespace sprintf('export(%s)', #' nms) #' @name a #' @title a NULL" ) expect_equal(out1, "export(a,b,c)") expect_equal(out2, "export(a,b,c)") }) test_that("evalNamspace can yield a vector", { out <- roc_proc_text( namespace_roclet(), " nms <- letters[1:2] #' @evalNamespace paste0('export(', nms, ')') #' @name a #' @title a NULL" ) expect_equal(out, c("export(a)", "export(b)")) }) # helpers ----------------------------------------------------------------- test_that("auto_quote behaves as needed", { expect_equal(auto_quote("x"), "x") expect_equal(auto_quote("if"), '"if"') # quotes non-syntactic expect_equal(auto_quote("'if'"), "'if'") # unless already quoted }) test_that("can extract non-imports from namespace preserving source", { path <- withr::local_tempfile( lines = c( "export(x)", "import(y)" ) ) expect_equal(namespace_exports(path), "export(x)") path <- withr::local_tempfile(lines = "export(x, y, z)") expect_equal(namespace_exports(path), "export(x, y, z)") lines <- c( "if (TRUE) {", " import(x, y, z)", "}", "import(a)", "export(b)" ) path <- withr::local_tempfile(lines = lines) expect_equal( namespace_exports(path), c( paste(lines[1:3], collapse = "\n"), lines[5L] ) ) }) test_that("invalid imports generate correct declarations", { # No matched functions --> no output block <- " #' @importFrom utils InvalidUtilsFunction NULL " expect_message(out <- roc_proc_text(namespace_roclet(), block)) expect_equal(out, character()) # Matched functions --> only drop unmatched functions block <- " #' @importFrom utils head InvalidUtilsFunction NULL " expect_message(out <- roc_proc_text(namespace_roclet(), block)) expect_equal(out, "importFrom(utils,head)") }) test_that("invalid imports generate helpful message", { block <- " #' @importFrom utils head InvalidUtilsFunction1 NULL " expect_snapshot(out <- roc_proc_text(namespace_roclet(), block)) block <- " #' @importFrom utils head InvalidUtilsFunction1 InvalidUtilsFunction2 NULL " expect_snapshot(out <- roc_proc_text(namespace_roclet(), block)) }) test_that("nothing we can do if package isn't installed", { block <- " #' @importFrom AnUnknownUnavailablePackage Unchecked NULL " expect_no_message(out <- roc_proc_text(namespace_roclet(), block)) expect_equal(out, "importFrom(AnUnknownUnavailablePackage,Unchecked)") }) test_that("non-syntactic imports can use multiple quoting forms", { lines <- c( "#' @importFrom stringr %>%", "#' @importFrom stringr `%>%`", "#' @importFrom stringr '%>%'", "#' @importFrom stringr \"%>%\"", "NULL" ) import <- expect_no_warning(roc_proc_text(namespace_roclet(), lines)) expect_equal( import, c( "importFrom(stringr,\"%>%\")", "importFrom(stringr,'%>%')", "importFrom(stringr,`%>%`)" ) ) }) # warn_missing_s3_exports ------------------------------------------------- test_that("warns if S3 method not documented", { # Need to manually transform since the srcref is coming from the function; # roc_proc_text() uses fake srcrefs for the blocks themselves fix_srcref <- function(x) gsub("file[a-z0-9]+", "", x) block <- " foo <- function(x) UseMethod('foo') foo.numeric <- function(x) 1 mean.myclass <- function(x) 2 " expect_snapshot( . <- roc_proc_text(namespace_roclet(), block), transform = fix_srcref ) # Works even if method contains { block <- " foo <- function(x) UseMethod('foo') `foo.{` <- function(x) 1 " expect_snapshot( . <- roc_proc_text(namespace_roclet(), block), transform = fix_srcref ) }) test_that("can suppress the warning", { block <- " #' @exportS3Method NULL mean.myclass <- function(x) 1 " expect_silent(out <- roc_proc_text(namespace_roclet(), block)) expect_equal(out, character()) }) test_that("doesn't warn for potential false postives", { roc <- namespace_roclet() expect_no_warning({ roc_proc_text(roc, "foo.numeric <- function(x) 1") roc_proc_text(roc, "is.numeric <- function(x) 1") roc_proc_text(roc, "as.numeric <- function(x) 1") }) }) roxygen2/tests/testthat/test-markdown-state.R0000644000176200001440000000524215151411610021067 0ustar liggesuserstest_that("markdown is off by default", { out1 <- roc_proc_text( rd_roclet(), " #' Title #' #' Description with some `code` included. `More code.` foo <- function() {}" )[[1]] expect_equal( out1$get_value("description"), "Description with some `code` included. `More code.`" ) }) test_that("turning on/off markdown globally", { out1 <- roc_proc_text( rd_roclet(), " #' Title #' #' Description with some `code` included. `More code.` foo <- function() {}" )[[1]] expect_equal( out1$get_value("description"), "Description with some `code` included. `More code.`" ) local_roxy_meta_set("markdown", TRUE) out1 <- roc_proc_text( rd_roclet(), " #' Title #' #' Description with some `code` included. `More code.` foo <- function() {}" )[[1]] expect_equal( out1$get_value("description"), r"(Description with some \code{code} included. \verb{More code.})" ) }) test_that("turning on/off markdown locally", { out1 <- roc_proc_text( rd_roclet(), " #' Title #' #' Description with some `code` included. `More code.` #' @noMd foo <- function() {}" )[[1]] expect_equal( out1$get_value("description"), "Description with some `code` included. `More code.`" ) out1 <- roc_proc_text( rd_roclet(), " #' Title #' #' Description with some `code` included. `More code.` #' @md foo <- function() {}" )[[1]] expect_equal( out1$get_value("description"), r"(Description with some \code{code} included. \verb{More code.})" ) local_roxy_meta_set("markdown", TRUE) out1 <- roc_proc_text( rd_roclet(), " #' Title #' #' Description with some `code` included. `More code.` #' @noMd foo <- function() {}" )[[1]] expect_equal( out1$get_value("description"), "Description with some `code` included. `More code.`" ) ## on / on out1 <- roc_proc_text( rd_roclet(), " #' Title #' #' Description with some `code` included. `More code.` #' @md foo <- function() {}" )[[1]] expect_equal( out1$get_value("description"), r"(Description with some \code{code} included. \verb{More code.})" ) }) test_that("warning for both @md and @noMd", { block <- " #' Title #' #' `code` #' @md #' @noMd foo <- function() {} " expect_snapshot(out1 <- roc_proc_text(rd_roclet(), block)) expect_equal(out1[[1]]$get_value("description"), "`code`") # No translation even if markdown on generally local_markdown() suppressMessages(out2 <- roc_proc_text(rd_roclet(), block)) expect_equal(out2[[1]]$get_value("description"), "`code`") }) roxygen2/tests/testthat/test-rd-template.R0000644000176200001440000000240015151411610020336 0ustar liggesuserstest_that("invalid syntax generates useful warning", { block <- " #' A #' @templateVar a <- function() {} " expect_snapshot(. <- roc_proc_text(rd_roclet(), block)) }) test_that("can find template from name", { base <- test_path("templates/") expect_equal( template_find(base, "UCase"), file.path(base, "man-roxygen", "UCase.R") ) # On case-insensitive file systems, will find upper case version first expect_equal( tolower(template_find(base, "lcase")), tolower(file.path(base, "man-roxygen", "lcase.r")) ) expect_equal( template_find(base, "new-path"), file.path(base, "man", "roxygen", "templates", "new-path.R") ) expect_error( template_find(base, "missing"), "Can't find template" ) }) test_that("templates gives useful error if not found", { block <- " #' @template doesn't-exist x <- 10 " expect_snapshot(roc_proc_text(rd_roclet(), block), error = TRUE) }) test_that("templates replace variables with their values", { out <- roc_proc_text( rd_roclet(), " #' @template values #' @templateVar x a #' @templateVar y b #' @templateVar z c x <- 10" )[[1]] expect_equal(out$get_value("title"), "a") expect_equal(out$get_value("param"), c(b = "c")) }) roxygen2/tests/testthat/test-topic.R0000644000176200001440000000107613544473137017266 0ustar liggesuserstest_that("adding tags merges with existing", { rd <- RoxyTopic$new() rd$add(rd_section("x", 1)) rd$add(rd_section("x", 2)) expect_equal(rd$get_value("x"), c(1, 2)) }) test_that("unless overwrite = TRUE", { rd <- RoxyTopic$new() rd$add(rd_section("x", 1)) rd$add(rd_section("x", 2), overwrite = TRUE) expect_equal(rd$get_value("x"), 2) }) test_that("can add a complete file", { rd1 <- RoxyTopic$new() rd2 <- RoxyTopic$new() rd1$add(rd_section("x", 1)) rd2$add(rd_section("x", 2)) rd2$add(rd1) expect_equal(rd2$get_value("x"), c(2, 1)) }) roxygen2/tests/testthat/test-rd-section.R0000644000176200001440000000215715151411610020200 0ustar liggesuserstest_that("warn if forgotten colon", { block <- " #' Foo #' #' @section Haz dox #' Here. #' There foo <- function(x = '%') x " expect_snapshot(. <- roc_proc_text(rd_roclet(), block)) }) test_that("@section-s with identical titles are merged", { block <- " #' Foo #' #' @section Haz dox: Here. #' #' @section TL: DR. foo <- function(x = '%') x #' @rdname foo #' @section RT: FM. #' @section Haz dox: #' Got news. bar <- function(y = '%') y " out <- roc_proc_text(rd_roclet(), block)[[1]] expect_equal( out$get_section("section"), rd_section_section( c("Haz dox", "TL", "RT"), c(" Here.\n\n\n Got news.", " DR.", " FM.") ) ) }) test_that("@section-s with different titles are kept as they are", { out <- roc_proc_text( rd_roclet(), " #' Foo #' #' @section A: 1 #' @section B: 2 foo <- function(x) x #' @rdname foo #' @section C: 3 bar <- function(x) x " )[[1]] expect_equal( out$get_section("section"), rd_section_section(LETTERS[1:3], c(" 1", " 2", " 3")) ) }) roxygen2/tests/testthat/test-object-package.R0000644000176200001440000001001015154314256020766 0ustar liggesuserstest_that("person turned into meaningful text", { person_desc <- function( given = "H", family = "W", email = "h@w.com", role = "aut", comment = NULL ) { out <- person( given = given, family = family, email = email, role = role, comment = comment ) author_desc(unclass(out)[[1]]) } expect_snapshot({ "Multiple given/family names" person_desc(c("First", "Second"), c("Family1", "Family2")) "Multiple roles" person_desc(role = "ctb") "ORCID comments" person_desc(comment = c("ORCID" = "0000-0003-4757-117X")) person_desc(comment = c("ORCID" = "https://orcid.org/0000-0003-4757-117X")) person_desc(comment = c("ORCID" = "0000-0003-4757-117X", "extra")) "ROR comments" person_desc(comment = c("ROR" = "03wc8by49")) person_desc(comment = c("ROR" = "https://ror.org/03wc8by49")) person_desc(comment = c("ROR" = "03wc8by49", "extra")) "Arbitrary comments (#1746)" person_desc(comment = c(acronym = "rajo", "Contributed XY")) person_desc(comment = c("comment 1", "comment 2")) }) }) test_that("cre+aut person appears in both maintainer and authors (#1588)", { authors <- 'c( person("A", "B", role = c("aut", "cre"), email = "a@b.com"), person("C", "D", role = "aut") )' out <- package_authors(authors) expect_match(out, "Maintainer.*A B") expect_match(out, "Authors.*A B") expect_match(out, "Authors.*C D") }) test_that("useful message if Authors@R is corrupted", { expect_snapshot({ package_authors("1 + ") package_authors("stop('Uhoh')") }) }) test_that("can convert quote percentage signs in urls", { expect_equal( package_seealso_urls("https://www.foo.bar/search?q=see%20also"), "\\url{https://www.foo.bar/search?q=see\\%20also}" ) expect_equal( package_seealso_urls( BugReports = "https://www.foo.bar/search?q=bug%20report" ), "Report bugs at \\url{https://www.foo.bar/search?q=bug\\%20report}" ) }) test_that("can convert DOIs in url", { expect_equal( package_seealso_urls("https://doi.org/10.5281/zenodo.1485309"), "\\doi{10.5281/zenodo.1485309}" ) }) test_that("annotated URLs are extracted correctly (#1420)", { expect_equal( package_seealso_urls("https://x (XYZ), https://u (ABC)"), c("\\url{https://x} (XYZ)", "\\url{https://u} (ABC)") ) }) test_that("can autolink urls on package Description", { expect_equal( package_url_parse("x y"), "x \\url{https://x.com} y" ) expect_equal( package_url_parse("x y"), "x \\url{https://x.com/\\%3C-\\%3E} y" ) }) test_that("can autolink DOIs", { expect_equal(package_url_parse("x y"), "x \\doi{abcdef} y") expect_equal(package_url_parse("x y"), "x \\doi{abcdef} y") # URL-decodes %XX sequences since \doi{} can't contain % (#1321) expect_equal(package_url_parse("x y"), "x \\doi{<->} y") expect_equal( package_url_parse( "x " ), "x \\doi{10.1175/1520-0469(1996)053<2365:PORWON>2.0.CO;2}" ) }) test_that("can autolink arxiv", { expect_equal( package_url_parse("x y"), "x \\href{https://arxiv.org/abs/abc}{arXiv:abc} y" ) expect_equal( package_url_parse("x y"), "x \\href{https://arxiv.org/abs/abc}{arXiv:abc} y" ) expect_equal( package_url_parse("x y"), "x \\href{https://arxiv.org/abs/abc}{arXiv:abc [def]} y" ) }) test_that("autolink several matching patterns", { text <- "url doi arxiv " expect_equal( package_url_parse(text), paste( "url \\url{http://a.com}", "doi \\doi{xx}", "arxiv \\href{https://arxiv.org/abs/xx}{arXiv:xx}" ) ) }) test_that("multiple email addresses for a person are acceptable #1487", { me <- person("me", email = c("one@email.me", "two@email.me")) expect_equal( author_desc(unclass(me)[[1]]), "me \\email{one@email.me} \\email{two@email.me}" ) }) roxygen2/tests/testthat/Rd-example-4.txt0000644000176200001440000000002214220671366017732 0ustar liggesusers\dontrun{ 1 + 1 } roxygen2/tests/testthat/test-collate.R0000644000176200001440000000320015151411610017542 0ustar liggesuserstest_that("collation as expected", { names <- generate_collate(test_path("collate")) before <- function(a, b) { all(which(names %in% a) < which(names %in% b)) } expect_true(before("undershorts", "pants")) expect_true(before(c("tie", "belt"), "jacket")) expect_true(before(c("socks", "undershorts", "pants"), "shoes")) }) test_that("update_collate() checks that directory exists", { expect_snapshot(update_collate("doesn't-exist"), error = TRUE) }) test_that("Collate field unchanged when no @includes", { path <- local_package_copy(test_path('testCollateNoIncludes')) update_collate(path) expect_equal(desc::desc_get_field("Collate", file = path), "b.R a.R") }) test_that("DESCRIPTION file is re-written only if collate changes", { path <- local_package_copy(test_path("testCollateOverwrite")) expect_snapshot( { update_collate(path) "Second run should be idempotent" update_collate(path) }, transform = function(x) gsub(path, "", x, fixed = TRUE) ) }) test_that("drops bad collect directives", { path <- local_package_copy(test_path("empty")) write_lines(c("#' @include foo", "NULL"), file.path(path, "R", "a.R")) write_lines(c("#' @include a.R", "NULL"), file.path(path, "R", "b.R")) unlink(file.path(path, "R/empty-package.R")) withr::with_dir(path, expect_snapshot(update_collate("."))) expect_equal(desc::desc_get_collate(file = path), c("a.R", "b.R")) }) test_that("can read from file name with utf-8 path", { path <- withr::local_tempfile( pattern = "Universit\u00e0-", lines = c("#' @include foo.R", NULL) ) expect_equal(find_includes(path), "foo.R") }) roxygen2/tests/testthat/testMdLinks/0000755000176200001440000000000015154314256017300 5ustar liggesusersroxygen2/tests/testthat/testMdLinks/DESCRIPTION0000644000176200001440000000024115154314256021003 0ustar liggesusersPackage: testMdLinks Title: Dummy package for testing markdown link resolution Version: 1.0.0 Imports: callr, processx, cli, pkgload, rlang roxygen2/tests/testthat/Rd-example-2.R0000644000176200001440000000002613523072405017310 0ustar liggesusersexample <- 'example2' roxygen2/tests/testthat/empty/0000755000176200001440000000000014527136371016200 5ustar liggesusersroxygen2/tests/testthat/empty/R/0000755000176200001440000000000013523072405016371 5ustar liggesusersroxygen2/tests/testthat/empty/R/empty-package.R0000644000176200001440000000000513523072405021236 0ustar liggesusersNULL roxygen2/tests/testthat/empty/DESCRIPTION0000644000176200001440000000014114527136371017702 0ustar liggesusersPackage: empty Version: 1.0.0 Encoding: UTF-8 Title: Empty package Description: An empty package roxygen2/tests/testthat/test-options-both/0000755000176200001440000000000015163737466020455 5ustar liggesusersroxygen2/tests/testthat/test-options-both/DESCRIPTION0000644000176200001440000000015215163737466022161 0ustar liggesusersPackage: testOptionsBoth Roxygen: list(old_usage = TRUE, markdown = TRUE) Config/roxygen2/markdown: FALSE roxygen2/tests/testthat/testLazyData/0000755000176200001440000000000014527170577017461 5ustar liggesusersroxygen2/tests/testthat/testLazyData/R/0000755000176200001440000000000014520730024017640 5ustar liggesusersroxygen2/tests/testthat/testLazyData/R/a.R0000644000176200001440000000002714520730024020202 0ustar liggesusers#' Data #' #' Desc "a" roxygen2/tests/testthat/testLazyData/data/0000755000176200001440000000000013523072405020354 5ustar liggesusersroxygen2/tests/testthat/testLazyData/data/a.rda0000644000176200001440000000011513523072405021261 0ustar liggesusersBZh91AY&SY C1D@  !2B`3(AܩA<^fV"(H_^!roxygen2/tests/testthat/testLazyData/NAMESPACE0000644000176200001440000000005613523072405020663 0ustar liggesusers# Generated by roxygen2: do not edit by hand roxygen2/tests/testthat/testLazyData/DESCRIPTION0000644000176200001440000000033214527170577021165 0ustar liggesusersPackage: testLazyData Title: Tools to make developing R code easier License: GPL-2 Description: Author: Hadley Maintainer: Hadley Version: 0.1 LazyData: TRUE Encoding: UTF-8 roxygen2/tests/testthat/test-object-import.R0000644000176200001440000000316415154546446020731 0ustar liggesuserstest_that("exporting a call to :: produces re-exports documentation", { block <- " #' @export testthat::auto_test " out <- roc_proc_text(rd_roclet(), block)[[1]] expect_equal( out$get_section("reexport"), rd_section_reexport("testthat", "auto_test") ) expect_equal(out$get_value("title"), "Objects exported from other packages") expect_equal(out$get_value("keyword"), "internal") expect_snapshot_output(cat(format(out))) # And generates correct namespace definitions out <- roc_proc_text(namespace_roclet(), block) expect_equal(out, c("export(auto_test)", "importFrom(testthat,auto_test)")) }) test_that("multiple re-exports are combined", { out <- roc_proc_text( rd_roclet(), " #' @export testthat::expect_lt #' @export testthat::expect_gt " )[[1]] expect_equal( out$get_section("reexport"), rd_section_reexport( c("testthat", "testthat"), c("expect_lt", "expect_gt") ) ) }) test_that("description generated correctly", { roc <- rd_roclet() out <- roc_proc_text( rd_roclet(), " #' @importFrom magrittr %>% #' @export magrittr::`%>%` " )[[1]] expect_null(out$get_section("description")) }) test_that("only non-infix reexported functions get ()", { skip_on_cran() expect_equal( reexport_link("testthat", "test_that"), "\\code{\\link[testthat:test_that]{test_that()}}" ) expect_equal( reexport_link("testthat", "CheckReporter"), "\\code{\\link[testthat:CheckReporter]{CheckReporter}}" ) expect_equal( reexport_link("pkg", "%op%"), "\\code{\\link[pkg:\\%op\\%]{\\%op\\%}}" ) }) roxygen2/tests/testthat/testCollateNoIncludes/0000755000176200001440000000000014520730024021275 5ustar liggesusersroxygen2/tests/testthat/testCollateNoIncludes/R/0000755000176200001440000000000014520730024021476 5ustar liggesusersroxygen2/tests/testthat/testCollateNoIncludes/R/a.R0000644000176200001440000000014514520730024022041 0ustar liggesusers# Manually edited the Collate field in the DESCRIPTION file so this *should* # run after b.R a <- 1 roxygen2/tests/testthat/testCollateNoIncludes/R/b.R0000644000176200001440000000000714520730024022037 0ustar liggesusersa <- 2 roxygen2/tests/testthat/testCollateNoIncludes/NAMESPACE0000644000176200001440000000003113523072405022512 0ustar liggesusersexportPattern("^[^\\.]") roxygen2/tests/testthat/testCollateNoIncludes/DESCRIPTION0000644000176200001440000000034614520730024023006 0ustar liggesusersPackage: testCollateNoIncludes Title: Test no change to Collate when there are no @includes License: GPL-2 Description: Author: Geoff Maintainer: Geoff Version: 0.1 Collate: b.R a.R roxygen2/tests/testthat/broken-namespace/0000755000176200001440000000000015160334463020250 5ustar liggesusersroxygen2/tests/testthat/broken-namespace/R/0000755000176200001440000000000014527170577020463 5ustar liggesusersroxygen2/tests/testthat/broken-namespace/R/x.R0000644000176200001440000000010014527170577021044 0ustar liggesusers#' @importFrom stats median NULL #' @export f <- function() {} roxygen2/tests/testthat/broken-namespace/NAMESPACE0000644000176200001440000000010414527170577021474 0ustar liggesusers# Generated by roxygen2: do not edit by hand importFrom(stats,foo) roxygen2/tests/testthat/broken-namespace/DESCRIPTION0000644000176200001440000000007014527170577021765 0ustar liggesusersPackage: brokenNamespace Version: 1.0.0 Encoding: UTF-8 roxygen2/tests/testthat/no-desc/0000755000176200001440000000000013523072405016362 5ustar liggesusersroxygen2/tests/testthat/no-desc/R/0000755000176200001440000000000013523072405016563 5ustar liggesusersroxygen2/tests/testthat/no-desc/R/no-description.R0000644000176200001440000000000513523072405021636 0ustar liggesusersNULL roxygen2/tests/testthat/no-desc/NAMESPACE0000644000176200001440000000003713523072405017601 0ustar liggesusersexportPattern("^[[:alpha:]]+") roxygen2/tests/testthat/test-block.R0000644000176200001440000001441415151411610017222 0ustar liggesusers# object ------------------------------------------------------------------ test_that("has thoughtful print method", { text <- "#' This is a title #' #' @param x,y A number #' @export f <- function(x, y) x + y " expect_snapshot(parse_text(text)[[1]]) }) # description block ------------------------------------------------------- test_that("title and description taken from first line if only one", { out <- roc_proc_text( rd_roclet(), " #' title #' @name a NULL" )[[1]] expect_equal(out$get_value("description"), "title") expect_equal(out$get_value("title"), "title") }) test_that("description taken from multiple titles if merged", { out <- roc_proc_text( rd_roclet(), " #' T1 #' @name a NULL #' T2 #' @name a NULL " )[[1]] expect_equal(out$get_value("title"), c("T1", "T2")) expect_equal(out$get_value("description"), c("T1", "T2")) }) test_that("title, description and details extracted correctly", { out <- roc_proc_text( rd_roclet(), " #' title #' #' description #' #' details #' @name a NULL" )[[1]] expect_equal(out$get_value("description"), "description") expect_equal(out$get_value("details"), "details") }) test_that("title taken from first paragraph", { out <- roc_proc_text( rd_roclet(), " #' Description with sentence. #' #' That continueth. #' @name a NULL" )[[1]] expect_equal(out$get_value("title"), "Description with sentence.") expect_equal(out$get_value("description"), "That continueth.") }) test_that("@title overrides default title", { out <- roc_proc_text( rd_roclet(), " #' Would be title #' @title Overridden title #' @name a NULL" )[[1]] expect_equal(out$get_value("title"), "Overridden title") expect_equal(out$get_value("description"), "Would be title") }) test_that("docs parsed correctly if no blank text", { out <- roc_proc_text( rd_roclet(), " #' @title My title #' @description My description #' @param x value a <- function(x) {}" )[[1]] expect_equal(out$get_value("title"), "My title") expect_equal(out$get_value("description"), "My description") }) test_that("question mark ends sentence", { out <- roc_proc_text( rd_roclet(), " #' Is a number odd? is.odd <- function(a) {}" )[[1]] expect_equal(out$get_value("title"), "Is a number odd?") }) test_that("no ending punctuation does not produce ellipsis", { out <- roc_proc_text( rd_roclet(), " #' Whether a number is odd is.odd <- function(a) {}" )[[1]] expect_equal(out$get_value("title"), "Whether a number is odd") }) test_that("details are merged if needed", { out <- roc_proc_text( rd_roclet(), " #' Title #' #' Description #' #' Details1 #' #' Details2 #' #' @details Details3 #' #' Details4 foo <- function(x) {}" )[[1]] expect_equal( out$get_value("details"), "Details1\n\nDetails2\n\nDetails3\n\nDetails4" ) }) test_that("whitespace is not detected as details", { expect_silent( out <- roc_proc_text( rd_roclet(), " #' Title #' #' #' Description #' #' #' foo <- function(x) {}" )[[1]] ) expect_null(out$get_value("details")) }) test_that("@description and @details are merged", { out <- roc_proc_text( rd_roclet(), " #' Foo #' #' This. #' #' OBTW. foo <- function(x = '%') x #' @rdname foo #' @description And that. #' @details ORLY? bar <- function(y = '%') y " )[[1]] expect_equal(out$get_value("description"), c("This.", "And that.")) expect_equal(out$get_value("details"), c("OBTW.", "ORLY?")) }) test_that("empty description block is silently removed", { expect_warning( roc_proc_text( rd_roclet(), " #' #' f <- function() {} " ), NA ) }) test_that("description block preserves whitespace", { out <- parse_text( " #' Title #' #' Line 1 #' Line 2 #' #' Line 1 #' Line 2 f <- function() {} " )[[1]] expect_equal(block_get_tag_value(out, "description"), "Line 1\n Line 2") expect_equal(block_get_tag_value(out, "details"), "Line 1\n Line 2") }) test_that("line numbers offset correctly", { out <- parse_text( "#' Title #' #' Line 3 #' Line 4 #' Line 5 #' #' Line 7 #' Line 8 f <- function() {} " )[[1]] expect_equal(out$tags[[1]]$line, 1) expect_equal(out$tags[[2]]$line, 3) expect_equal(out$tags[[3]]$line, 7) }) test_that("even with explicit title/description", { out <- parse_text( "#' Line 1 #' Line 2 #' Line 3 #' #' Line 5 #' Line 6 #' @title This is a title f <- function() {} " )[[1]] expect_equal(out$tags[[1]]$line, 1) expect_equal(out$tags[[2]]$line, 5) expect_equal(out$tags[[3]]$line, 7) }) # evaluate ---------------------------------------------------------------- test_that("evaluation occurs during parsing", { out <- roc_proc_text( rd_roclet(), " foo <- function() c('@name a', '@title a') #' @eval foo() NULL" )[[1]] expect_equal(out$get_value("title"), "a") expect_equal(out$filename, "a.Rd") }) test_that("errors are propagated", { block <- " foo <- function() stop('Uhoh') #' Title #' @name foo #' @eval foo() NULL " expect_snapshot(. <- roc_proc_text(rd_roclet(), block)) }) test_that("must return non-NA string", { block <- " foo <- function() NA #' @eval foo() NULL " expect_snapshot(. <- roc_proc_text(rd_roclet(), block)) block <- " foo <- function() NA_character_ #' @eval foo() NULL " expect_snapshot(. <- roc_proc_text(rd_roclet(), block)) }) test_that("also works with namespace roclet", { out <- roc_proc_text( namespace_roclet(), " foo <- function() '@export a' #' @eval foo() #' @name a #' @title a NULL" ) expect_equal(out, "export(a)") }) # other ------------------------------------------------------------------- test_that("warns about duplicate tags", { block <- " #' Foo #' @rdname foo #' @rdname bar foo <- function() {} " expect_snapshot(. <- roc_proc_text(rd_roclet(), block)) }) roxygen2/tests/testthat/test-object-r6.R0000644000176200001440000000727615156257756017763 0ustar liggesuserstest_that("extract_r6_data without source refs", { txt <- "R6::R6Class('foo', public = list( field1 = NULL, meth1 = function(Z) { }, meth2 = function(Z = 10, ...) { }, field2 = \"foobar\", meth3 = function() { } ) )" C <- eval(parse(text = txt, keep.source = FALSE)) expect_snapshot(extract_r6_data(C), error = TRUE) }) test_that("extract_r6_methods", { txt <- "R6::R6Class( public = list( field1 = NULL, meth1 = function(Z) { }, meth2 = function(Z = 10, ...) { }, field2 = \"foobar\", meth3 = function() { } ) )" C <- eval(parse(text = txt, keep.source = TRUE)) M <- extract_r6_methods(C) expect_equal(M$type, rep("method", 4)) expect_equal(M$name, c(paste0("meth", 1:3), "clone")) expect_equal(M$line, c(4L, 5L, 7L, NA_integer_)) expect_equal( M$formals, I(list( as.pairlist(alist(Z = )), as.pairlist(alist(Z = 10, ... = )), NULL, as.pairlist(alist(deep = FALSE)) )) ) }) test_that("extract_r6_super_data", { eval(parse(test_path("testR6/R/classes.R"), keep.source = TRUE)) D <- extract_r6_super_data(C) mypkg <- environmentName(topenv()) expect_equal(D$classes$package, rep(mypkg, 2)) expect_equal(D$classes$classname, c("B", "A")) expect_equal(D$members$package, rep(mypkg, 18)) expect_equal(D$members$classname, rep(c("B", "A"), c(8, 10))) expect_equal( D$members$type, c( rep("method", 3), rep("field", 2), rep("active", 3), rep("method", 4), rep("field", 3), rep("active", 3) ) ) expect_equal( D$members$name, c( "meth4", "meth1", "clone", "field4", "field1", "active5", "active4", "active1", "meth3", "meth2", "meth1", "clone", "field3", "field2", "field1", "active3", "active2", "active1" ) ) }) test_that("extract_r6_fields", { C <- R6::R6Class( public = list( field1 = NULL, meth1 = function() {}, field2 = "foobar" ) ) F <- extract_r6_fields(C) expect_equal(F$type, rep("field", 2)) expect_equal(F$name, c("field1", "field2")) C <- R6::R6Class( public = list( meth1 = function() {} ) ) F <- extract_r6_fields(C) expect_s3_class(F, "data.frame") expect_equal(F$type, character()) expect_equal(F$name, character()) C <- R6::R6Class() F <- extract_r6_fields(C) expect_s3_class(F, "data.frame") expect_equal(F$type, character()) expect_equal(F$name, character()) }) test_that("extract_r6_bindings", { C <- R6::R6Class( active = list( bind1 = function(x) {}, bind2 = function(x) {} ), public = list( meth1 = function() {} ) ) F <- extract_r6_bindings(C) expect_equal(F$type, rep("active", 2)) expect_equal(F$name, c("bind1", "bind2")) C <- R6::R6Class( public = list( meth1 = function() {} ) ) F <- extract_r6_bindings(C) expect_s3_class(F, "data.frame") expect_equal(F$type, character()) expect_equal(F$name, character()) C <- R6::R6Class() F <- extract_r6_bindings(C) expect_s3_class(F, "data.frame") expect_equal(F$type, character()) expect_equal(F$name, character()) }) test_that("can disable R6 handling", { text <- " #' Class C <- R6::R6Class( public = list( field = NULL, #' @description Method desc. #' @param arg Method arg. meth = function(arg) { } ) )" local_roxy_meta_set("r6", FALSE) out <- roc_proc_text(rd_roclet(), text)[[1]] rd <- format(out) expect_false(grepl("section{Methods}", rd, fixed = TRUE)) expect_true(grepl("arguments{", rd, fixed = TRUE)) }) roxygen2/tests/testthat/test-rd-r6-super.R0000644000176200001440000000211715156520264020226 0ustar liggesuserstest_that("can extract superclasses", { text <- " C1 <- R6::R6Class('C1', cloneable = FALSE) #' Class C2 <- R6::R6Class('C2', inherit = C1, public = list( #' @description method1 meth1 = function() 1 ) )" docs <- r6_doc(text) expect_equal( docs$superclasses, rd_r6_super( "C2", package = "R_GlobalEnv", classname = "C1", has_topic = FALSE ) ) }) test_that("format.rd_r6_super with one superclass", { supers <- rd_r6_super( "Child", package = "mypkg", classname = "Parent", has_topic = FALSE ) expect_snapshot(cat(format(supers), sep = "\n")) }) test_that("format.rd_r6_super omits pkg:: for same-package classes", { local_roxy_meta_set("current_package", "mypkg") supers <- rd_r6_super( "Child", package = c("mypkg", "otherpkg"), classname = c("Parent", "GrandParent"), has_topic = c(FALSE, FALSE) ) expect_snapshot(cat(format(supers), sep = "\n")) }) test_that("format.rd_r6_super returns nothing when empty", { expect_null(format(rd_r6_super("Foo"))) }) roxygen2/tests/testthat/test-markdown-link.R0000644000176200001440000003730515166171276020732 0ustar liggesuserstest_that("proper link references are added", { cases <- list( c("foo [func()] bar", "[func()]: R:func()"), c("foo [obj] bar", "[obj]: R:obj"), c("foo [text][func()] bar", "[func()]: R:func()"), c("foo [text][obj] bar", "[obj]: R:obj"), c("foo [pkg::func()] bar", "[pkg::func()]: R:pkg::func()"), c("foo [pkg::obj] bar", "[pkg::obj]: R:pkg::obj"), c("foo [text][pkg::func()] bar", "[pkg::func()]: R:pkg::func()"), c("foo [text][pkg::obj] bar", "[pkg::obj]: R:pkg::obj"), c("foo [linktos4-class] bar", "[linktos4-class]: R:linktos4-class"), c("foo [pkg::s4-class] bar", "[pkg::s4-class]: R:pkg::s4-class") ) for (i in seq_along(cases)) { expect_match( add_linkrefs_to_md(cases[[i]][1]), cases[[i]][2], fixed = TRUE ) } }) test_that("can not have [ inside of link", { expect_equal( markdown("`[[`. [subset()]"), r"(\code{[[}. \code{\link[=subset]{subset()}})" ) }) test_that("can escape [ to avoid spurious links", { expect_equal( markdown("\\[test\\]"), "[test]" ) expect_equal( markdown("\\[ [test] \\]"), r"([ \link{test} ])" ) }) test_that("\\Sexpr with options not converted to links", { expect_equal( markdown(r"(\Sexpr[results=rd]{runif(1)})"), r"(\Sexpr[results=rd]{runif(1)})" ) }) test_that("% in links are escaped", { expect_equal(markdown("[x][%%]"), r"(\link[=\%\%]{x})") expect_equal(markdown("[%][x]"), r"(\link[=x]{\%})") expect_equal(markdown("[%%]"), r"(\link{\%\%})") expect_equal(markdown("[base::%%]"), r"(\link[base:\%\%]{base::\%\%})") # %in% can be resolved to base package (#1728) expect_equal(markdown("[%in%]"), r"(\link{\%in\%})") }) test_that("links to topic, not filename", { expect_equal( markdown("[tools::CRAN_package_db]"), r"(\link[tools:CRAN_package_db]{tools::CRAN_package_db})" ) }) test_that("{ and } in links are escaped (#1259)", { expect_equal( markdown("[`foo({ bar })`][x]"), r"(\code{\link[=x]{foo(\{ bar \})}})" ) expect_equal(markdown("[`{{`][x]"), r"(\code{\link[=x]{\{\{}})") # Non code parts are not escaped (invalid Rd) expect_equal(markdown("[foo({ bar })][x]"), r"(\link[=x]{foo({ bar })})") }) test_that("non-text nodes in links are supported", { expect_equal(markdown("[`foo` bar][x]"), r"(\link[=x]{\code{foo} bar})") expect_equal(markdown("[__baz__][x]"), r"(\link[=x]{\strong{baz}})") expect_equal(markdown("[_italic_][x]"), r"(\link[=x]{\emph{italic}})") }) test_that("commonmark picks up the various link references", { cases <- list( list("foo [func()] bar", c("R:func()", "func()")), list("foo [obj] bar", c("R:obj", "obj")), list("foo [text][func()] bar", c("R:func()", "text")), list("foo [text][obj] bar", c("R:obj", "text")), list("foo [pkg::func()] bar", c("R:pkg::func()", "pkg::func()")), list("foo [pkg::obj] bar", c("R:pkg::obj", "pkg::obj")), list("foo [text][pkg::func()] bar", c("R:pkg::func()", "text")), list("foo [text][pkg::obj] bar", c("R:pkg::obj", "text")), list("foo [linktos4-cl] bar", c("R:linktos4-cl", "linktos4-cl")), list("foo [pkg::s4-cl] bar", c("R:pkg::s4-cl", "pkg::s4-cl")) ) for (i in seq_along(cases)) { x <- commonmark::markdown_xml(add_linkrefs_to_md(cases[[i]][[1]])) xdoc <- xml2::xml_ns_strip(xml2::read_xml(x)) link <- xml2::xml_find_first(xdoc, "//link") expect_equal(xml2::xml_attr(link, "destination"), cases[[i]][[2]][1]) text <- xml2::xml_find_first(link, "./text") expect_equal(xml2::xml_text(text), cases[[i]][[2]][2], info = i) } }) test_that("short and sweet links work", { out1 <- roc_proc_text( rd_roclet(), " #' Title #' #' Description, see [function()]. #' And also [object]. #' @md foo <- function() {}" )[[1]] out2 <- roc_proc_text( rd_roclet(), " #' Title #' #' Description, see \\code{\\link[=function]{function()}}. #' And also \\link{object}. foo <- function() {}" )[[1]] expect_equivalent_rd(out1, out2) expect_snapshot( out1 <- roc_proc_text( rd_roclet(), " #' Title #' #' See [11pkg::function()], [11pkg::object]. #' @md foo <- function() {}" )[[1]] ) out2 <- roc_proc_text( rd_roclet(), " #' Title #' #' See \\code{\\link[11pkg:function]{11pkg::function()}}, \\link[11pkg:object]{11pkg::object}. foo <- function() {}" )[[1]] expect_equivalent_rd(out1, out2) out1 <- roc_proc_text( rd_roclet(), " #' Title #' #' Description, see [name][dest]. #' @md foo <- function() {}" )[[1]] out2 <- roc_proc_text( rd_roclet(), " #' Title #' #' Description, see \\link[=dest]{name}. foo <- function() {}" )[[1]] expect_equivalent_rd(out1, out2) block <- " #' Title #' #' Description, see [name words][cli::bar111]. #' @md foo <- function() {} " expect_snapshot(out1 <- roc_proc_text(rd_roclet(), block)[[1]]) out2 <- roc_proc_text( rd_roclet(), " #' Title #' #' Description, see \\link[cli:bar111]{name words}. foo <- function() {}" )[[1]] expect_equivalent_rd(out1, out2) out1 <- roc_proc_text( rd_roclet(), " #' Title #' #' Description, see [terms][terms.object]. #' @md foo <- function() {}" )[[1]] out2 <- roc_proc_text( rd_roclet(), " #' Title #' #' Description, see \\link[=terms.object]{terms}. foo <- function() {}" )[[1]] expect_equivalent_rd(out1, out2) out1 <- roc_proc_text( rd_roclet(), " #' Title #' #' Description, see [abc][abc-class]. #' @md foo <- function() {}" )[[1]] out2 <- roc_proc_text( rd_roclet(), " #' Title #' #' Description, see \\link[=abc-class]{abc}. foo <- function() {}" )[[1]] expect_equivalent_rd(out1, out2) out1 <- roc_proc_text( rd_roclet(), " #' Title. #' #' In another package: [and this one][desc::desc]. #' [name words][desc::desc]. #' #' @md #' @name markdown-test foo <- function() {}" )[[1]] out2 <- roc_proc_text( rd_roclet(), " #' Title. #' #' In another package: \\link[desc:desc]{and this one}. #' \\link[desc:desc]{name words}. #' #' @name markdown-test foo <- function() {}" )[[1]] expect_equivalent_rd(out1, out2) }) test_that("a weird markdown link bug is fixed", { out1 <- roc_proc_text( rd_roclet(), " #' Dummy page to test roxygen's markdown formatting #' #' Links are very tricky, so I'll put in some links here: #' Link to a function: [roxygenize()]. #' Link to an object: [roxygenize] (we just treat it like an object here). #' #' Link to another package, function: [desc::desc()]. #' Link to another package, non-function: [desc::desc]. #' #' Link with link text: [this great function][roxygenize()], #' [`roxygenize`][roxygenize()], or [that great function][roxygenize]. #' #' In another package: [and this one][desc::desc]. #' #' @md #' @name markdown-test #' @keywords internal NULL" )[[1]] out2 <- roc_proc_text( rd_roclet(), " #' Dummy page to test roxygen's markdown formatting #' #' Links are very tricky, so I'll put in some links here: #' Link to a function: \\code{\\link[=roxygenize]{roxygenize()}}. #' Link to an object: \\link{roxygenize} (we just treat it like an object here). #' #' Link to another package, function: \\code{\\link[desc:desc]{desc::desc()}}. #' Link to another package, non-function: \\link[desc:desc]{desc::desc}. #' #' Link with link text: \\link[=roxygenize]{this great function}, #' \\code{\\link{roxygenize}}, or \\link[=roxygenize]{that great function}. #' #' In another package: \\link[desc:desc]{and this one}. #' #' @name markdown-test #' @keywords internal NULL" )[[1]] expect_equivalent_rd(out1, out2) }) test_that("another markdown link bug is fixed", { out1 <- roc_proc_text( rd_roclet(), " #' Title #' #' Description, see [escape_rd_for_md()]. #' #' And also [object]. #' @md foo <- function() {}" )[[1]] out2 <- roc_proc_text( rd_roclet(), " #' Title #' #' Description, see \\code{\\link[=escape_rd_for_md]{escape_rd_for_md()}}. #' #' And also \\link{object}. foo <- function() {}" )[[1]] expect_equivalent_rd(out1, out2) }) test_that("markdown code as link text is rendered as code", { suppressMessages( out1 <- roc_proc_text( rd_roclet(), " #' Title #' #' Description, see [`name`][dest], #' [`function`][function()], #' [`filter`][stats::filter()], #' [`bar`][pkg::bar], #' [`terms`][terms.object], #' [`abc`][abc-class]. #' @md foo <- function() {}" )[[1]] ) out2 <- roc_proc_text( rd_roclet(), " #' Title #' #' Description, see \\code{\\link[=dest]{name}}, #' \\code{\\link{function}}, #' \\code{\\link[stats:filter]{filter}}, #' \\code{\\link[pkg:bar]{bar}}, #' \\code{\\link[=terms.object]{terms}}, #' \\code{\\link[=abc-class]{abc}}. foo <- function() {}" )[[1]] expect_equivalent_rd(out1, out2) }) test_that("non-code link in backticks works", { out1 <- roc_proc_text( rd_roclet(), " #' Title #' #' Description, see [`foobar`]. #' Also [`this_too`]. #' @md foo <- function() {}" )[[1]] out2 <- roc_proc_text( rd_roclet(), " #' Title #' #' Description, see \\code{\\link{foobar}}. #' Also \\code{\\link{this_too}}. foo <- function() {}" )[[1]] expect_equivalent_rd(out1, out2) }) test_that("[] is not picked up in code", { out1 <- roc_proc_text( rd_roclet(), " #' Title #' #' @param connect_args `[named list]`\\cr Connection arguments #' Description, see `[foobar]`. #' Also `[this_too]`. #' @md foo <- function() {}" )[[1]] out2 <- roc_proc_text( rd_roclet(), " #' Title #' #' @param connect_args \\verb{[named list]}\\cr Connection arguments #' Description, see \\verb{[foobar]}. #' Also \\verb{[this_too]}. foo <- function() {}" )[[1]] expect_equivalent_rd(out1, out2) }) test_that("[]() links are still fine", { out1 <- roc_proc_text( rd_roclet(), " #' Title #' #' Description, see [some thing](http://www.someurl.com). #' @md foo <- function() {}" )[[1]] out2 <- roc_proc_text( rd_roclet(), " #' Title #' #' Description, see \\href{http://www.someurl.com}{some thing}. foo <- function() {}" )[[1]] expect_equivalent_rd(out1, out2) out1 <- roc_proc_text( rd_roclet(), " #' Title #' #' Link #' text [broken #' across lines](http://www.someurl.com) preserve #' whitespace, even when #' [broken across #' several #' lines](http://www.someurl.com), #' or with varying #' [amounts \ #' of \ #' interspersed \ #' whitespace](http://www.someurl.com). #' @md foo <- function() {}" )[[1]] out2 <- roc_proc_text( rd_roclet(), " #' Title #' #' Link #' text \\href{http://www.someurl.com}{broken across lines} preserve #' whitespace, even when #' \\href{http://www.someurl.com}{broken across several lines}, #' or with varying #' \\href{http://www.someurl.com}{amounts of interspersed whitespace}. foo <- function() {}" )[[1]] expect_equivalent_rd(out1, out2) }) test_that("links to S4 classes are OK", { out1 <- roc_proc_text( rd_roclet(), " #' Title #' #' Description, see [linktos4-class] as well. #' @md foo <- function() {}" )[[1]] out2 <- roc_proc_text( rd_roclet(), " #' Title #' #' Description, see \\link[=linktos4-class]{linktos4} as well. foo <- function() {}" )[[1]] expect_equivalent_rd(out1, out2) suppressMessages( out1 <- roc_proc_text( rd_roclet(), " #' Title #' #' Description, see [pkg::linktos4-class] as well. #' @md foo <- function() {}" )[[1]] ) out2 <- roc_proc_text( rd_roclet(), " #' Title #' #' Description, see \\link[pkg:linktos4-class]{pkg::linktos4} as well. foo <- function() {}" )[[1]] expect_equivalent_rd(out1, out2) }) test_that("linebreak in 'text' of [text][foo] turns into single space", { out1 <- roc_proc_text( rd_roclet(), " #' Title #' #' Link #' text [broken #' across lines][fcn] preserve #' whitespace, even when #' [broken across #' several #' lines][fcn], #' or with varying #' [amounts \ #' of \ #' interspersed \ #' whitespace][fcn]. #' @md foo <- function() {}" )[[1]] out2 <- roc_proc_text( rd_roclet(), " #' Title #' #' Link #' text \\link[=fcn]{broken across lines} preserve #' whitespace, even when #' \\link[=fcn]{broken across several lines}, #' or with varying #' \\link[=fcn]{amounts of interspersed whitespace}. foo <- function() {}" )[[1]] expect_equivalent_rd(out1, out2) }) test_that("markup in link text", { out1 <- roc_proc_text( rd_roclet(), " #' Title #' #' Description, see [`code link text`][func]. #' And also [`code as well`](https://external.com). #' @md foo <- function() {}" )[[1]] out2 <- roc_proc_text( rd_roclet(), " #' Title #' #' Description, see \\code{\\link[=func]{code link text}}. #' And also \\href{https://external.com}{\\verb{code as well}}. foo <- function() {}" )[[1]] expect_equivalent_rd(out1, out2) }) test_that("non-code markup in link text", { out1 <- roc_proc_text( rd_roclet(), " #' Title #' #' See [*italic text*][func] and [**bold text**][func2]. #' @md foo <- function() {}" )[[1]] out2 <- roc_proc_text( rd_roclet(), " #' Title #' #' See \\link[=func]{\\emph{italic text}} and \\link[=func2]{\\strong{bold text}}. foo <- function() {}" )[[1]] expect_equivalent_rd(out1, out2) }) test_that("linking to self is unqualified", { local_roxy_meta_set("current_package", "myself") rd <- markdown("foo [myself::fun()] and [myself::obj] bar") expect_equal( rd, r"(foo \code{\link[=fun]{fun()}} and \link{obj} bar)" ) }) test_that("resolved links don't change link text (#1662)", { local_roxy_meta_set("current_package", "testMdLinks") local_roxy_meta_set("current_package_dir", test_path("testMdLinks")) expect_equal( markdown("[cli_abort()]"), r"(\code{\link[cli:cli_abort]{cli_abort()}})" ) }) test_that("percents are escaped in link targets", { out1 <- roc_proc_text( rd_roclet(), " #' Title #' #' [link % text](https://foo.bar/link%20target) #' @md foo <- function() {}" )[[1]] out2 <- roc_proc_text( rd_roclet(), " #' Title #' #' \\href{https://foo.bar/link%20target}{link % text} #' @md foo <- function() {}" )[[1]] expect_equivalent_rd(out1, out2) }) # Helpers ---------------------------------------------------------------------- test_that("generates informative warnings", { expect_snapshot({ tag <- roxy_test_tag() check_topic("11papaya", "foo", tag) check_topic("cli", "foofofofoo", tag) }) }) test_that("rd_link() builds correct Rd links", { expect_equal(rd_link(NA, "topic", "topic"), "\\link{topic}") expect_equal(rd_link("pkg", "topic", "text"), "\\link[pkg:topic]{text}") expect_equal(rd_link(NA, "topic", "text"), "\\link[=topic]{text}") }) test_that("fun_suffix() adds () only to non-infix functions", { env <- env(f = function() NULL, x = 1, `%op%` = function(a, b) NULL) expect_equal(fun_suffix("f", env), "f()") expect_equal(fun_suffix("x", env), "x") expect_equal(fun_suffix("%op%", env), "%op%") expect_equal(fun_suffix("missing", env), "missing") }) roxygen2/tests/testthat/up-to-date/0000755000176200001440000000000015160334463017015 5ustar liggesusersroxygen2/tests/testthat/up-to-date/R/0000755000176200001440000000000015154546447017230 5ustar liggesusersroxygen2/tests/testthat/up-to-date/R/foo.R0000644000176200001440000000005115154546447020132 0ustar liggesusers#' Title #' @export foo <- function() {} roxygen2/tests/testthat/up-to-date/DESCRIPTION0000644000176200001440000000007415154546447020536 0ustar liggesusersPackage: testpkg Title: Test Version: 0.0.1 Encoding: UTF-8 roxygen2/tests/testthat/templates/0000755000176200001440000000000015174650071017035 5ustar liggesusersroxygen2/tests/testthat/templates/man/0000755000176200001440000000000015175154672017617 5ustar liggesusersroxygen2/tests/testthat/templates/man/roxygen/0000755000176200001440000000000015174650071021303 5ustar liggesusersroxygen2/tests/testthat/templates/man/roxygen/templates/0000755000176200001440000000000013540202201023260 5ustar liggesusersroxygen2/tests/testthat/templates/man/roxygen/templates/new-path.R0000644000176200001440000000005013540202201025121 0ustar liggesusers#' <%= x %> #' @param <%= y %> <%= z %> roxygen2/tests/testthat/templates/man-roxygen/0000755000176200001440000000000015174650071021301 5ustar liggesusersroxygen2/tests/testthat/templates/man-roxygen/lcase.r0000644000176200001440000000000014520713066022537 0ustar liggesusersroxygen2/tests/testthat/templates/man-roxygen/UCase.R0000644000176200001440000000000013540202201022371 0ustar liggesusersroxygen2/tests/testthat/test-load.R0000644000176200001440000000240215154546446017064 0ustar liggesuserstest_that("load_installed retrieves installed package", { skip_if_not(file.exists(test_path("../../DESCRIPTION"))) env <- load_installed(test_path("../..")) expect_identical(env, asNamespace("roxygen2")) }) test_that("can load simple package with load_pkgload()", { suppressMessages(env <- load_pkgload(test_path("testRbuildignore"))) expect_equal(env_get(env, "a"), 1) }) test_that("can load simple package with load_source()", { env <- load_source(test_path("testRbuildignore")) expect_equal(env_get(env, "a"), 1) }) # find_load_strategy ------------------------------------------------------ test_that("function returned as is", { expect_equal(find_load_strategy(load_installed), load_installed) }) test_that("look up string", { expect_equal(find_load_strategy("installed"), load_installed) expect_equal(find_load_strategy("pkgload"), load_pkgload) expect_equal(find_load_strategy("source"), load_source) expect_snapshot(find_load_strategy("blahblahb"), error = TRUE) }) test_that("NULL uses option", { expect_equal(find_load_strategy(NULL, "installed"), load_installed) }) test_that("informative errors for bad inputs", { expect_snapshot( { find_load_strategy(1) find_load_strategy(NULL, list()) }, error = TRUE ) }) roxygen2/tests/testthat/test-rd-r6-methods.R0000644000176200001440000000417315165177023020540 0ustar liggesuserstest_that("format.rd_r6_methods with one method", { methods <- rd_r6_methods( "Foo", self = list( rd_r6_method( name = "run", class = "Foo", formals = NULL, description = "Run it." ) ) ) expect_snapshot(cat(format(methods), sep = "\n")) }) test_that("format.rd_r6_methods returns nothing when empty", { expect_null(format(rd_r6_methods("Foo"))) }) test_that("r6_all_examples aggregates across methods", { text <- " #' Class C <- R6::R6Class('C', cloneable = FALSE, public = list( #' @description Greet. #' @examples c$greet() greet = function() 'hi', #' @description Run. run = function() 1, #' @description Stop. #' @param force Force it #' @examples c$stop() #' @examples c$stop(force = TRUE) stop = function(force = FALSE) NULL ) )" docs <- r6_doc(text) expect_snapshot(cat(r6_all_examples(docs$methods), sep = "\n")) }) test_that("@noRd suppresses entire class documentation", { text <- " #' Class #' @noRd #' @field field1 Foo. C <- R6::R6Class( public = list( field1 = NULL, field2 = 'bar' ) )" expect_silent(out <- roc_proc_text(rd_roclet(), text)) expect_equal(out, list()) }) test_that("@noRd suppresses method documentation", { text <- " #' Class C <- R6::R6Class('C', cloneable = FALSE, public = list( #' @description Greet. greet = function() 'hi', #' @noRd initialize = function(x) NULL ) )" expect_silent(docs <- r6_doc(text)) nms <- map_chr(docs$methods$self, \(m) m$name) expect_equal(nms, "greet") }) test_that("r6_flatten_sections collapses markdown sections", { local_markdown() tag <- roxy_tag("details", "Some details.\n\n# A heading\n\nBody.") tag <- tag_markdown_with_sections(tag) expect_length(tag$val, 2) tag <- r6_flatten_sections(tag) expect_length(tag$val, 1) expect_match(tag$val, "Some details.", fixed = TRUE) expect_match(tag$val, "\\subsection{A heading}", fixed = TRUE) expect_match(tag$val, "Body.", fixed = TRUE) }) roxygen2/tests/testthat/testNamespace/0000755000176200001440000000000015160334463017632 5ustar liggesusersroxygen2/tests/testthat/testNamespace/R/0000755000176200001440000000000014520730024020023 5ustar liggesusersroxygen2/tests/testthat/testNamespace/R/a.R0000644000176200001440000000012014520730024020357 0ustar liggesusers#' @export f <- function(x) x #' @evalNamespace "export(g)" g <- function() 1 roxygen2/tests/testthat/testNamespace/NAMESPACE0000644000176200001440000000010214224311554021036 0ustar liggesusers# Generated by roxygen2: do not edit by hand export(f) export(g) roxygen2/tests/testthat/testNamespace/DESCRIPTION0000644000176200001440000000035014262717326021343 0ustar liggesusersPackage: testNamespace Title: Check that utf8 escapes are round tripped ok License: GPL-2 Description: Author: Hadley Maintainer: Hadley Encoding: UTF-8 Version: 0.1 RoxygenNote: 7.1.2.9000 roxygen2/tests/testthat/escapes.Rd0000644000176200001440000000010514225272441016742 0ustar liggesusers\name{x} \alias{x} \title{title} \description{ % Comment \code{\\} } roxygen2/tests/testthat/Rd-example-3.R0000644000176200001440000000002013523072405017303 0ustar liggesusersx1 <- 1 x2 <- 2 roxygen2/tests/testthat/roxygen-example-1.R0000644000176200001440000000071113523072405020436 0ustar liggesusers##' Title ##' ##' Description ##' ##' Details ##' ##' @param x,y,z Descriptions for x, y, z ##' @param a Description of a ##' @param b ##' Description of b ##' @section Important: ##' Don't run with scissors! ##' @export NULL ##' Title ##' ##' Description ##' ##' Details ##' ##' @param x,y,z Descriptions for x, y, z ##' @param a Description of a ##' @param b ##' Description of b ##' @section Important: ##' Don't run with scissors! ##' @export NULL roxygen2/tests/testthat/example.Rmd0000644000176200001440000000007615165447100017136 0ustar liggesusers```{r} # comment this <- 10 is <- this + 10 good <- this + is roxygen2/tests/testthat/test-parse.R0000644000176200001440000000057215151411610017242 0ustar liggesuserstest_that("can control processing order with @order", { out <- roc_proc_text( rd_roclet(), " #' @rdname bar #' @details 2 #' @order 2 foo <- function() {} #' @rdname bar #' @details 1 #' @order 1 bar <- function() {} #' @title Title #' @name bar NULL " )[[1]] expect_equal(out$get_value("details"), c("1", "2")) }) roxygen2/tests/testthat/test-utils-io.R0000644000176200001440000000250414262717326017711 0ustar liggesuserstest_that("write_lines uses specified line_ending", { win_nl <- withr::local_tempfile() write_lines("baz", win_nl, line_ending = "\r\n") expect_equal(readChar(win_nl, 100), "baz\r\n") unix_nl <- withr::local_tempfile() write_lines("baz", unix_nl, line_ending = "\n") expect_equal(readChar(unix_nl, 100), "baz\n") }) test_that("detect_line_ending captures existing line ending", { win_nl <- withr::local_tempfile() write_lines("baz", win_nl, line_ending = "\r\n") expect_equal(detect_line_ending(win_nl), "\r\n") unix_nl <- withr::local_tempfile() write_lines("baz", unix_nl, line_ending = "\n") expect_equal(detect_line_ending(unix_nl), "\n") }) test_that("detect_line_ending defaults to unix", { non_existent_file <- withr::local_tempfile() expect_equal(detect_line_ending(non_existent_file), "\n") empty_file <- withr::local_tempfile() file.create(empty_file) expect_equal(detect_line_ending(empty_file), "\n") }) test_that("write_lines preserves existing line ending", { win_nl <- withr::local_tempfile() write_lines("baz", win_nl, line_ending = "\r\n") write_lines("baz", win_nl) expect_equal(readChar(win_nl, 100), "baz\r\n") unix_nl <- withr::local_tempfile() write_lines("baz", unix_nl, line_ending = "\n") write_lines("baz", unix_nl) expect_equal(readChar(unix_nl, 100), "baz\n") }) roxygen2/tests/testthat/test-rd-markdown.R0000644000176200001440000000323115151411610020350 0ustar liggesuserstest_that("generic keys produce expected output", { out <- roc_proc_text( rd_roclet(), " #' @title a #' @note test #' @author test #' @seealso test #' @references test #' @name a NULL" )[[1]] expect_equal(out$get_value("references"), "test") expect_equal(out$get_value("note"), "test") expect_equal(out$get_value("seealso"), "test") expect_equal(out$get_value("author"), "test") }) test_that("author duplicated get removed", { out <- roc_proc_text( rd_roclet(), " #' @name a #' @title a #' @author A NULL #' @name b #' @rdname a #' @author A NULL #' @name c #' @rdname a #' @author B NULL" )[[1]] expect_equal(out$get_value("author"), c("A", "B")) }) test_that("@format overrides defaults", { out <- roc_proc_text( rd_roclet(), " #' Title #' #' @format abc #' x <- list(a = 1, b = 2)" )[[1]] expect_equal(out$get_value("format"), "abc") }) test_that("@format NULL suppresses default usage", { out <- roc_proc_text( rd_roclet(), " #' Title #' #' @format NULL x <- list(a = 1, b = 2)" )[[1]] expect_equal(out$get_value("format"), NULL) }) test_that("@format not escaped", { out <- roc_proc_text( rd_roclet(), " #' Title #' @format % x <- list(a = 1, b = 2)" )[[1]] expect_equal(out$get_value("format"), "%") expect_equal(out$get_rd("format"), "\\format{\n%\n}") }) test_that("@title warns about multiple paragraphs", { block <- " #' @title Paragraph 1 #' #' Paragraph 2 x <- list(a = 1, b = 2) " expect_snapshot(. <- roc_proc_text(rd_roclet(), block)) }) roxygen2/tests/testthat/test-utils-rd.R0000644000176200001440000000034514262717326017710 0ustar liggesuserstest_that("has_topic() works as you'd expect", { expect_equal(has_topic("abbreviate", "base"), TRUE) expect_equal(has_topic("abbreviateXXXX", "base"), FALSE) expect_equal(has_topic("foo", "PACKAGEDOESNOTEXIST"), FALSE) }) roxygen2/tests/testthat/testEagerData/0000755000176200001440000000000014520730024017543 5ustar liggesusersroxygen2/tests/testthat/testEagerData/R/0000755000176200001440000000000014520730024017744 5ustar liggesusersroxygen2/tests/testthat/testEagerData/R/a.R0000644000176200001440000000002714520730024020306 0ustar liggesusers#' Data #' #' Desc "a" roxygen2/tests/testthat/testEagerData/data/0000755000176200001440000000000013523072405020460 5ustar liggesusersroxygen2/tests/testthat/testEagerData/data/a.rda0000644000176200001440000000011513523072405021365 0ustar liggesusersBZh91AY&SY C1D@  !2B`3(AܩA<^fV"(H_^!roxygen2/tests/testthat/testEagerData/DESCRIPTION0000644000176200001440000000031413523072405021253 0ustar liggesusersPackage: testEagerData Title: Tools to make developing R code easier License: GPL-2 Description: Author: Hadley Maintainer: Hadley Version: 0.1 Encoding: UTF-8 roxygen2/tests/testthat/test-rd-backref.R0000644000176200001440000000100615151411610020121 0ustar liggesuserstest_that("Source reference is included as comment", { out <- roc_proc_text( rd_roclet(), " #' @name a #' @title a NULL" )[[1]] expect_match(out$get_rd("backref"), "^% Please edit documentation in ") }) test_that("Explicit @backref is included as comment", { out <- roc_proc_text( rd_roclet(), " #' @name a #' @title a #' @backref back/ref.file #' @backref root.file NULL" )[[1]] expect_equal(out$get_value("backref"), c("back/ref.file", "root.file")) }) roxygen2/tests/testthat/_snaps/0000755000176200001440000000000015172453522016322 5ustar liggesusersroxygen2/tests/testthat/_snaps/roxygenize-setup.md0000644000176200001440000000255115172444137022212 0ustar liggesusers# useful error if no DESCRIPTION Code roxygen_setup(path) Condition Error: ! `package.dir` ('') does not contain a DESCRIPTION file. # informs about initial setup Code roxygen_setup(path, cur_version = "8.0.0") Message First time using roxygen2. Upgrading automatically... i Setting Config/roxygen2/version to "8.0.0" Output [1] TRUE # warns about non UTF-8 encoding Code roxygen_setup(path, cur_version = "8.0.0") Message x roxygen2 requires "Encoding: UTF-8" i Current encoding is "latin1" Output [1] FALSE # warns if roxygen version is too new Code roxygen_setup(path, cur_version = "8.0.0") Message x Installed roxygen2 is older than the version used with this package i You have "8.0.0" but you need "10.0.0" Output [1] FALSE # informs about major changes in 7.0.0 Code roxygen_setup(path, cur_version = "8.0.0") Message -------------------------------------------------------------------------------- Changes in roxygen2 7.0.0: * `%` is now escaped automatically in Markdown mode. Please carefully check .Rd files for changes -------------------------------------------------------------------------------- i Setting Config/roxygen2/version to "8.0.0" Output [1] FALSE roxygen2/tests/testthat/_snaps/rd-describe-in.md0000644000176200001440000000550415172444136021440 0ustar liggesusers# @describeIn suggests @rdname Code . <- roc_proc_text(rd_roclet(), block) Message x :2: @describeIn requires a name and description. i Did you want @rdname instead? # Multiple @describeIn functions combined into one Code out$get_section("minidesc") Output \section{Functions}{ \itemize{ \item \code{square()}: Square a number \item \code{cube()}: Cube a number }} # multiple methods and others are combined into a generic Code out$get_section("minidesc") Output \section{Methods (by class)}{ \itemize{ \item \code{zap(numeric)}: method \item \code{zap(character)}: method }} \section{Functions}{ \itemize{ \item \code{print(qux)}: function (method for different generic) \item \code{zap_helper()}: function }} # multiple methods and others are combined into a class constructor Code out$get_section("minidesc") Output \section{Methods (by generic)}{ \itemize{ \item \code{print(foo)}: method \item \code{format(foo)}: method }} \section{Functions}{ \itemize{ \item \code{format(bar)}: function (method for different class) \item \code{is_foo()}: function }} --- Code out$get_section("minidesc") Output \section{Methods (by generic)}{ \itemize{ \item \code{print(roxygen2_baz)}: method }} \section{Functions}{ \itemize{ \item \code{format(quuz_baz)}: function (method for another class) }} # infix and replacement names get nice label Code out$get_section("minidesc") Output \section{Functions}{ \itemize{ \item \code{x \%foo\% y}: infix \item \code{foo(x) <- value}: replacement for foo }} # s4 methods get nice label Code out$get_section("minidesc") Output \section{Methods (by generic)}{ \itemize{ \item \code{m_id(foo1)}: function }} \section{Functions}{ \itemize{ \item \code{m_id()}: generic }} --- Code out$get_section("minidesc") Output \section{Methods (by class)}{ \itemize{ \item \code{bar1(x = foo2, y = foo3)}: method1 \item \code{bar1(x = foo3, y = foo2)}: method2 }} # complains about bad usage Code . <- roc_proc_text(rd_roclet(), block) Message x :6: @describeIn must be used with an object. --- Code . <- roc_proc_text(rd_roclet(), block) Message x :6: @describeIn can not be used with @name. --- Code . <- roc_proc_text(rd_roclet(), block) Message x :6: @describeIn can not be used with @rdname. roxygen2/tests/testthat/_snaps/rd.md0000644000176200001440000000250315172444140017245 0ustar liggesusers# documenting unknown function requires name Code . <- roc_proc_text(rd_roclet(), block) Message x :6: Block must have a @name. i Either document an existing object or manually specify with @name # can't set description and re-export Code out <- roc_proc_text(rd_roclet(), block) Message x :4: Block must not include a description when re-exporting a function. # documenting NA gives useful error message (#194) Code . <- roc_proc_text(rd_roclet(), block) Message x :3: Block must have a @name. i Either document an existing object or manually specify with @name # can generate nonASCII document Code roxygenise(path, roclets = "rd") Message i Loading testNonASCII Writing 'printChineseMsg.Rd' Code # Second run should be idempotent roxygenise(path, roclets = "rd") Message i Loading testNonASCII # unicode escapes are ok Code roxygenise(path, roclets = "rd") Message i Loading testUtf8Escape Writing 'a.Rd' Code # Second run should be idempotent roxygenise(path, roclets = "rd") Message i Loading testUtf8Escape # automatically deletes unused files Code roxygenise(path) Message i Loading empty Deleting 'test.Rd' roxygen2/tests/testthat/_snaps/rd-examples.md0000644000176200001440000000232615172444136021071 0ustar liggesusers# @examples and @example interleave \examples{ example <- 'example1' a <- 2 example <- 'example2' } # @example gives warning if used instead of @examples Code out <- roc_proc_text(rd_roclet(), block)[[1]] Message x :4: @example must be a single line. i Do you want @examples? # warns if path doesn't exist Code . <- roc_proc_text(rd_roclet(), block) Message x :4: @example './this-path-doesnt-exist.R' doesn't exist. # @examplesIf \examples{ \dontshow{if (foo::bar()) withAutoprint(\{ # examplesIf} maybe-run-this-code \dontshow{\}) # examplesIf} \dontshow{if (foobar()) withAutoprint(\{ # examplesIf} and-this and-that \dontshow{\}) # examplesIf} } # @examplesIf warns about unparseable condition Code out <- roc_proc_text(rd_roclet(), block) Message x :4: @examplesIf condition failed to parse. Caused by error in `parse()`: ! :2:0: unexpected end of input 1: 1 + ^ # @examplesIf warns on empty body (#1695) Code out <- roc_proc_text(rd_roclet(), block) Message x :4: @examplesIf requires example code after the condition. roxygen2/tests/testthat/_snaps/rd-markdown.md0000644000176200001440000000024215172444136021070 0ustar liggesusers# @title warns about multiple paragraphs Code . <- roc_proc_text(rd_roclet(), block) Message x :2: @title must be a single paragraph. roxygen2/tests/testthat/_snaps/rd-s4.md0000644000176200001440000000040615172444137017577 0ustar liggesusers# invalid syntax generates useful warning Code . <- roc_proc_text(rd_roclet(), block) Message x :3: @field requires two parts: a field name and a description. x :4: @slot requires two parts: a slot name and a description. roxygen2/tests/testthat/_snaps/collate.md0000644000176200001440000000113215172444135020264 0ustar liggesusers# update_collate() checks that directory exists Code update_collate("doesn't-exist") Condition Error in `update_collate()`: ! 'doesn't-exist' doesn't exist. # DESCRIPTION file is re-written only if collate changes Code update_collate(path) Message Updating collate directive in '/DESCRIPTION' Code # Second run should be idempotent update_collate(path) # drops bad collect directives Code update_collate(".") Message x a.R: unknown path in `@include foo`. Updating collate directive in './DESCRIPTION' roxygen2/tests/testthat/_snaps/select-args.md0000644000176200001440000000156215172444137021063 0ustar liggesusers# warns on invalid input Code select_args_text(c("x", "na.rm"), "-xlab:", "test") Message x In topic 'test': argument selection failed. Caused by error in `parse()`: ! :2:0: unexpected end of input 1: -xlab: ^ Output character(0) Code select_args_text(c("x", "na.rm"), "\"a\"", "test") Message x In topic 'test': argument selection failed. Caused by error in `select_check()`: ! Argument specification must evaluate to a numeric vector. Problem in `"a"`. Output character(0) Code select_args_text(c("x", "y", "z"), "-x:z", "test") Message x In topic 'test': argument selection failed. Caused by error: ! Argument specification must be all positive or all negative, not a mixture. i Problem in `-x:z`. Output character(0) roxygen2/tests/testthat/_snaps/markdown-link.md0000644000176200001440000000151315172444136021422 0ustar liggesusers# short and sweet links work Code out1 <- roc_proc_text(rd_roclet(), "\n #' Title\n #'\n #' See [11pkg::function()], [11pkg::object].\n #' @md\n foo <- function() {}")[[ 1]] Message x :4: @description refers to un-installed package 11pkg. x :4: @description refers to un-installed package 11pkg. --- Code out1 <- roc_proc_text(rd_roclet(), block)[[1]] Message x :4: @description refers to unavailable topic cli::bar111. # generates informative warnings Code tag <- roxy_test_tag() check_topic("11papaya", "foo", tag) Message x test.R:1: @test refers to un-installed package 11papaya. Code check_topic("cli", "foofofofoo", tag) Message x test.R:1: @test refers to unavailable topic cli::foofofofoo. roxygen2/tests/testthat/_snaps/namespace.md0000644000176200001440000000611715172453522020605 0ustar liggesusers# parsing warnings only fire once Code roxygenise(path) Message Writing 'NAMESPACE' i Loading testNamespace x multiline.R:1: @importFrom must be only 1 line long, not 2. i The first line is "stats median" # @exportS3Method generates fully automatically Code . <- roc_proc_text(namespace_roclet(), block) Message x :2: @exportS3Method must be used with a known S3 method. # @exportS3Method can extract class from generic Code . <- roc_proc_text(namespace_roclet(), block) Message x :2: @exportS3Method must have form package::generic. --- Code . <- roc_proc_text(namespace_roclet(), block) Message x :2: @exportS3Method must be used with a function. --- Code . <- roc_proc_text(namespace_roclet(), block) Message x :2: @exportS3Method generic ("foo") doesn't match function ("foo1.bar"). # poorly formed importFrom throws error Code . <- roc_proc_text(namespace_roclet(), block) Message x :2: @importFrom must have at least 2 words, not 1. # multiline importFrom generates warning Code . <- roc_proc_text(namespace_roclet(), block) Message x :2: @importFrom must be only 1 line long, not 2. i The first line is "test test1" # can regenerate NAMESPACE even if its broken Code update_namespace_imports(path) Message Writing 'NAMESPACE' # rawNamespace must be valid code Code . <- roc_proc_text(namespace_roclet(), block) Message x :2: @rawNamespace failed to parse. Caused by error in `parse()`: ! :2:0: unexpected end of input 1: a + ^ # evalNamespace warns for bad code Code . <- roc_proc_text(namespace_roclet(), block) Message x :2: @evalNamespace failed to parse. Caused by error in `parse()`: ! :2:0: unexpected end of input 1: a + ^ --- Code . <- roc_proc_text(namespace_roclet(), block) Message x :2: @evalNamespace failed to evaluate. Caused by error: ! Uhoh --- Code . <- roc_proc_text(namespace_roclet(), block) Message x :2: @evalNamespace must evaluate to a character vector. # invalid imports generate helpful message Code out <- roc_proc_text(namespace_roclet(), block) Message x :2: @importFrom Excluding unknown export from utils: `InvalidUtilsFunction1`. --- Code out <- roc_proc_text(namespace_roclet(), block) Message x :2: @importFrom Excluding unknown exports from utils: `InvalidUtilsFunction1` and `InvalidUtilsFunction2`. # warns if S3 method not documented Code . <- roc_proc_text(namespace_roclet(), block) Message x :5: S3 method `mean.myclass` needs @export or @exportS3Method tag. x :3: S3 method `foo.numeric` needs @export or @exportS3Method tag. --- Code . <- roc_proc_text(namespace_roclet(), block) Message x :3: S3 method `foo.{` needs @export or @exportS3Method tag. roxygen2/tests/testthat/_snaps/rd-s7.md0000644000176200001440000000165015172444137017604 0ustar liggesusers# @prop creates Additional properties section Code out$get_section("prop") Output \section{Additional properties}{ \describe{ \item{\code{@a}}{prop a} \item{\code{@b}}{prop b} }} # @prop class@name groups by class Code out$get_section("prop") Output \section{Additional properties}{ \subsection{Parent}{ \describe{ \item{\code{@x}}{prop x} } } \subsection{Child}{ \describe{ \item{\code{@y}}{prop y} } } } # @prop with mismatched braces warns and doesn't crash Code . <- roc_proc_text(rd_roclet(), text) Message x :4: @prop has mismatched braces or quotes. # @prop class@name warns on invalid spec Code . <- roc_proc_text(rd_roclet(), text) Message x :4: @prop must have form class@prop. roxygen2/tests/testthat/_snaps/object-r6.md0000644000176200001440000000054415172444136020443 0ustar liggesusers# extract_r6_data without source refs Code extract_r6_data(C) Condition Error: ! R6 class lacks source references. i If you are using the `installed` load method in 'DESCRIPTION', then try re-installing the package with option '--with-keep.source', e.g. `install.packages(..., INSTALL_OPTS = "--with-keep.source")`. roxygen2/tests/testthat/_snaps/object-from-call.md0000644000176200001440000000053715172444136021772 0ustar liggesusers# recommends use of _PACKAGE Code out <- parse_text(block)[[1]] Message x :3: `@docType "package"` is deprecated. i Please document "_PACKAGE" instead. # S7 method with unknown class type warns Code s7_class_name(42L, block) Message x test.R:1: Unknown S7 class type. Output [1] "42L" roxygen2/tests/testthat/_snaps/object-import.md0000644000176200001440000000102515172444136021421 0ustar liggesusers# exporting a call to :: produces re-exports documentation % Generated by roxygen2: do not edit by hand % Please edit documentation in ./ \docType{import} \name{reexports} \alias{reexports} \alias{auto_test} \title{Objects exported from other packages} \keyword{internal} \description{ These objects are imported from other packages. Follow the links below to see their documentation. \describe{ \item{testthat}{\code{\link[testthat:auto_test]{auto_test()}}} }} roxygen2/tests/testthat/_snaps/topics.md0000644000176200001440000000030115172444137020141 0ustar liggesusers# missing title generates useful message Code roc_proc_text(rd_roclet(), block) Message x In topic 'foo.Rd': Skipping; no name and/or title. Output named list() roxygen2/tests/testthat/_snaps/utils-warn.md0000644000176200001440000000024715172444137020756 0ustar liggesusers# warn_roxy_function works when fun doesn't have a srcref Code warn_roxy_function(fun, "test message") Message x :0: test message. roxygen2/tests/testthat/_snaps/block.md0000644000176200001440000000221215172444135017733 0ustar liggesusers# has thoughtful print method Code parse_text(text)[[1]] Output [:5] $tag [line: 1] @title 'This is a title' {parsed} [line: 3] @param 'x,y A number' {parsed} [line: 4] @export '' {parsed} [line: 5] @usage '' {parsed} [line: 5] @.formals '' {parsed} [line: 5] @backref '' {parsed} $call f <- function(x, y) x + y $object $topic f $alias f # errors are propagated Code . <- roc_proc_text(rd_roclet(), block) Message x :5: @eval failed to evaluate. Caused by error in `foo()`: ! Uhoh # must return non-NA string Code . <- roc_proc_text(rd_roclet(), block) Message x :3: @eval must evaluate to a character vector. --- Code . <- roc_proc_text(rd_roclet(), block) Message x :3: @eval must not contain any missing values. # warns about duplicate tags Code . <- roc_proc_text(rd_roclet(), block) Message x :5: Block must contain only one @rdname. roxygen2/tests/testthat/_snaps/rd-r6-methods-self.md0000644000176200001440000000731115172444137022172 0ustar liggesusers# warns about multiple @return(s) tags Code docs <- r6_doc(text) Message x :3: Must use one @return(s) per R6 method. # warns about undocumented params Code docs <- r6_doc(text) Message x In topic 'C': Must use one @param for each argument. x run(x) is not documented x In topic 'C': Must use one @param for each argument. x run(y) is not documented # warns about duplicated params Code docs <- r6_doc(text) Message x :3: Must use one @param for each argument. x $run(x) is documented multiple times # format.rd_r6_method produces method subsection Code cat(format(method), sep = "\n") Output \if{html}{\out{
}} \if{html}{\out{}} \if{latex}{\out{\hypertarget{method-Person-greet}{}}} \subsection{\code{Person$greet()}}{ Say hello. \subsection{Usage}{ \if{html}{\out{
}} \preformatted{Person$greet(who, how = "nicely")} \if{html}{\out{
}} } \subsection{Arguments}{ \if{html}{\out{
}} \describe{ \item{\code{who}}{Name to greet.} \item{\code{how}}{Greeting style.} } \if{html}{\out{
}} } } # format.rd_r6_method renames initialize to new Code cat(format(method), sep = "\n") Output \if{html}{\out{
}} \if{html}{\out{}} \if{latex}{\out{\hypertarget{method-Foo-initialize}{}}} \subsection{\code{Foo$new()}}{ Create object. \subsection{Usage}{ \if{html}{\out{
}} \preformatted{Foo$new()} \if{html}{\out{
}} } } # format.rd_r6_method includes optional sections Code cat(format(method), sep = "\n") Output \if{html}{\out{
}} \if{html}{\out{}} \if{latex}{\out{\hypertarget{method-Job-run}{}}} \subsection{\code{Job$run()}}{ Run the job. \subsection{Usage}{ \if{html}{\out{
}} \preformatted{Job$run()} \if{html}{\out{
}} } \subsection{Details}{ Some details. } \subsection{Returns}{ The result. } \subsection{Examples}{ \if{html}{\out{
}} \preformatted{job$run() } \if{html}{\out{
}} } } # format.rd_r6_method strips \dontrun etc from examples (#1072) Code cat(format(method), sep = "\n") Output \if{html}{\out{
}} \if{html}{\out{}} \if{latex}{\out{\hypertarget{method-Job-run}{}}} \subsection{\code{Job$run()}}{ Run the job. \subsection{Usage}{ \if{html}{\out{
}} \preformatted{Job$run()} \if{html}{\out{
}} } \subsection{Examples}{ \if{html}{\out{
}} \preformatted{ donrun() donttest() } \if{html}{\out{
}} } } # format.rd_r6_method omits empty optional sections Code cat(format(method), sep = "\n") Output \if{html}{\out{
}} \if{html}{\out{}} \if{latex}{\out{\hypertarget{method-Job-run}{}}} \subsection{\code{Job$run()}}{ Run. \subsection{Usage}{ \if{html}{\out{
}} \preformatted{Job$run()} \if{html}{\out{
}} } } roxygen2/tests/testthat/_snaps/rd-r6-external.md0000644000176200001440000000125415172444137021422 0ustar liggesusers# @R6method tag parser validates input Code . <- roxy_tag_parse(roxy_tag("R6method", "")) Message x NA:NA: @R6method requires a value like `Class$method`. Code . <- roxy_tag_parse(roxy_tag("R6method", "nomethod")) Message x NA:NA: @R6method must be of the form `Class$method`. Code . <- roxy_tag_parse(roxy_tag("R6method", "Foo$bar\nextra")) Message x NA:NA: @R6method must be only 1 line long, not 2. i The first line is "Foo$bar" # @R6method warns on unknown class Code roc_proc_text(rd_roclet(), text) Message x :2: @R6method Can't find R6 class . Output list() roxygen2/tests/testthat/_snaps/object-format.md0000644000176200001440000000166415172444135021407 0ustar liggesusers# format has nice defaults for bare vectors Code call_to_format(x <- list(a = 1, b = 2)) Output [1] "An object of class \\code{list} of length 2." Code call_to_format(x <- ordered(letters[1:5])) Output [1] "An object of class \\code{ordered} (inherits from \\code{factor}) of length 5." Code call_to_format(x <- diag(10)) Output [1] "An object of class \\code{matrix} (inherits from \\code{array}) with 10 rows and 10 columns." Code call_to_format(x <- array(1:27, dim = c(3, 3, 3))) Output [1] "An object of class \\code{array} of dimension 3 x 3 x 3." Code call_to_format(x <- data.frame(a = 1, b = 2)) Output [1] "An object of class \\code{data.frame} with 1 rows and 2 columns." # format escapes braces in class names (#1744) Code call_to_format(x <- quote({ })) Output [1] "An object of class \\code{\\{} of length 1." roxygen2/tests/testthat/_snaps/markdown-link-resolve.md0000644000176200001440000000144615172444135023103 0ustar liggesusers# useful warning if no topic found Code . <- find_package("doesntexist") Message x Could not resolve link to topic "doesntexist" in the dependencies or base packages. i If you haven't documented "doesntexist" yet, or just changed its name, this is normal. Once "doesntexist" is documented, this warning goes away. i Make sure that the name of the topic is spelled correctly. i Always list the linked package as a dependency. i Alternatively, you can fully qualify the link with a package name. # gives useful warning if same name in multiple packages Code . <- find_package("pkg_env") Message x Topic "pkg_env" is available in multiple packages: pkgload and rlang. i Qualify topic explicitly with a package name when linking to it. roxygen2/tests/testthat/_snaps/tag-parser.md0000644000176200001440000001335615172444140020715 0ustar liggesusers# tags containing only whitespace generate warning Code tag <- roxy_test_tag(" ") expect_parse_failure(tag_value(tag)) Output Message: x test.R:1: @test requires a value. Code expect_parse_failure(tag_inherit(tag)) Output Message: x test.R:1: @test requires a value. Code expect_parse_failure(tag_name(tag)) Output Message: x test.R:1: @test requires a value. Code expect_parse_failure(tag_name_description(tag)) Output Message: x test.R:1: @test requires two parts: a name and a description. Code expect_parse_failure(tag_code(tag)) Output Message: x test.R:1: @test requires a value. Code expect_parse_failure(tag_examples(tag)) Output Message: x test.R:1: @test requires a value. Code expect_parse_failure(tag_markdown(tag)) Output Message: x test.R:1: @test requires a value. Code expect_parse_failure(tag_markdown_with_sections(tag)) Output Message: x test.R:1: @test requires a value. # tags check for mismatched parents gives useful warnings Code tag <- roxy_test_tag("a {") expect_parse_failure(tag_value(tag)) Output Message: x test.R:1: @test has mismatched braces or quotes. Code expect_parse_failure(tag_inherit(tag)) Output Message: x test.R:1: @test has mismatched braces or quotes. Code expect_parse_failure(tag_name(tag)) Output Message: x test.R:1: @test has mismatched braces or quotes. Code expect_parse_failure(tag_two_part(tag)) Output Message: x test.R:1: @test has mismatched braces or quotes. Code expect_parse_failure(tag_words(tag)) Output Message: x test.R:1: @test has mismatched braces or quotes. Code expect_parse_failure(tag_examples(tag)) Output Message: x test.R:1: @test has mismatched braces or quotes. Code # markdown tags return empty values tag_markdown(tag) Message x test.R:1: @test has mismatched braces or quotes. Output [test.R: 1] @test 'a {' {parsed} Code tag_markdown_with_sections(tag) Message x test.R:1: @test has mismatched braces or quotes. Output [test.R: 1] @test 'a {' {parsed} --- Code tag <- roxy_test_tag("# one\ntwo\n# three\nfour {") tag_markdown_with_sections(tag) Message x test.R:1: @test has mismatched braces or quotes. Output [test.R: 1] @test '# one...' {parsed} # tag_inhert checks for valid inherits Code tag <- roxy_test_tag("foo params section") . <- tag_inherit(tag) Message x test.R:1: @test attempts to inherit from unknown type "section". # tag_name() checks for valid names Code tag <- roxy_test_tag("a b c") expect_parse_failure(tag_name(tag)) Output Message: x test.R:1: @test must have only one argument, not 3. # tag_two_part() gives useful warnings Code tag <- roxy_test_tag("") expect_parse_failure(tag_two_part(tag, "a name", "a value", required = FALSE)) Output Message: x test.R:1: @test requires a name. Code expect_parse_failure(tag_two_part(tag, "a name", "a value")) Output Message: x test.R:1: @test requires two parts: a name and a value. # tag_words() gives useful warnings Code tag <- roxy_test_tag("a b") expect_parse_failure(tag_words(tag, 3, 3)) Output Message: x test.R:1: @test must have at least 3 words, not 2. Code expect_parse_failure(tag_words(tag, 1, 1)) Output Message: x test.R:1: @test must have at most 1 word, not 2. # tag_words() warns on multi-line content and preserves value Code tag <- roxy_test_tag("a\nb") out <- tag_words(tag) Message x test.R:1: @test must be only 1 line long, not 2. i The first line is "a" # tag_two_part() warns on multi-line content and preserves value Code tag <- roxy_test_tag("foo bar\nbaz") out <- tag_two_part(tag, "a name", "a value") Message x test.R:1: @test must be only 1 line long, not 2. i The first line is "foo bar" # tag_value() warns on multi-line content and preserves value Code tag <- roxy_test_tag("a\nb") out <- tag_value(tag) Message x test.R:1: @test must be only 1 line long, not 2. i The first line is "a" # tag_words_line() is deprecated Code tag <- roxy_test_tag("a b") tag_words_line(tag) Condition Warning: `tag_words_line()` was deprecated in roxygen2 8.0.0. i Please use `tag_words()` instead. Output [test.R: 1] @test 'a b' {parsed} # tag_toggle() gives useful warnings Code tag <- roxy_test_tag("x") tag_toggle(tag) Message x test.R:1: @test must not be followed by any text. Output NULL # tag_code() gives useful warnings Code tag <- roxy_test_tag("a + ") tag_code(tag) Message x test.R:1: @test failed to parse. Caused by error in `parse()`: ! :2:0: unexpected end of input 1: a + ^ Output NULL roxygen2/tests/testthat/_snaps/rd-template.md0000644000176200001440000000054515172444137021070 0ustar liggesusers# invalid syntax generates useful warning Code . <- roc_proc_text(rd_roclet(), block) Message x :3: @templateVar requires two parts: a variable name and a value. # templates gives useful error if not found Code roc_proc_text(rd_roclet(), block) Condition Error: ! Can't find template "doesn't-exist". roxygen2/tests/testthat/_snaps/rd-inherit.md0000644000176200001440000000572015172444137020717 0ustar liggesusers# \links are transformed \arguments{ \item{algo}{The hashing algorithm to be used by \code{\link[digest]{digest}}. Defaults to "sha1"} } # invalid syntax gives useful warning Code . <- roc_proc_text(rd_roclet(), block) Message x :2: @inheritDotParams requires a source. x :3: @inheritSection requires two parts: a topic name and a section title. # warns on unknown inherit type Code parse_text(text) Message x :2: @inherit attempts to inherit from unknown type "blah". Output [[1]] [:3] $tag [line: 2] @inherit 'fun blah' {parsed} [line: 3] @backref '' {parsed} $call NULL $object NULL # warns if can't find section Code . <- roc_proc_text(rd_roclet(), code) Message x In topic 'b': @inheritSection failed to find section "A" in topic a. # warned if no params need documentation Code . <- roc_proc_text(rd_roclet(), code) Message x In topic 'x': @inheritParams failed. i All parameters are already documented; none remain to be inherited. # can inherit all from single function [1] "test-rd-inherit-dots.txt" # does not produce multiple ... args [1] "test-rd-inherit-dots-inherit.txt" # can inherit dots from several functions \arguments{ \item{...}{ Arguments passed on to \code{\link{foo}}, \code{\link{bar}} \describe{ \item{\code{x}}{x} \item{\code{y}}{y1} \item{\code{z}}{z} }} } # useful error for bad inherits Code . <- roc_proc_text(rd_roclet(), text) Message x In topic 'bar': argument selection failed. Caused by error in `FUN()`: ! object 'z' not found x In topic 'bar': @inheritDotParams failed. i No arguments inherited from `foo()`. # warns when no params to inherit (#1671) Code out <- roc_proc_text(rd_roclet(), text) Message x In topic 'bar': @inheritDotParams failed. i No arguments inherited from `foo()`. # inheritDotParams warns when source not found (#1602) Code . <- roc_proc_text(rd_roclet(), text) Message x In topic 'test': @inheritDotParams failed to find topic "format" in current package. x In topic 'test': @inheritDotParams failed. i No arguments inherited from `format()`. # useful warnings if can't find topics Code get_rd("not_installed::pkg", source = "source") Message x In topic 'source': @inherits failed because not_installed is not installed. Output NULL Code get_rd("base::doesntexist", source = "source") Message x In topic 'source': @inherits failed to find topic base::doesntexist. Output NULL Code get_rd("doesntexist", RoxyTopics$new(), source = "source") Message x In topic 'source': @inherits failed to find topic "doesntexist" in current package. Output NULL roxygen2/tests/testthat/_snaps/markdown-code.md0000644000176200001440000000363415172444136021405 0ustar liggesusers# multi-line inline code gives useful warning Code out <- roc_proc_text(rd_roclet(), block)[[1]] Message x :4: @description failed to evaluate inline markdown code. Caused by error: ! Multi-line `r ` markup is not supported. # inline code gives useful warning Code out <- roc_proc_text(rd_roclet(), block)[[1]] Output Message Quitting from :1-1 x :4: @description failed to evaluate inline markdown code. Caused by error: ! Failed to parse the inline R code: `r 1 + ` Reason: :2:0: unexpected end of input 1: 1 + ^ # interleaving fences and inline code Code cat(out1$get_value("details")) Output Details 10 \if{html}{\out{
}}\preformatted{y <- x + 10 y #> [1] 20 }\if{html}{\out{
}} # preserves white space Code cat(out1$get_value("details")) Output \if{html}{\out{
}}\preformatted{a <- 1 b <- 2 }\if{html}{\out{
}} \if{html}{\out{
}}\preformatted{c <- 3 }\if{html}{\out{
}} # alternative knitr engines Code print(out1 <- roc_proc_text(rd_roclet(), "\n #' Title\n #'\n #' Description.\n #'\n #' ```{verbatim}\n #' #| file = testthat::test_path(\"example.Rmd\")\n #' ```\n #' @md\n #' @name x\n NULL\n ")) Output $x.Rd % Generated by roxygen2: do not edit by hand % Please edit documentation in ./ \name{x} \alias{x} \title{Title} \description{ Description. } \details{ \if{html}{\out{
}}\preformatted{```\{r\} # comment this <- 10 is <- this + 10 good <- this + is }\if{html}{\out{
}} } roxygen2/tests/testthat/_snaps/markdown-state.md0000644000176200001440000000026215172444135021604 0ustar liggesusers# warning for both @md and @noMd Code out1 <- roc_proc_text(rd_roclet(), block) Message x :5: @md conflicts with @noMd; turning markdown parsing off. roxygen2/tests/testthat/_snaps/rd-params.md0000644000176200001440000000026615172444136020537 0ustar liggesusers# empty @param generates warning Code . <- roc_proc_text(rd_roclet(), block) Message x :3: @param requires two parts: an argument name and a description. roxygen2/tests/testthat/_snaps/rd-family.md0000644000176200001440000000321715172444136020534 0ustar liggesusers# careful ordering Code out Output $foo1.Rd % Generated by roxygen2: do not edit by hand % Please edit documentation in ./ \name{foo1} \alias{foo1} \title{foo1} \usage{ foo1() } \description{ foo1 } \seealso{ Other a: \code{\link[=Foo3]{Foo3()}}, \code{\link[=foo]{foo()}}, \code{\link[=foo2]{foo2()}} } \concept{a} $foo2.Rd % Generated by roxygen2: do not edit by hand % Please edit documentation in ./ \name{foo2} \alias{foo2} \title{foo2} \usage{ foo2() } \description{ foo2 } \seealso{ Other a: \code{\link[=Foo3]{Foo3()}}, \code{\link[=foo]{foo()}}, \code{\link[=foo1]{foo1()}} } \concept{a} $Foo3.Rd % Generated by roxygen2: do not edit by hand % Please edit documentation in ./ \name{Foo3} \alias{Foo3} \title{Foo3} \usage{ Foo3() } \description{ Foo3 } \seealso{ Other a: \code{\link[=foo]{foo()}}, \code{\link[=foo1]{foo1()}}, \code{\link[=foo2]{foo2()}} } \concept{a} $foo.Rd % Generated by roxygen2: do not edit by hand % Please edit documentation in ./ \name{foo} \alias{foo} \title{foo} \usage{ foo() } \description{ foo } \seealso{ Other a: \code{\link[=Foo3]{Foo3()}}, \code{\link[=foo1]{foo1()}}, \code{\link[=foo2]{foo2()}} } \concept{a} roxygen2/tests/testthat/_snaps/rd-section.md0000644000176200001440000000032215172444137020712 0ustar liggesusers# warn if forgotten colon Code . <- roc_proc_text(rd_roclet(), block) Message x :4: @section title spans multiple lines.. i Did you forget a colon (:) at the end of the title? roxygen2/tests/testthat/_snaps/tag.md0000644000176200001440000000057015172444137017423 0ustar liggesusers# warn about unknown tags Code . <- roc_proc_text(rd_roclet(), block) Message x :2: @unknown is not a known tag. # incomplete rd in prequel or tag raises issue Code out <- roc_proc_text(rd_roclet(), block) Message x :2: @title has mismatched braces or quotes. x :3: @aliases has mismatched braces or quotes. roxygen2/tests/testthat/_snaps/rd-r6-methods-inherited.md0000644000176200001440000000046415172444137023216 0ustar liggesusers# format.rd_r6_inherited renders method list Code cat(format(inherited), sep = "\n") Output \if{html}{\out{
Inherited methods
  • pkg::A$foo()
  • pkg::A$bar()
}} roxygen2/tests/testthat/_snaps/rd-usage.md0000644000176200001440000000346315172444137020363 0ustar liggesusers# new wrapping style doesn't change unexpectedly f( a = " a", b = " b", c = " c", d = " d" ) f( a = c("abcdef", "abcdef", "abcdef", "abcdef", "abcdef", "abcdef", "abcdef", "abcdef", "abcdef", "abcdef") ) \method{mean}{reallyratherquitelongclassname}( reallyreatherquitelongargument = "reallyratherquitelongvalue_____________________" ) long_replacement_fun( x, a = "aaaaaaaaaaaaaaaa", b = "aaaaaaaaaaaaaaaa", c = "aaaaaaaaaaaaaaaa" ) <- value function_name( x, y, xy = "abcdef", xyz = c(`word word word word` = "abcdef", `word word word` = "abcdef", `word word word` = "abcdef", `word word word` = "abcdef") ) function_name( f = function(x) { 1 2 } ) # old wrapping style doesn't change unexpectedly f(a = " a", b = " b", c = " c", d = " d") f(a = c("abcdef", "abcdef", "abcdef", "abcdef", "abcdef", "abcdef", "abcdef", "abcdef", "abcdef", "abcdef")) \method{mean}{reallyratherquitelongclassname}(reallyreatherquitelongargument = "reallyratherquitelongvalue_____________________") long_replacement_fun(x, a = "aaaaaaaaaaaaaaaa", b = "aaaaaaaaaaaaaaaa", c = "aaaaaaaaaaaaaaaa") <- value f(xxxxxxxxxxxxxxxxxx1, xxxxxxxxxxxxxxxxxx2, xxxxxxxxxxxxxxxxxx3, x = "\\"'", xxxxxxxxxxxxxxxxxx4, xxxxxxxxxxxxxxxxxx5, xxxxxxxxxxxxxxxxxx6, xxxxxxxxxxxxxxxxxx7) roxygen2/tests/testthat/_snaps/rd-r6.md0000644000176200001440000002336615172444137017612 0ustar liggesusers# integration test Code cat(read_lines(rd_file), sep = "\n") Output % Generated by roxygen2: do not edit by hand % Please edit documentation in R/classes.R \name{A} \alias{A} \title{Class A} \description{ Class A description. } \details{ Class A details } \examples{ a <- A$new() ## ------------------------------------------------ ## Method `A$meth1()` ## ------------------------------------------------ ## Example for meth1 ## ------------------------------------------------ ## Method `A$meth2()` ## ------------------------------------------------ ## Example for meth2 ## Second line ## Third line } \section{Public fields}{ \if{html}{\out{
}} \describe{ \item{\code{field1}}{A field 1.} \item{\code{field2}}{A field 2.} \item{\code{field3}}{A field 3.} } \if{html}{\out{
}} } \section{Active bindings}{ \if{html}{\out{
}} \describe{ \item{\code{active1}}{A binding 1.} \item{\code{active2}}{A binding 2.} \item{\code{active3}}{A binding 3.} } \if{html}{\out{
}} } \section{Methods}{ \subsection{Public methods}{ \itemize{ \item \href{#method-A-meth1}{\code{A$meth1()}} \item \href{#method-A-meth2}{\code{A$meth2()}} \item \href{#method-A-meth3}{\code{A$meth3()}} \item \href{#method-A-clone}{\code{A$clone()}} } } \if{html}{\out{
}} \if{html}{\out{}} \if{latex}{\out{\hypertarget{method-A-meth1}{}}} \subsection{\code{A$meth1()}}{ A method 1. \subsection{Usage}{ \if{html}{\out{
}} \preformatted{A$meth1(Z)} \if{html}{\out{
}} } \subsection{Arguments}{ \if{html}{\out{
}} \describe{ \item{\code{Z}}{zzzzzzz} } \if{html}{\out{
}} } \subsection{Examples}{ \if{html}{\out{
}} \preformatted{## Example for meth1 } \if{html}{\out{
}} } } \if{html}{\out{
}} \if{html}{\out{}} \if{latex}{\out{\hypertarget{method-A-meth2}{}}} \subsection{\code{A$meth2()}}{ Method 2 description. \subsection{Usage}{ \if{html}{\out{
}} \preformatted{A$meth2(Z = 10, ...)} \if{html}{\out{
}} } \subsection{Arguments}{ \if{html}{\out{
}} \describe{ \item{\code{Z}}{Overriding Z argument for meth2.} \item{\code{...}}{Rest.} } \if{html}{\out{
}} } \subsection{Details}{ Method 2 details. } \subsection{Returns}{ A value. } \subsection{Examples}{ \if{html}{\out{
}} \preformatted{## Example for meth2 ## Second line ## Third line } \if{html}{\out{
}} } } \if{html}{\out{
}} \if{html}{\out{}} \if{latex}{\out{\hypertarget{method-A-meth3}{}}} \subsection{\code{A$meth3()}}{ Method 3. \subsection{Usage}{ \if{html}{\out{
}} \preformatted{A$meth3(x)} \if{html}{\out{
}} } \subsection{Arguments}{ \if{html}{\out{
}} \describe{ \item{\code{x}}{An argument.} } \if{html}{\out{
}} } } \if{html}{\out{
}} \if{html}{\out{}} \if{latex}{\out{\hypertarget{method-A-clone}{}}} \subsection{\code{A$clone()}}{ The objects of this class are cloneable with this method. \subsection{Usage}{ \if{html}{\out{
}} \preformatted{A$clone(deep = FALSE)} \if{html}{\out{
}} } \subsection{Arguments}{ \if{html}{\out{
}} \describe{ \item{\code{deep}}{Whether to make a deep clone.} } \if{html}{\out{
}} } } } --- Code cat(read_lines(rd_file), sep = "\n") Output % Generated by roxygen2: do not edit by hand % Please edit documentation in R/classes.R \name{B} \alias{B} \title{Class B} \description{ Class B Description. } \details{ Class B details. } \section{Super class}{ \code{A} -> \code{B} } \section{Public fields}{ \if{html}{\out{
}} \describe{ \item{\code{field1}}{B field 1.} \item{\code{field4}}{B field 4.} } \if{html}{\out{
}} } \section{Active bindings}{ \if{html}{\out{
}} \describe{ \item{\code{active1}}{B binding 1.} \item{\code{active4}}{B binding 4.} \item{\code{active5}}{B binding 5.} } \if{html}{\out{
}} } \section{Methods}{ \subsection{Public methods}{ \itemize{ \item \href{#method-B-meth1}{\code{B$meth1()}} \item \href{#method-B-meth4}{\code{B$meth4()}} \item \href{#method-B-clone}{\code{B$clone()}} } } \if{html}{\out{
Inherited methods
  • A$meth2()
  • A$meth3()
}} \if{html}{\out{
}} \if{html}{\out{}} \if{latex}{\out{\hypertarget{method-B-meth1}{}}} \subsection{\code{B$meth1()}}{ B method 1. \subsection{Usage}{ \if{html}{\out{
}} \preformatted{B$meth1(Z)} \if{html}{\out{
}} } \subsection{Arguments}{ \if{html}{\out{
}} \describe{ \item{\code{Z}}{Still zzzzzzzz.} } \if{html}{\out{
}} } } \if{html}{\out{
}} \if{html}{\out{}} \if{latex}{\out{\hypertarget{method-B-meth4}{}}} \subsection{\code{B$meth4()}}{ B method 4. \subsection{Usage}{ \if{html}{\out{
}} \preformatted{B$meth4()} \if{html}{\out{
}} } } \if{html}{\out{
}} \if{html}{\out{}} \if{latex}{\out{\hypertarget{method-B-clone}{}}} \subsection{\code{B$clone()}}{ The objects of this class are cloneable with this method. \subsection{Usage}{ \if{html}{\out{
}} \preformatted{B$clone(deep = FALSE)} \if{html}{\out{
}} } \subsection{Arguments}{ \if{html}{\out{
}} \describe{ \item{\code{deep}}{Whether to make a deep clone.} } \if{html}{\out{
}} } } } --- Code cat(read_lines(rd_file), sep = "\n") Output % Generated by roxygen2: do not edit by hand % Please edit documentation in R/classes.R \name{C} \alias{C} \title{Class C} \description{ Class C Description. } \details{ Class C details. } \section{Super classes}{ \code{A} -> \code{B} -> \code{C} } \section{Public fields}{ \if{html}{\out{
}} \describe{ \item{\code{field2}}{C field 2.} \item{\code{field5}}{C field 5.} } \if{html}{\out{
}} } \section{Active bindings}{ \if{html}{\out{
}} \describe{ \item{\code{active2}}{C binding 2.} \item{\code{active4}}{C binding 4.} \item{\code{active6}}{C binding 6.} } \if{html}{\out{
}} } \section{Methods}{ \subsection{Public methods}{ \itemize{ \item \href{#method-C-meth2}{\code{C$meth2()}} \item \href{#method-C-meth5}{\code{C$meth5()}} } } \if{html}{\out{
Inherited methods
  • A$meth3()
  • B$meth1()
  • B$meth4()
}} \if{html}{\out{
}} \if{html}{\out{}} \if{latex}{\out{\hypertarget{method-C-meth2}{}}} \subsection{\code{C$meth2()}}{ C method 2. \subsection{Usage}{ \if{html}{\out{
}} \preformatted{C$meth2(Z = 10, ...)} \if{html}{\out{
}} } \subsection{Arguments}{ \if{html}{\out{
}} \describe{ \item{\code{Z}}{zzzzz} \item{\code{...}}{etc} } \if{html}{\out{
}} } } \if{html}{\out{
}} \if{html}{\out{}} \if{latex}{\out{\hypertarget{method-C-meth5}{}}} \subsection{\code{C$meth5()}}{ C method 5. \subsection{Usage}{ \if{html}{\out{
}} \preformatted{C$meth5()} \if{html}{\out{
}} } } } roxygen2/tests/testthat/_snaps/rd-include-rmd.md0000644000176200001440000000116015172444137021452 0ustar liggesusers# invalid syntax gives useful warning Code . <- roc_proc_text(rd_roclet(), block) Message x :2: @includeRmd requires a path. # useful warnings Code . <- roc_proc_text(rd_roclet(), block) Message x :3: @includeRmd Can't find Rmd 'path'. --- Code . <- roc_proc_text(rd_roclet(), text) Output Message Quitting from :1-3 [unnamed-chunk-2] Quitting from :1-2 [unnamed-chunk-1] x :3: @includeRmd failed to evaluate ''. Caused by error: ! Error roxygen2/tests/testthat/_snaps/rd-r6-class.md0000644000176200001440000000742415172444137020712 0ustar liggesusers# warns about unmatched components Code docs <- r6_doc(text) Message x :6: @description can't find matching R6 method. # format.rd_r6_class with fields Code cat(format(docs), sep = "\n") Output \section{Public fields}{ \if{html}{\out{
}} \describe{ \item{\code{x}}{A number.} \item{\code{y}}{A string.} } \if{html}{\out{
}} } # format.rd_r6_class with active bindings Code cat(format(docs), sep = "\n") Output \section{Active bindings}{ \if{html}{\out{
}} \describe{ \item{\code{val}}{A value.} } \if{html}{\out{
}} } # format.rd_r6_class with no inherited methods Code cat(format(docs), sep = "\n") Output \section{Super class}{ \code{R_GlobalEnv::C1} -> \code{C2} } \section{Methods}{ \subsection{Public methods}{ \itemize{ \item \href{#method-C2-meth1}{\code{C2$meth1()}} } } \if{html}{\out{
}} \if{html}{\out{}} \if{latex}{\out{\hypertarget{method-C2-meth1}{}}} \subsection{\code{C2$meth1()}}{ method1 \subsection{Usage}{ \if{html}{\out{
}} \preformatted{C2$meth1()} \if{html}{\out{
}} } } } # format.rd_r6_class with inherited methods Code cat(format(docs), sep = "\n") Output \section{Super class}{ \code{R_GlobalEnv::A} -> \code{B} } \section{Methods}{ \subsection{Public methods}{ \itemize{ \item \href{#method-B-shared}{\code{B$shared()}} \item \href{#method-B-clone}{\code{B$clone()}} } } \if{html}{\out{
Inherited methods
  • R_GlobalEnv::A$only_a()
}} \if{html}{\out{
}} \if{html}{\out{}} \if{latex}{\out{\hypertarget{method-B-shared}{}}} \subsection{\code{B$shared()}}{ Method from B. \subsection{Usage}{ \if{html}{\out{
}} \preformatted{B$shared()} \if{html}{\out{
}} } } \if{html}{\out{
}} \if{html}{\out{}} \if{latex}{\out{\hypertarget{method-B-clone}{}}} \subsection{\code{B$clone()}}{ The objects of this class are cloneable with this method. \subsection{Usage}{ \if{html}{\out{
}} \preformatted{B$clone(deep = FALSE)} \if{html}{\out{
}} } \subsection{Arguments}{ \if{html}{\out{
}} \describe{ \item{\code{deep}}{Whether to make a deep clone.} } \if{html}{\out{
}} } } } # format.rd_r6_class with markdown sections Code cat(format(docs), sep = "\n") Output \section{Methods}{ \subsection{Public methods}{ \itemize{ \item \href{#method-C-meth}{\code{C$meth()}} } } \if{html}{\out{
}} \if{html}{\out{}} \if{latex}{\out{\hypertarget{method-C-meth}{}}} \subsection{\code{C$meth()}}{ Method description. \subsection{Description section}{ Description section body. } \subsection{Usage}{ \if{html}{\out{
}} \preformatted{C$meth()} \if{html}{\out{
}} } \subsection{Details}{ \subsection{Details section}{ Details section body. } } } } roxygen2/tests/testthat/_snaps/rd-r6-methods.md0000644000176200001440000000212115172444137021235 0ustar liggesusers# format.rd_r6_methods with one method Code cat(format(methods), sep = "\n") Output \section{Methods}{ \subsection{Public methods}{ \itemize{ \item \href{#method-Foo-run}{\code{Foo$run()}} } } \if{html}{\out{
}} \if{html}{\out{}} \if{latex}{\out{\hypertarget{method-Foo-run}{}}} \subsection{\code{Foo$run()}}{ Run it. \subsection{Usage}{ \if{html}{\out{
}} \preformatted{Foo$run()} \if{html}{\out{
}} } } } # r6_all_examples aggregates across methods Code cat(r6_all_examples(docs$methods), sep = "\n") Output ## ------------------------------------------------ ## Method `C$greet()` ## ------------------------------------------------ c$greet() ## ------------------------------------------------ ## Method `C$stop()` ## ------------------------------------------------ c$stop() c$stop(force = TRUE) roxygen2/tests/testthat/_snaps/rd-r6-field.md0000644000176200001440000000232115172444137020657 0ustar liggesusers# warns about undocumented fields Code docs <- r6_doc(text) Message x In topic 'C': Undocumented R6 field: undocumented_field. # warns about fields documented multiple times Code docs <- r6_doc(text) Message x :5: R6 field documented multiple times: x. # warns about unknown fields Code docs <- r6_doc(text) Message x :4: Unknown R6 field: nosuch. x In topic 'C': Undocumented R6 field: x. # format.rd_r6_field produces \item markup Code cat(format(rd_r6_field("x", "A number."))) Output \item{\code{x}}{A number.} # format.rd_r6_fields produces fields & bindings sections Code cat(format(fields), sep = "\n") Output \section{Public fields}{ \if{html}{\out{
}} \describe{ \item{\code{x}}{A number.} \item{\code{y}}{A string.} } \if{html}{\out{
}} } --- Code cat(format(bindings), sep = "\n") Output \section{Active bindings}{ \if{html}{\out{
}} \describe{ \item{\code{val}}{A value.} } \if{html}{\out{
}} } roxygen2/tests/testthat/_snaps/markdown.md0000644000176200001440000000541215172444136020471 0ustar liggesusers# code blocks work Before \if{html}{\out{
}}\preformatted{x \%in\% 1:10 }\if{html}{\out{
}} After # code block with language creates HTML tag Before \if{html}{\out{
}}\preformatted{x \%in\% 1:10 }\if{html}{\out{
}} After # can insert block and inline html Code out$get_section("description") Output \description{ \if{html}{\out{

This is a paragraph

This is another paragraph

}} } --- Code out$get_section("description") Output \description{ This is a paragraph containing a manually inserted image before-\if{html}{\out{}}-after } # can convert table to Rd Code for (table in tables) { cat_line(table) cat_line(markdown(table)) cat_line() } Output | x | y | | --- | --- | | 1 | 2 | \tabular{ll}{ x \tab y \cr 1 \tab 2 \cr } | x | y | | :-: | --: | | 1 | 2 | \tabular{cr}{ x \tab y \cr 1 \tab 2 \cr } | x | y | | ----- | --------- | | 1 _2_ | 3 *4* `5` | \tabular{ll}{ x \tab y \cr 1 \emph{2} \tab 3 \emph{4} \code{5} \cr } # unhandled markdown generates warning Code . <- roc_proc_text(rd_roclet(), text) Message x :4: @description markdown translation failed. x block quotes are not currently supported # horizontal rules generate warning (#1707) Code . <- roc_proc_text(rd_roclet(), text) Message x :6: @details markdown translation failed. x horizontal rules are not currently supported # level 1 heading in markdown generates warning in some tags Code . <- roc_proc_text(rd_roclet(), text) Message x :4: @seealso markdown translation failed. x Level 1 headings are not supported in @seealso i Do you want to put the heading in @description or @details? # image formats work Code roc_proc_text(rd_roclet(), "\n #' Title\n #'\n #' ![](example.svg \"Plot title 1\")\n #' ![](example.pdf \"Plot title 2\")\n #' ![](example.PNG \"Plot title 3\")\n #' @md\n foo <- function() { }\n ")[[ 1]] Output % Generated by roxygen2: do not edit by hand % Please edit documentation in ./ \name{foo} \alias{foo} \title{Title} \usage{ foo() } \description{ \if{html}{\figure{example.svg}{Plot title 1}} \if{pdf}{\figure{example.pdf}{Plot title 2}} \figure{example.PNG}{Plot title 3} } roxygen2/tests/testthat/_snaps/load.md0000644000176200001440000000071515172444135017566 0ustar liggesusers# look up string Code find_load_strategy("blahblahb") Condition Error: ! Unknown value of `load` option: "blahblahb". # informative errors for bad inputs Code find_load_strategy(1) Condition Error: ! `load_code` must be a single string, not the number 1. Code find_load_strategy(NULL, list()) Condition Error: ! roxygen2 `load` option must be a single string, not an empty list. roxygen2/tests/testthat/_snaps/rd-r6-super.md0000644000176200001440000000062615172444137020740 0ustar liggesusers# format.rd_r6_super with one superclass Code cat(format(supers), sep = "\n") Output \section{Super class}{ \code{mypkg::Parent} -> \code{Child} } # format.rd_r6_super omits pkg:: for same-package classes Code cat(format(supers), sep = "\n") Output \section{Super classes}{ \code{otherpkg::GrandParent} -> \code{Parent} -> \code{Child} } roxygen2/tests/testthat/_snaps/roxygenize.md0000644000176200001440000000026015172444137021047 0ustar liggesusers# can regenerate NAMESPACE even if its broken Code roxygenise(path) Message Writing 'NAMESPACE' i Loading brokenNamespace Writing 'NAMESPACE' roxygen2/tests/testthat/_snaps/utils.md0000644000176200001440000000135415172444137020011 0ustar liggesusers# write_if_different produces informative messages Code write_if_different(path, "a <- 2") Message Writing 'test.R' Output [1] TRUE --- Code write_if_different(path, "a <- 2") Message x Skipping test.R () i It already exists and was not generated by roxygen2. Output [1] FALSE --- Code write_if_different(path, "a <- 2") Message x Skipping '+.R' i Invalid file name Output [1] FALSE # write_if_different produces correct command hyperlink Code write_if_different(path, "a <- 2", command = "rlang::inform('hi')") Message Writing ']8;;x-r-run:rlang::inform('hi')test.R]8;;' Output [1] TRUE roxygen2/tests/testthat/_snaps/roxygenize-needs.md0000644000176200001440000000024615172444137022147 0ustar liggesusers# needs_roxygenize returns TRUE when source is newer Code out <- needs_roxygenize(path) Message ! 1 man page may be out of date: * foo.Rd roxygen2/tests/testthat/_snaps/object-package.md0000644000176200001440000000412215172444136021503 0ustar liggesusers# person turned into meaningful text Code # Multiple given/family names person_desc(c("First", "Second"), c("Family1", "Family2")) Output [1] "First Second Family1 Family2 \\email{h@w.com}" Code # Multiple roles person_desc(role = "ctb") Output [1] "H W \\email{h@w.com} [contributor]" Code # ORCID comments person_desc(comment = c(ORCID = "0000-0003-4757-117X")) Output [1] "H W \\email{h@w.com} (\\href{https://orcid.org/0000-0003-4757-117X}{ORCID})" Code person_desc(comment = c(ORCID = "https://orcid.org/0000-0003-4757-117X")) Output [1] "H W \\email{h@w.com} (\\href{https://orcid.org/0000-0003-4757-117X}{ORCID})" Code person_desc(comment = c(ORCID = "0000-0003-4757-117X", "extra")) Output [1] "H W \\email{h@w.com} (\\href{https://orcid.org/0000-0003-4757-117X}{ORCID}) (extra)" Code # ROR comments person_desc(comment = c(ROR = "03wc8by49")) Output [1] "H W \\email{h@w.com} (\\href{https://ror.org/03wc8by49}{ROR})" Code person_desc(comment = c(ROR = "https://ror.org/03wc8by49")) Output [1] "H W \\email{h@w.com} (\\href{https://ror.org/03wc8by49}{ROR})" Code person_desc(comment = c(ROR = "03wc8by49", "extra")) Output [1] "H W \\email{h@w.com} (\\href{https://ror.org/03wc8by49}{ROR}) (extra)" Code # Arbitrary comments (#1746) person_desc(comment = c(acronym = "rajo", "Contributed XY")) Output [1] "H W \\email{h@w.com} (acronym: rajo, Contributed XY)" Code person_desc(comment = c("comment 1", "comment 2")) Output [1] "H W \\email{h@w.com} (comment 1, comment 2)" # useful message if Authors@R is corrupted Code package_authors("1 + ") Message x Failed to evaluate Authors@R. Caused by error in `parse()`: ! :2:0: unexpected end of input 1: 1 + ^ Output NULL Code package_authors("stop('Uhoh')") Message x Failed to evaluate Authors@R. Caused by error: ! Uhoh Output NULL roxygen2/tests/testthat/_snaps/rd-raw.md0000644000176200001440000000123015172444137020036 0ustar liggesusers# error-ful evalRd generates warning Code expect_parse_failure(roxy_tag_eval(roxy_test_tag(val = 1))) Output Message: x test.R:1: @test must evaluate to a character vector. Code expect_parse_failure(roxy_tag_eval(roxy_test_tag(val = NA_character_))) Output Message: x test.R:1: @test must not contain any missing values. Code expect_parse_failure(roxy_tag_eval(roxy_test_tag(val = quote(stop("Uhoh"))))) Output Message: x test.R:1: @test failed to evaluate. Caused by error: ! Uhoh roxygen2/tests/testthat/test-select-args.R0000644000176200001440000000133515166171276020360 0ustar liggesuserstest_that("warns on invalid input", { expect_snapshot({ select_args_text(c("x", "na.rm"), "-xlab:", "test") select_args_text(c("x", "na.rm"), '"a"', "test") select_args_text(c("x", "y", "z"), "-x:z", "test") }) }) test_that("positive initial values starts from nothing", { expect_equal(select_args_text(c("x", "y", "z"), "x y", "test"), c("x", "y")) }) test_that("negative initial starts from everything", { expect_equal(select_args_text(c("x", "y", "z"), "-z", "test"), c("x", "y")) }) test_that("can alternative exclusion and inclusion", { expect_equal(select_args_text(c("x", "y", "z"), "-z z", "test"), c("x", "y", "z")) expect_equal(select_args_text(c("x", "y", "z"), "z -z", "test"), character()) }) roxygen2/tests/testthat/test-rd-include-rmd.R0000644000176200001440000002116115151411610020733 0ustar liggesusersskip_if_not_installed("rmarkdown") # clear some state roxy_meta_clear() test_that("invalid syntax gives useful warning", { block <- " #' @includeRmd NULL " expect_snapshot(. <- roc_proc_text(rd_roclet(), block)) }) test_that("markdown file can be included", { skip_if_not(rmarkdown::pandoc_available("2.17")) tmp <- tempfile(fileext = ".md") on.exit(unlink(tmp, recursive = TRUE), add = TRUE) cat( "List:\n\n* item1\n* item2\n\nInline `code` and _emphasis_.\n", file = tmp ) rox <- sprintf( " #' Title #' @includeRmd %s #' @name foobar NULL", tmp ) out1 <- roc_proc_text(rd_roclet(), rox)[[1]] out2 <- roc_proc_text( rd_roclet(), " #' @title Title #' @details List: #' \\itemize{ #' \\item item1 #' \\item item2 #' } #' #' Inline \\code{code} and \\emph{emphasis}. #' @name foobar NULL" )[[1]] # make sure sections are in the same order expect_equal(sort(names(out1$sections)), sort(names(out2$sections))) out2$sections <- out2$sections[names(out1$sections)] expect_equivalent_rd(out1, out2) }) test_that("markdown with headers", { skip_if_not(rmarkdown::pandoc_available("2.17")) tmp <- tempfile(fileext = ".md") on.exit(unlink(tmp, recursive = TRUE), add = TRUE) cat( sep = "\n", file = tmp, "Text at the front", "", "# Header 1", "", "## Header 2", "", "Text", "", "## Header 22", "", "# Header 11", "", "Text again" ) rox <- sprintf( " #' Title #' @includeRmd %s #' @name foobar NULL", tmp ) out1 <- roc_proc_text(rd_roclet(), rox)[[1]] exp_details <- "Text at the front" exp_secs <- list( title = c("Header 1", "Header 11"), content = c( "\\subsection{Header 2}{\n\nText\n}\n\n\\subsection{Header 22}{\n}", "Text again" ) ) expect_equal_strings(out1$get_value("details"), exp_details) expect_equal_strings(out1$get_value("section"), exp_secs) }) test_that("subsection within details", { skip_if_not(rmarkdown::pandoc_available("2.17")) tmp <- tempfile(fileext = ".md") on.exit(unlink(tmp, recursive = TRUE), add = TRUE) cat( sep = "\n", file = tmp, "Text at the front", "", "", "## Subsection in details", "", "Some subsection text" ) rox <- sprintf( " #' Title #' @includeRmd %s #' @name foobar NULL", tmp ) out1 <- roc_proc_text(rd_roclet(), rox)[[1]] exp_details <- paste0( "Text at the front", "\\subsection{Subsection in details}{ Some subsection text }" ) expect_equal_strings(out1$get_value("details"), exp_details) }) test_that("links to functions", { skip_if_not(rmarkdown::pandoc_available("2.17")) tmp <- tempfile(fileext = ".md") on.exit(unlink(tmp, recursive = TRUE), add = TRUE) cat( sep = "\n", file = tmp, "This is a link: [roxygenize()].", "Another one: [stringr::str_length()]" ) rox <- sprintf( " #' Title #' @includeRmd %s #' @name foobar NULL", tmp ) out1 <- roc_proc_text(rd_roclet(), rox)[[1]] exp_details <- paste0( "This is a link: \\code{\\link[=roxygenize]{roxygenize()}}.", "Another one:\n\\code{\\link[stringr:str_length]{stringr::str_length()}}" ) expect_equal_strings(out1$get_value("details"), exp_details) }) test_that("links to functions, with anchors", { skip_if_not(rmarkdown::pandoc_available("2.17")) tmp <- tempfile(fileext = ".md") on.exit(unlink(tmp, recursive = TRUE), add = TRUE) cat( sep = "\n", file = tmp, "This is a link: [roxygenize()].", "Another one: [stringr::str_length()]", "", "[roxygenize()]: https://roxygen2.r-lib.org/reference/roxygenize.html", "[stringr::str_length()]: https://stringr.tidyverse.org/reference/str_length.html" ) rox <- sprintf( " #' Title #' @includeRmd %s #' @name foobar NULL", tmp ) out1 <- roc_proc_text(rd_roclet(), rox)[[1]] exp_details <- paste0( "This is a link: \\code{\\link[=roxygenize]{roxygenize()}}.", "Another one:\n\\code{\\link[stringr:str_length]{stringr::str_length()}}" ) expect_equal_strings(out1$get_value("details"), exp_details) }) test_that("empty Rmd", { tmp <- tempfile() on.exit(unlink(tmp), add = TRUE) tag <- roxy_test_tag() cat("", sep = "", file = tmp) expect_equal(rmd_eval_rd(tmp, tag), structure("", names = "")) cat(" ", sep = "", file = tmp) expect_equal(rmd_eval_rd(tmp, tag), structure("", names = "")) cat("\n", sep = "", file = tmp) expect_equal(rmd_eval_rd(tmp, tag), structure("", names = "")) }) test_that("inline html", { skip_if_not(rmarkdown::pandoc_available("2.17")) tmp <- tempfile(fileext = ".md") on.exit(unlink(tmp, recursive = TRUE), add = TRUE) cat( sep = "\n", file = tmp, "Text at the front", "", "", "## Subsection in details", "", "Some subsection text with inline html." ) rox <- sprintf( " #' Title #' @includeRmd %s #' @name foobar NULL", tmp ) out1 <- roc_proc_text(rd_roclet(), rox)[[1]] exp_details <- paste0( "Text at the front", "\\subsection{Subsection in details}{", "Some subsection text with ", "\\if{html}{\\out{}}inline html\\if{html}{\\out{}}.\n}" ) expect_equal_strings(out1$get_value("details"), exp_details) }) test_that("html block", { skip_if_not(rmarkdown::pandoc_available("2.17")) tmp <- tempfile(fileext = ".md") on.exit(unlink(tmp, recursive = TRUE), add = TRUE) cat( sep = "\n", file = tmp, "Text at the front", "", "", "", "Text" ) rox <- sprintf( " #' Title #' @includeRmd %s #' @name foobar NULL", tmp ) out1 <- roc_proc_text(rd_roclet(), rox)[[1]] exp_details <- paste0( "Text at the front", "\\if{html}{\\out{}}\\if{html}{\\out{}}", "Text" ) expect_equal_strings(out1$get_value("details"), exp_details) }) test_that("include as another section", { skip_if_not(rmarkdown::pandoc_available("2.17")) tmp <- tempfile(fileext = ".md") on.exit(unlink(tmp, recursive = TRUE), add = TRUE) cat( "List:\n\n* item1\n* item2\n\nInline `code` and _emphasis_.\n", file = tmp ) rox <- sprintf( " #' Title #' @includeRmd %s description #' @name foobar NULL", tmp ) out1 <- roc_proc_text(rd_roclet(), rox)[[1]] out2 <- roc_proc_text( rd_roclet(), " #' @title Title #' @description List: #' \\itemize{ #' \\item item1 #' \\item item2 #' } #' #' Inline \\code{code} and \\emph{emphasis}. #' @name foobar NULL" )[[1]] # make sure sections are in the same order expect_equal(sort(names(out1$sections)), sort(names(out2$sections))) out2$sections <- out2$sections[names(out1$sections)] expect_equivalent_rd(out1, out2) }) test_that("order of sections is correct", { skip_if_not(rmarkdown::pandoc_available("2.17")) tmp <- tempfile(fileext = ".md") on.exit(unlink(tmp, recursive = TRUE), add = TRUE) cat( "# Rmd\n\nList:\n\n* item1\n* item2\n\nInline `code` and _emphasis_.\n", file = tmp ) rox <- sprintf( " #' Title #' @description desc #' @details details #' @includeRmd %s #' @section After: #' This is after. #' @section After2: #' This is even more after. #' @name foobar NULL", tmp ) out1 <- roc_proc_text(rd_roclet(), rox)[[1]] expect_match(format(out1), "Rmd.*After.*After2") }) test_that("useful warnings", { skip_if_not(rmarkdown::pandoc_available("2.17")) block <- " #' Title #' @includeRmd path #' @name foobar NULL" expect_snapshot(. <- roc_proc_text(rd_roclet(), block)) path <- withr::local_tempfile( fileext = ".Rmd", lines = c( "```{r}", "stop('Error')", "```" ) ) path <- normalizePath(path) text <- sprintf( " #' Title #' @includeRmd %s #' @name foobar NULL", path ) expect_snapshot( . <- roc_proc_text(rd_roclet(), text), transform = function(x) { x <- gsub(path, "", x, fixed = TRUE) x <- gsub("file.*\\.Rmd", "", x) line <- grep("~~~", x)[1] if (!is.na(line)) { x <- x[1:(line - 1)] } x } ) }) test_that("sets width", { skip_if_not(rmarkdown::pandoc_available("2.17")) local_options(width = 123) temp_rd <- withr::local_tempfile(lines = "`r getOption('width')`") rox <- sprintf( " #' Title #' @includeRmd %s #' @name foobar NULL", temp_rd ) out <- roc_proc_text(rd_roclet(), rox)[[1]] expect_equal(out$get_value("details"), "80") }) roxygen2/tests/testthat/test-object-defaults.R0000644000176200001440000001150015154314256021207 0ustar liggesuserstest_that("values only get usage", { out <- roc_proc_text( rd_roclet(), " #' Title. a <- data.frame(a = 1:10) " )[[1]] expect_equal(out$get_value("usage"), rd("a")) expect_null(out$get_value("docType")) expect_null(out$get_value("keyword")) expect_null(out$get_value("format")) }) test_that("values can opt-in to @docType data", { out <- roc_proc_text( rd_roclet(), " #' Title. #' #' @docType data a <- data.frame(a = 1:10) " )[[1]] expect_equal(out$get_value("usage"), rd("a")) expect_equal(out$get_value("docType"), "data") }) test_that("@docType data automatically added to data objects created elsewhere", { out <- roc_proc_text( rd_roclet(), " a <- data.frame(a = 1:10) #' Title. 'a' " )[[1]] expect_equal(out$get_value("docType"), "data") expect_equal(out$get_value("usage"), rd("data(a)")) expect_equal(out$get_value("keyword"), "datasets") }) test_that("data usage wraps in data() when LazyData is false", { out <- roc_proc_text( rd_roclet(), " a <- data.frame(a = 1:10) #' Title. 'a' " )[[1]] expect_equal(out$get_value("usage"), rd("data(a)")) local_roxy_meta_set("lazy_data", TRUE) out <- roc_proc_text( rd_roclet(), " a <- data.frame(a = 1:10) #' Title. 'a' " )[[1]] expect_equal(out$get_value("usage"), rd("a")) }) # Reference classes ---------------------------------------------------------- test_that("@docType class automatically added to reference class objects", { out <- roc_proc_text( rd_roclet(), " #' Title. #' a <- setRefClass('a')" )[[1]] expect_equal(out$get_value("docType"), "class") }) # imports ----------------------------------------------------------------- test_that("only generates re-exports if no @name or @rdname", { block <- " #' @export stats::median " out <- roc_proc_text(rd_roclet(), block)[[1]] expect_equal(out$get_value("name"), "reexports") expect_equal(out$get_value("keyword"), "internal") block <- " #' Title #' @name stats-imports #' @export stats::median #' @rdname stats-imports stats::acf " out <- roc_proc_text(rd_roclet(), block)[[1]] expect_equal(out$get_value("name")[[1]], "stats-imports") expect_equal(out$get_value("alias"), c("stats-imports", "median", "acf")) expect_equal(out$get_value("keyword"), NULL) }) test_that("imports are automatically imported", { block <- " #' @export stats::median " out <- roc_proc_text(namespace_roclet(), block) expect_equal(out, c("export(median)", "importFrom(stats,median)")) block <- " #' @export #' @name foo stats::median " out <- roc_proc_text(namespace_roclet(), block) expect_equal(out, c("export(median)", "importFrom(stats,median)")) }) # packages ----------------------------------------------------------------- test_that("can create package documentation", { path <- local_package_copy(test_path("empty")) desc::desc_set( file = path, Package = "roxygendevtest", Title = "Package Title", Description = "Package description." ) block <- " #' @details Details. '_PACKAGE' " withr::with_dir( path, blocks <- parse_text(block, env = new.env()) ) out <- roclet_process(rd_roclet(), blocks, env = new.env(), base_path = ".")[[ 1 ]] expect_equal(out$get_value("name"), "roxygendevtest-package") expect_equal( out$get_value("alias"), c("roxygendevtest", "roxygendevtest-package") ) expect_equal(out$get_value("title"), "roxygendevtest: Package Title") expect_equal(out$get_value("description"), "Package description.") expect_equal(out$get_value("docType"), "package") expect_equal(out$get_value("details"), "Details.") }) test_that("package doc prefers logo.svg over logo.png (#1640)", { path <- local_package_copy(test_path("empty")) desc::desc_set( file = path, Package = "roxygendevtest", Title = "Package Title", Description = "Package description." ) block <- " #' @details Details. '_PACKAGE' " # No logo withr::with_dir(path, blocks <- parse_text(block)) out <- roclet_process(rd_roclet(), blocks)[[1]] expect_no_match(out$get_value("description"), "figure") # Only png dir.create(file.path(path, "man", "figures"), recursive = TRUE) file.create(file.path(path, "man", "figures", "logo.png")) withr::with_dir(path, blocks <- parse_text(block, env = new.env())) out <- roclet_process(rd_roclet(), blocks)[[1]] expect_match(out$get_value("description"), "logo.png") # Both svg and png: svg wins file.create(file.path(path, "man", "figures", "logo.svg")) withr::with_dir(path, blocks <- parse_text(block, env = new.env())) out <- roclet_process(rd_roclet(), blocks)[[1]] expect_match(out$get_value("description"), "logo.svg") expect_no_match(out$get_value("description"), "logo.png") }) roxygen2/tests/testthat/test-rd-r6-external.R0000644000176200001440000000334415165177023020716 0ustar liggesusers# @R6method tag parsing ---------------------------------------------------- test_that("@R6method tag parser validates input", { tag <- roxy_tag("R6method", "Foo$bar") result <- roxy_tag_parse(tag) expect_equal(result$val, list(class = "Foo", method = "bar")) expect_snapshot({ . <- roxy_tag_parse(roxy_tag("R6method", "")) . <- roxy_tag_parse(roxy_tag("R6method", "nomethod")) . <- roxy_tag_parse(roxy_tag("R6method", "Foo$bar\nextra")) }) }) # $set() ------------------------------------------------------------------ test_that("$set() infers @R6method automatically", { docs <- r6_doc( " #' Title C <- R6::R6Class('C', cloneable = FALSE) #' @description Method 2. #' @param x A number. C$set('public', 'meth2', function(x) { }) " ) meth <- docs$methods$self[[1]] expect_equal(meth$name, "meth2") expect_equal(meth$description, "Method 2.") expect_equal(meth$params, list(list(name = "x", description = "A number."))) }) # explicit @R6method ------------------------------------------------------- test_that("explicit @R6method works on NULL block", { docs <- r6_doc( " #' Title C <- R6::R6Class('C', cloneable = FALSE, public = list( meth2 = function(x) {} ) ) #' @R6method C$meth2 #' @description Method 2. #' @param x A number. NULL " ) meth <- docs$methods$self[[1]] expect_equal(meth$name, "meth2") expect_equal(meth$description, "Method 2.") expect_equal(meth$params, list(list(name = "x", description = "A number."))) }) test_that("@R6method warns on unknown class", { text <- " #' @R6method NoSuchClass$meth #' @description A method. NULL " expect_snapshot(roc_proc_text(rd_roclet(), text)) }) roxygen2/tests/testthat/made-by-roxygen/0000755000176200001440000000000013523072405020041 5ustar liggesusersroxygen2/tests/testthat/made-by-roxygen/without-header.Rd0000644000176200001440000000001613523072405023256 0ustar liggesusers # Test file roxygen2/tests/testthat/made-by-roxygen/empty.Rd0000644000176200001440000000000013523072405021454 0ustar liggesusersroxygen2/tests/testthat/made-by-roxygen/with-header.Rd0000644000176200001440000000010213523072405022522 0ustar liggesusers# Generated by roxygen2 (4.0.0): Do not edit by hand # Test file roxygen2/tests/testthat/test-rd-usage.R0000644000176200001440000002724015166171276017661 0ustar liggesuserstest_that("@usage overrides default", { out <- roc_proc_text( rd_roclet(), " #' A #' @usage a(a=2) a <- function(a=1) {}" )[[1]] expect_equal(out$get_value("usage"), rd("a(a=2)")) }) test_that("@usage overrides default for @docType data", { out <- roc_proc_text( rd_roclet(), " #' Title. #' #' @name abc #' @docType data #' @usage data(abc) NULL" )[[1]] expect_equal(out$get_value("usage"), rd("data(abc)")) }) test_that("@usage NULL suppresses default usage", { out <- roc_proc_text( rd_roclet(), " #' A #' @usage NULL a <- function(a=1) {}" )[[1]] expect_equal(out$get_value("usage"), NULL) }) test_that("quoted topics have usage statements", { out <- roc_proc_text( rd_roclet(), " #' Title. \"f\" <- function(a = 1, b = 2, c = a + b) {}" )[[1]] expect_equal(out$get_value("usage"), rd("f(a = 1, b = 2, c = a + b)")) expect_equal( out$get_rd("usage"), r"(\usage{ f(a = 1, b = 2, c = a + b) })" ) }) # Escaping -------------------------------------------------------------------- test_that("usage escaping preserved when combined", { out <- roc_proc_text( rd_roclet(), " #' Foo foo <- function(x = '%') x #' @rdname foo bar <- function(y = '%') y " )[[1]] expect_s3_class(out$get_value("usage"), "rd") }) test_that("default usage not double escaped", { out <- roc_proc_text( rd_roclet(), " #' Regular mean.foo <- function(x) 'foo' " )[[1]] expect_equal( out$get_rd("usage"), r"(\usage{ \method{mean}{foo}(x) })" ) }) test_that("% and \\ are escaped in usage", { out <- roc_proc_text( rd_roclet(), " #' Title. a <- function(a='%\\\\') {}" )[[1]] expect_equal(out$get_value("usage"), escape('a(a = "%\\\\")')) expect_equal( out$get_rd("usage"), r"[\usage{ a(a = "\%\\\\") }]" ) }) test_that("% and \\ not escaped in manual usage", { out <- roc_proc_text( rd_roclet(), " #' Title. #' @usage %\\ a <- function(a) {} " )[[1]] expect_equal(out$get_value("usage"), rd('%\\')) expect_equal( out$get_rd("usage"), r"(\usage{ %\ })" ) }) test_that("Special vars removed in rc methods usage", { out <- roc_proc_text( rd_roclet(), " #' Class Blob ABCD <- setRefClass('ABC', methods = list( draw = function(x = 1) { \"2\" x }) ) " )[[1]] expect_equal(out$get_value("rcmethods"), list("draw(x = 1)" = "2")) }) # object_usage ------------------------------------------------------------ test_that("usage captured from formals", { expect_equal( call_to_usage(f <- function() {}), "f()" ) expect_equal( call_to_usage(f <- function(a = 1) {}), "f(a = 1)" ) }) test_that("argument containing function is generates correct usage", { expect_equal( call_to_usage(f <- function(a = function(x) 1) {}), "f(a = function(x) 1)" ) }) test_that("backticks retained when needed", { expect_equal( call_to_usage(f <- function(`_a`) {}), "f(`_a`)" ) expect_equal( call_to_usage(`-f` <- function(x) {}), "`-f`(x)" ) }) test_that("% escaped when not in infix function", { expect_equal( call_to_usage(`%foo%bar` <- function(x, table) {}), r"(`\%foo\%bar`(x, table))" ) expect_equal( call_to_usage(`%foo%bar<-` <- function(x, value) {}), r"(`\%foo\%bar`(x) <- value)" ) }) test_that("default usage formats data correctly", { expect_equal( call_to_usage(hello <- 1), "hello" ) }) test_that("default usage formats replacement functions correctly", { expect_equal( call_to_usage(`f<-` <- function(x, value) {}), "f(x) <- value" ) expect_equal( call_to_usage(`f<-` <- function(x, y, value) {}), "f(x, y) <- value" ) }) test_that("default usage formats infix functions correctly", { expect_equal(call_to_usage("%.%" <- function(a, b) {}), r"(a \%.\% b)") expect_equal(call_to_usage(":" <- function(a, b) {}), "a:b") expect_equal(call_to_usage("+" <- function(a, b) {}), "a + b") # even if it contains <- expect_equal(call_to_usage("%<-%" <- function(a, b) {}), r"(a \%<-\% b)") # defaults are ignored expect_equal(call_to_usage(":" <- function(a = 1, b = 2) {}), "a:b") }) test_that("default usage formats S3 methods correctly", { expect_equal( call_to_usage(mean.foo <- function(x) {}), r"(\method{mean}{foo}(x))" ) expect_equal( call_to_usage(mean.function <- function(x) {}), r"(\method{mean}{`function`}(x))" ) expect_equal( call_to_usage("+.foo" <- function(x, b) {}), r"(\method{+}{foo}(x, b))" ) expect_equal( call_to_usage("%%.foo" <- function(x, b) {}), r"(\method{\%\%}{foo}(x, b))" ) expect_equal( call_to_usage("[<-.foo" <- function(x, value) {}), r"(\method{[}{foo}(x) <- value)" ) }) test_that("S4 classes have no default usage", { expect_equal( call_to_usage({ setClass("Foo") }), character() ) }) test_that("default usage correct for S4 generics", { expect_equal( call_to_usage({ setGeneric("foo", function(x, y) {}) }), "foo(x, y)" ) }) test_that("default usage correct for S4 methods", { expect_equal( call_to_usage({ setClass("Foo") setMethod("sum", "Foo", function(x, ..., na.rm = FALSE) {}) }), r"(\S4method{sum}{Foo}(x, ..., na.rm = FALSE))" ) expect_equal( call_to_usage({ setClass("Foo") setMethod("+", "Foo", function(e1, e2) "foo") }), r"(\S4method{+}{Foo,ANY}(e1, e2))" ) expect_equal( call_to_usage({ setClass("Foo") setMethod("[<-", "Foo", function(x, i, j, ..., value) "foo") }), r"(\S4method{[}{Foo}(x, i, j, ...) <- value)" ) expect_equal( call_to_usage({ setGeneric("%&&%", function(x, y) standardGeneric("%&&%")) setMethod("%&&%", signature("logical", "logical"), function(x, y) {}) }), r"(\S4method{\%&&\%}{logical,logical}(x, y))" ) }) test_that("default usage correct for S4 methods with different args to generic", { expect_equal( call_to_usage({ setGeneric("testfun", function(x, ...) standardGeneric("testfun")) setMethod("testfun", "matrix", function(x, add = FALSE, ...) { x - 1 }) }), r"(\S4method{testfun}{matrix}(x, add = FALSE, ...))" ) }) test_that("non-syntactic S4 class names are not escaped in usage", { expect_equal( call_to_usage({ setGeneric("rhs", function(x) standardGeneric("rhs")) setMethod("rhs", "<-", function(x) x[[3]]) }), r"(\S4method{rhs}{<-}(x))" ) }) # Wrapping -------------------------------------------------------------------- test_that("new wrapping style doesn't change unexpectedly", { expect_snapshot_output({ cat( call_to_usage({ f <- function( a = ' a', b = ' b', c = ' c', d = ' d' ) {} }), "\n\n" ) cat( call_to_usage({ f <- function( a = c( 'abcdef', 'abcdef', 'abcdef', 'abcdef', 'abcdef', 'abcdef', 'abcdef', 'abcdef', 'abcdef', 'abcdef' ) ) {} }), "\n\n" ) cat( call_to_usage({ mean.reallyratherquitelongclassname <- function( reallyreatherquitelongargument = 'reallyratherquitelongvalue_____________________' ) {} }), "\n\n" ) cat( call_to_usage({ `long_replacement_fun<-` <- function( x, a = 'aaaaaaaaaaaaaaaa', b = 'aaaaaaaaaaaaaaaa', c = 'aaaaaaaaaaaaaaaa', value ) {} }), "\n\n" ) cat( call_to_usage({ function_name <- function( x, y, xy = "abcdef", xyz = c( `word word word word` = "abcdef", `word word word` = "abcdef", `word word word` = "abcdef", `word word word` = "abcdef" ) ) {} }), "\n\n" ) cat( call_to_usage({ function_name <- function( f = function(x) { 1 2 } ) {} }), "\n\n" ) }) }) test_that("old wrapping style doesn't change unexpectedly", { local_roxy_meta_set("old_usage", TRUE) expect_snapshot_output({ cat( call_to_usage({ f <- function( a = ' a', b = ' b', c = ' c', d = ' d' ) {} }), "\n\n" ) cat( call_to_usage({ f <- function( a = c( 'abcdef', 'abcdef', 'abcdef', 'abcdef', 'abcdef', 'abcdef', 'abcdef', 'abcdef', 'abcdef', 'abcdef' ) ) {} }), "\n\n" ) cat( call_to_usage({ mean.reallyratherquitelongclassname <- function( reallyreatherquitelongargument = 'reallyratherquitelongvalue_____________________' ) {} }), "\n\n" ) cat( call_to_usage({ `long_replacement_fun<-` <- function( x, a = 'aaaaaaaaaaaaaaaa', b = 'aaaaaaaaaaaaaaaa', c = 'aaaaaaaaaaaaaaaa', value ) {} }), "\n\n" ) # breaking works after escapes (#265) cat( call_to_usage({ f <- function( xxxxxxxxxxxxxxxxxx1, xxxxxxxxxxxxxxxxxx2, xxxxxxxxxxxxxxxxxx3, x = "\"'", xxxxxxxxxxxxxxxxxx4, xxxxxxxxxxxxxxxxxx5, xxxxxxxxxxxxxxxxxx6, xxxxxxxxxxxxxxxxxx7 ) {} }), "\n\n" ) }) }) test_that("S7 class usage is constructor", { skip_unless_r(">= 4.3.0") expect_equal( call_to_usage({ Dog <- S7::new_class( "Dog", properties = list( name = S7::class_character, age = S7::class_numeric ) ) }), "Dog(name = character(0), age = integer(0))" ) }) test_that("S7 generic usage is function signature", { skip_unless_r(">= 4.3.0") expect_equal( call_to_usage({ speak <- S7::new_generic("speak", "x") }), "speak(x, ...)" ) }) test_that("S7 method usage includes comment", { skip_unless_r(">= 4.3.0") expect_equal( call_to_usage({ Dog <- S7::new_class("Dog") speak <- S7::new_generic("speak", "x") S7::method(speak, Dog) <- function(x) "Woof" }), "## S7 method for class \nspeak(x)" ) }) test_that("S7 multi-dispatch method usage includes all classes", { skip_unless_r(">= 4.3.0") expect_equal( call_to_usage({ Dog <- S7::new_class("Dog") Cat <- S7::new_class("Cat") greet <- S7::new_generic("greet", c("x", "y")) S7::method(greet, list(Dog, Cat)) <- function(x, y) "hi" }), "## S7 method for classes , \ngreet(x, y)" ) }) test_that("S7 union method usage shows member classes", { skip_unless_r(">= 4.3.0") expect_equal( call_to_usage({ Dog <- S7::new_class("Dog") Cat <- S7::new_class("Cat") Pet <- S7::new_union(Dog, Cat) speak <- S7::new_generic("speak", "x") S7::method(speak, Pet) <- function(x) "hi" }), "## S7 method for class /\nspeak(x)" ) }) test_that("preserves non-breaking-space", { expect_equal( call_to_usage(f <- function(a = "\u{A0}") {}), 'f(a = "\u{A0}")' ) }) roxygen2/tests/testthat/test-markdown-link-resolve.R0000644000176200001440000000563615154314256022403 0ustar liggesuserstest_that("don't resolve if current_package not set", { expect_equal(find_package("cli_abort"), NA_character_) }) test_that("topics in current package don't need qualification", { local_roxy_meta_set("current_package", "cli") expect_equal(find_package("cli_abort"), NA_character_) }) test_that("imported functions qualified with package name", { local_roxy_meta_set("current_package", "testMdLinks") local_roxy_meta_set("current_package_dir", test_path("testMdLinks")) expect_equal(find_package("cli_abort"), "cli") }) test_that("base functions don't need qualification", { local_roxy_meta_set("current_package", "testMdLinks") local_roxy_meta_set("current_package_dir", test_path("testMdLinks")) expect_equal(find_package("mean"), NA_character_) }) test_that("base functions re-exported by deps don't need qualification", { local_roxy_meta_set("current_package", "testMdLinks") local_roxy_meta_set("current_package_dir", test_path("testMdLinks")) expect_equal(find_package("is.null"), NA_character_) }) test_that("useful warning if no topic found", { local_roxy_meta_set("current_package", "testMdLinks") local_roxy_meta_set("current_package_dir", test_path("testMdLinks")) expect_snapshot(. <- find_package("doesntexist")) }) test_that("re-exported topics are identified", { local_roxy_meta_set("current_package", "testMdLinks") local_roxy_meta_set("current_package_dir", test_path("testMdLinks")) expect_equal(. <- find_package("process"), "processx") }) test_that("gives useful warning if same name in multiple packages", { skip_on_cran() # in case pkgload/rlang changes this local_roxy_meta_set("current_package", "testMdLinks") local_roxy_meta_set("current_package_dir", test_path("testMdLinks")) expect_equal( find_package_lookup("pkg_env", "testMdLinks", test_path("testMdLinks")), c("pkgload", "rlang") ) expect_snapshot(. <- find_package("pkg_env")) }) test_that("topic found in multiple base packages doesn't warn", { local_roxy_meta_set("current_package", "testMdLinks") local_roxy_meta_set("current_package_dir", test_path("testMdLinks")) # plot is in both base and graphics expect_no_message(expect_equal(find_package("plot"), NA_character_)) }) test_that("find_source handles simple cases", { skip_on_cran() # since depends on other packages # in base package expect_equal(find_source("list", "base"), "base") # topic not in namespace expect_equal(find_source("doesnt'exist", "cli"), "cli") # primitive objects are always in base expect_equal(find_source("is_null", "rlang"), "base") # callr re-exports process from processx expect_equal(find_source("process", "callr"), "processx") }) test_that("find_source traces re-exported non-function to source package", { # .data is a non-function object exported by rlang, re-exported by others skip_on_cran() skip_if_not_installed("tidyselect") expect_equal(find_source(".data", "tidyselect"), "rlang") }) roxygen2/tests/testthat/test-object-format.R0000644000176200001440000000073415151412021020661 0ustar liggesuserstest_that("format has nice defaults for bare vectors", { skip_if(getRversion() <= "4.0.0") expect_snapshot({ call_to_format(x <- list(a = 1, b = 2)) call_to_format(x <- ordered(letters[1:5])) call_to_format(x <- diag(10)) call_to_format(x <- array(1:27, dim = c(3, 3, 3))) call_to_format(x <- data.frame(a = 1, b = 2)) }) }) test_that("format escapes braces in class names (#1744)", { expect_snapshot({ call_to_format(x <- quote({})) }) }) roxygen2/tests/testthat/test-tag-metadata.R0000644000176200001440000000016214264333036020465 0ustar liggesuserstest_that("all exported tags included in tags.yml", { expect_setequal(tags_metadata()$tag, tags_list(FALSE)) }) roxygen2/tests/testthat/test-rd-r6-class.R0000644000176200001440000000721515170121342020167 0ustar liggesusers# Construction tests ------------------------------------------------------- test_that("can construct empty class", { text <- " #' Class C <- R6::R6Class(cloneable = FALSE)" docs <- r6_doc(text) expect_s3_class(docs, "rd_r6_class") expect_equal(docs$methods, rd_r6_methods("C")) expect_equal(docs$fields$fields, list()) expect_equal(docs$active_bindings$fields, list()) }) test_that("class description is not duplicated", { text <- " #' Title #' #' Description foo <- R6::R6Class( public = list( #' @description foo foo = function() {} ) ) " out <- roc_proc_text(rd_roclet(), text)[[1]] expect_equal(out$get_value("description"), "Description") }) test_that("title-only class has single description", { out <- roc_proc_text( rd_roclet(), " #' Title foo <- R6::R6Class(cloneable = FALSE) " )[[1]] expect_equal(out$get_value("description"), "Title") out <- roc_proc_text( rd_roclet(), " #' @title Title foo <- R6::R6Class(cloneable = FALSE) " )[[1]] expect_equal(out$get_value("description"), "Title") }) test_that("class with only active bindings doesn't error (#1610)", { text <- " #' Class C <- R6::R6Class('C', active = list( #' @field x A value. x = function(val) val ), cloneable = FALSE )" docs <- r6_doc(text) expect_equal(docs$methods, rd_r6_methods("C")) }) test_that("warns about unmatched components ", { text <- " #' Class #' @field field1 Yep. C <- R6::R6Class( public = list( #' @description Dangling. field1 = NULL ) )" expect_snapshot(docs <- r6_doc(text)) }) # Format tests ------------------------------------------------------------- test_that("format.rd_r6_class with fields", { docs <- rd_r6_class( class = "Foo", fields = rd_r6_fields(list( rd_r6_field("x", "A number."), rd_r6_field("y", "A string.") )) ) expect_snapshot(cat(format(docs), sep = "\n")) }) test_that("format.rd_r6_class with active bindings", { docs <- rd_r6_class( class = "Foo", active_bindings = rd_r6_fields( list(rd_r6_field("val", "A value.")), type = "active" ) ) expect_snapshot(cat(format(docs), sep = "\n")) }) test_that("format.rd_r6_class with no inherited methods", { text <- " C1 <- R6::R6Class('C1', cloneable = FALSE) #' @title Title #' @description Description. C2 <- R6::R6Class('C2', inherit = C1, cloneable = FALSE, public = list( #' @description method1 meth1 = function() 1 ) )" docs <- r6_doc(text) expect_snapshot(cat(format(docs), sep = "\n")) }) test_that("format.rd_r6_class with inherited methods", { text <- " A <- R6::R6Class('A', public = list( shared = function() 1, only_a = function() 2 ) ) #' Class B. B <- R6::R6Class('B', inherit = A, public = list( #' @description Method from B. shared = function() 3 ) )" docs <- r6_doc(text) expect_snapshot(cat(format(docs), sep = "\n")) }) test_that("format.rd_r6_class with markdown sections", { local_roxy_meta_set("markdown", TRUE) text <- " #' @title Title #' @description Description. C <- R6::R6Class('C', cloneable = FALSE, public = list( #' @description Method description. #' #' # Description section #' #' Description section body. #' @details # Details section #' #' Details section body. meth = function() { } ) )" docs <- r6_doc(text) expect_snapshot(cat(format(docs), sep = "\n")) }) roxygen2/tests/testthat/test-rd-r6-field.R0000644000176200001440000001026115170121342020140 0ustar liggesuserstest_that("r6_extract_field_tags builds rd_r6_field objects", { text <- " #' @field field1 Foo. #' @field field2 Bar. C <- R6::R6Class( public = list( field1 = NULL, field2 = \"foobar\" ) )" block <- parse_text(text)[[1]] r6data <- block_get_tag_value(block, ".r6data") expect_silent(fields <- r6_extract_field_tags(block, r6data, "field")) expect_equal( fields$fields, list( rd_r6_field("field1", "Foo."), rd_r6_field("field2", "Bar.") ) ) }) test_that("comma-separated @field works (#1600)", { text <- " #' Class C <- R6::R6Class( public = list( #' @field var_1,var_2 do something vars var_1 = 1, var_2 = 2 ) )" expect_silent(docs <- r6_doc(text)) expect_equal( docs$fields$fields, list(rd_r6_field("var_1, var_2", "do something vars")) ) }) test_that("r6_extract_field_tags builds active binding objects", { text <- " #' @field bind1 Active binding. #' @field bind2 Active 2. C <- R6::R6Class( active = list( bind1 = function(x) { }, bind2 = function(x) { } ) )" block <- parse_text(text)[[1]] r6data <- block_get_tag_value(block, ".r6data") expect_silent(bindings <- r6_extract_field_tags(block, r6data, "active")) expect_equal( bindings$fields, list( rd_r6_field("bind1", "Active binding."), rd_r6_field("bind2", "Active 2.") ) ) }) test_that("@field name NULL suppresses field documentation", { text <- " #' Class #' @field field1 Foo. #' @field field2 NULL C <- R6::R6Class( public = list( field1 = NULL, field2 = 'bar' ) )" block <- parse_text(text)[[1]] r6data <- block_get_tag_value(block, ".r6data") expect_silent(fields <- r6_extract_field_tags(block, r6data, "field")) expect_equal( fields$fields, list(rd_r6_field("field1", "Foo.")) ) }) test_that("warns about undocumented fields", { text <- " #' Class C <- R6::R6Class( public = list( undocumented_field = NULL ) )" expect_snapshot(docs <- r6_doc(text)) expect_equal(docs$fields$fields, list()) }) test_that("warns about fields documented multiple times", { text <- " #' Class #' @field x First. #' @field x Second. C <- R6::R6Class( public = list(x = NULL) )" expect_snapshot(docs <- r6_doc(text)) }) test_that("warns about unknown fields", { text <- " #' Class #' @field nosuch Doesn't exist. C <- R6::R6Class( public = list(x = NULL) )" expect_snapshot(docs <- r6_doc(text)) }) test_that("format.rd_r6_field produces \\item markup", { expect_snapshot(cat(format(rd_r6_field("x", "A number.")))) }) test_that("format.rd_r6_fields produces fields & bindings sections", { fields <- rd_r6_fields(list( rd_r6_field("x", "A number."), rd_r6_field("y", "A string.") )) expect_snapshot(cat(format(fields), sep = "\n")) bindings <- rd_r6_fields( list(rd_r6_field("val", "A value.")), type = "active" ) expect_snapshot(cat(format(bindings), sep = "\n")) }) test_that("format.rd_r6_fields returns nothing when empty", { expect_null(format(rd_r6_fields())) }) # Superclass field inheritance ----------------------------------------------- test_that("fields inherit docs from superclass", { text <- " #' Parent A <- R6::R6Class('A', cloneable = FALSE, public = list( #' @field x A field. x = NULL ) ) #' Child B <- R6::R6Class('B', cloneable = FALSE, inherit = A, public = list( x = NULL ) )" expect_silent(docs <- r6_doc(text)) expect_equal( docs$fields$fields, list(rd_r6_field("x", "A field.")) ) }) test_that("field docs in subclass override superclass", { text <- " #' Parent A <- R6::R6Class('A', cloneable = FALSE, public = list( #' @field x Parent field. x = NULL ) ) #' Child #' @field x Child field. B <- R6::R6Class('B', cloneable = FALSE, inherit = A, public = list( x = NULL ) )" docs <- r6_doc(text) expect_equal( docs$fields$fields, list(rd_r6_field("x", "Child field.")) ) }) roxygen2/tests/testthat/test-tag.R0000644000176200001440000000067515154314256016722 0ustar liggesuserstest_that("warn about unknown tags", { block <- " #' @unknown foo <- function() {} " expect_snapshot(. <- roc_proc_text(rd_roclet(), block)) }) test_that("incomplete rd in prequel or tag raises issue", { block <- " #' Title { #' @aliases title{ x <- 1 " expect_snapshot(out <- roc_proc_text(rd_roclet(), block)) expect_equal(out[[1]]$get_value("title"), "") expect_equal(out[[1]]$get_value("alias"), "x") }) roxygen2/tests/testthat/testCollateParse/0000755000176200001440000000000014520730024020304 5ustar liggesusersroxygen2/tests/testthat/testCollateParse/R/0000755000176200001440000000000015151411610020503 5ustar liggesusersroxygen2/tests/testthat/testCollateParse/R/c.R0000644000176200001440000000002315151411610021043 0ustar liggesusersc <- function() {} roxygen2/tests/testthat/testCollateParse/R/b.R0000644000176200001440000000002315151411610021042 0ustar liggesusersb <- function() {} roxygen2/tests/testthat/testCollateParse/DESCRIPTION0000644000176200001440000000062414520730024022014 0ustar liggesusersPackage: testCollateParse Title: Tools to make developing R code easier License: GPL-2 Description: A test to make sure collate field is parsed correctly when it has a file name per line and is indented (#790). This package only has a DESCRIPTION file. Author: Brodie Gaslam Maintainer: Brodie Gaslam Version: 0.1 Collate: 'b.R' 'c.R' roxygen2/tests/testthat/test-rd-describe-in.R0000644000176200001440000001536615151411610020726 0ustar liggesuserstest_that("@describeIn suggests @rdname", { block <- " #' @describeIn foo NULL " expect_snapshot(. <- roc_proc_text(rd_roclet(), block)) }) test_that("@describeIn generic destination captures s3 method source", { out <- roc_proc_text( rd_roclet(), " #' Title f <- function(x) UseMethod('f') #' @describeIn f Method for a #' f.a <- function(x) 1 " )[[1]] expect_equal(out$get_value("minidesc")$extends, "generic") expect_equal(out$get_value("minidesc")$generic, "f") expect_equal(out$get_value("minidesc")$class, "a") }) test_that("@describeIn generic destination captures s4 method source", { suppressMessages( out <- roc_proc_text( rd_roclet(), " #' Title setGeneric('f', function(x) standardGeneric('f')) #' @describeIn f Method for a setMethod(f, signature('a'), function(x) 1) " )[[1]] ) out$get_value("minidesc") expect_equal(out$get_value("minidesc")$extends, "generic") expect_equal(out$get_value("minidesc")$generic, "f") expect_equal(out$get_value("minidesc")$class, "a") }) test_that("@describeIn constructor destination captures s3 method source", { out <- roc_proc_text( rd_roclet(), " #' Title boo <- function() structure(list(), class = 'boo') #' @describeIn boo mean method #' mean.boo <- function(x) 1 " )[[1]] expect_equal(out$get_value("minidesc")$extends, "class") expect_equal(out$get_value("minidesc")$generic, "mean") expect_equal(out$get_value("minidesc")$class, "boo") }) test_that("@describeIn constructor destination captures s4 method source", { out <- roc_proc_text( rd_roclet(), " setGeneric('mean') #' Title setClass('a') #' @describeIn a mean method setMethod('mean', 'a', function(x) 1) " )[[1]] out$get_value("minidesc") expect_equal(out$get_value("minidesc")$extends, "class") expect_equal(out$get_value("minidesc")$generic, "mean") expect_equal(out$get_value("minidesc")$class, "a") }) test_that("@describeIn function destination captures function source", { out <- roc_proc_text( rd_roclet(), " #' Title f <- function(x) 1 #' @describeIn f A f2 <- function(x) 1 " )[[1]] expect_equal(out$get_value("minidesc")$name, "f2()") expect_equal(out$get_value("minidesc")$extends, "") expect_equal(out$get_value("minidesc")$generic, "") expect_equal(out$get_value("minidesc")$class, "") }) test_that("@describeIn class captures function name with data", { out <- roc_proc_text( rd_roclet(), " #' Title #' @name f NULL #' @describeIn f A f2 <- function(x) 1 " )[[1]] expect_equal(out$get_value("minidesc")$name, "f2()") }) test_that("@describeIn class captures function description", { out <- roc_proc_text( rd_roclet(), " #' Title f <- function(x) 1 #' @describeIn f A f2 <- function(x) 1 " )[[1]] expect_equal(out$get_value("minidesc")$desc, "A") }) test_that("Multiple @describeIn functions combined into one", { out <- roc_proc_text( rd_roclet(), " #' Power #' @param x base #' @param exp exponent power <- function(x, exp) x ^ exp #' @describeIn power Square a number square <- function(x) power(x, 2) #' @describeIn power Cube a number cube <- function(x) power(x, 3) " )[[1]] expect_snapshot(out$get_section("minidesc")) }) test_that("multiple methods and others are combined into a generic", { out <- roc_proc_text( rd_roclet(), " #' Zap generic #' #' @param x Object to zap. zap <- function(x) UseMethod('zap') #' @describeIn zap method zap.numeric <- function(x) {} #' @describeIn zap method zap.character <- function(x) {} #' @describeIn zap function (method for different generic) print.qux <- function(x) {} #' @describeIn zap function zap_helper <- function(x) {} " )[[1]] expect_snapshot(out$get_section("minidesc")) }) test_that("multiple methods and others are combined into a class constructor", { out <- roc_proc_text( rd_roclet(), " #' Class constructor #' #' @param x x foo <- function(x) {} #' @describeIn foo method print.foo <- function(x) {} #' @describeIn foo method format.foo <- function(x) {} #' @describeIn foo function (method for different class) format.bar <- function(x) {} #' @describeIn foo function is_foo <- function(x) {} " )[[1]] expect_snapshot(out$get_section("minidesc")) # more complicated with disambiguated class name "pkg_class" out <- roc_proc_text( rd_roclet(), " #' Class constructor with disambiguated class #' #' @param x x baz <- function(x) {} #' @describeIn baz method print.roxygen2_baz <- function(x) {} #' @describeIn baz function (method for another class) format.quuz_baz <- function(x) {}" )[[1]] expect_snapshot(out$get_section("minidesc")) }) test_that("infix and replacement names get nice label", { out <- roc_proc_text( rd_roclet(), " #' foo foo <- 100 #' @describeIn foo infix `%foo%` <- function(x, y) foo(x, y) #' @describeIn foo replacement for foo `foo<-` <- function(x, value) 10 " )[[1]] expect_snapshot(out$get_section("minidesc")) }) test_that("s4 methods get nice label", { withr::defer(removeClass("foo1")) out <- roc_proc_text( rd_roclet(), " #' Class #' setClass('foo1', slots = c(id = 'character'), where = .GlobalEnv) #' @describeIn foo1 generic setGeneric('m_id', function(x) standardGeneric('m_id')) #' @describeIn foo1 function setMethod('m_id', 'foo1', function(x) x@id) " )[[1]] expect_snapshot(out$get_section("minidesc")) withr::defer(removeClass("foo2")) withr::defer(removeClass("foo3")) out <- roc_proc_text( rd_roclet(), " setClass('foo2', where = .GlobalEnv) setClass('foo3', where = .GlobalEnv) #' bar1 setGeneric('bar1', function(x, y) standardGeneric('bar1')) #' @describeIn bar1 method1 setMethod('bar1', c('foo2', 'foo3'), function(x, y) 1) #' @describeIn bar1 method2 setMethod('bar1', c('foo3', 'foo2'), function(x, y) 1) " )[[1]] expect_snapshot(out$get_section("minidesc")) }) test_that("complains about bad usage", { block <- " #' bar bar <- 100 #' @name bar #' @describeIn foo shortcut for foo NULL " expect_snapshot(. <- roc_proc_text(rd_roclet(), block)) block <- " #' bar bar <- 100 #' @name bar #' @describeIn foo shortcut for foo foo <- 10 " expect_snapshot(. <- roc_proc_text(rd_roclet(), block)) block <- " #' bar bar <- 100 #' @rdname bar #' @describeIn foo shortcut for foo foo <- 10 " expect_snapshot(. <- roc_proc_text(rd_roclet(), block)) }) roxygen2/tests/testthat/test-rd-r6.R0000644000176200001440000000231615171700424017066 0ustar liggesuserstest_that("integration test", { path <- local_package_copy(test_path("testR6")) suppressMessages(roxygenise(path)) withr::defer(pkgload::unload("testR6")) rd_files <- sort(dir(file.path(path, "man"), full.names = TRUE)) for (rd_file in rd_files) { expect_snapshot(cat(read_lines(rd_file), sep = "\n")) expect_silent(chk <- tools::checkRd(rd_file)) expect_equal(length(chk), 0L) } }) test_that("multiple R6 classes in one topic (@rdname) produce valid Rd", { out <- roc_proc_text( rd_roclet(), " #' Title #' @name shared NULL #' @rdname shared A <- R6::R6Class('A', public = list( #' @field x Field x. x = NULL, #' @description Method a. meth_a = function() {} ) ) #' @rdname shared B <- R6::R6Class('B', public = list( #' @field y Field y. y = NULL, #' @description Method b. meth_b = function() {} ) ) " )[[1]] rd <- out$get_rd("rawRd") expect_match(rd, "\\item{\\code{x}}{Field x.}", fixed = TRUE) expect_match(rd, "\\item{\\code{y}}{Field y.}", fixed = TRUE) expect_match(rd, "A$meth_a", fixed = TRUE) expect_match(rd, "B$meth_b", fixed = TRUE) }) roxygen2/tests/testthat/test-rd-simple.R0000644000176200001440000000137715151411610020030 0ustar liggesuserstest_that("one line per concept", { out <- roc_proc_text( rd_roclet(), " #' @title a #' @name a #' @concept test1 #' @concept test2 NULL" )[[1]] expect_equal(out$get_value("concept"), c("test1", "test2")) expect_equal(out$get_rd("concept"), c("\\concept{test1}", "\\concept{test2}")) }) test_that("simple keys produce expected output", { out <- roc_proc_text( rd_roclet(), " #' @title a #' @encoding test #' @name a NULL" )[[1]] expect_equal(out$get_value("encoding"), "test") }) test_that("keywords split into pieces", { out <- roc_proc_text( rd_roclet(), " #' @keywords a b #' @title a #' @name a NULL" )[[1]] expect_equal(out$get_value("keyword"), c("a", "b")) }) roxygen2/tests/testthat/testRawNamespace/0000755000176200001440000000000015160334463020304 5ustar liggesusersroxygen2/tests/testthat/testRawNamespace/R/0000755000176200001440000000000014553531033020502 5ustar liggesusersroxygen2/tests/testthat/testRawNamespace/R/a.R0000644000176200001440000000020714553531033021044 0ustar liggesusers#' @import graphics #' @rawNamespace #' if (TRUE) { #' import(grDevices) #' } else { #' import(methods) #' } #' @import utils NULL roxygen2/tests/testthat/testRawNamespace/NAMESPACE0000644000176200001440000000021315151361571021517 0ustar liggesusers# Generated by roxygen2: do not edit by hand if (TRUE) { import(grDevices) } else { import(methods) } import(graphics) import(utils) roxygen2/tests/testthat/testRawNamespace/DESCRIPTION0000644000176200001440000000042215151411610021776 0ustar liggesusersPackage: testRawNamespace Title: Check that re-running on multi-line @rawNamespace directive is OK License: GPL-2 Description: testRawNamespace. Author: Hadley Maintainer: Hadley Encoding: UTF-8 Version: 0.1 RoxygenNote: 7.3.3.9000 roxygen2/tests/testthat/testNonASCII/0000755000176200001440000000000014520730024017231 5ustar liggesusersroxygen2/tests/testthat/testNonASCII/R/0000755000176200001440000000000015151411610017430 5ustar liggesusersroxygen2/tests/testthat/testNonASCII/R/a.R0000644000176200001440000000011515151411610017770 0ustar liggesusers#' 中文注释 #' #' @note 我爱中文。 printChineseMsg <- function() {} roxygen2/tests/testthat/testNonASCII/DESCRIPTION0000644000176200001440000000033213523072405020741 0ustar liggesusersPackage: testNonASCII Title: Test no change to Collate when there are no @includes License: GPL-2 Description: Author: Shrëktan Maintainer: Shrëktan Encoding: UTF-8 Version: 0.1 roxygen2/tests/testthat/test-roxygenize-setup.R0000644000176200001440000000266515163737466021525 0ustar liggesuserstest_that("useful error if no DESCRIPTION", { path <- local_package_copy(test_path("no-desc"), set_version = FALSE) expect_snapshot( roxygen_setup(path), error = TRUE, transform = function(x) gsub(path, "", x) ) }) test_that("informs about initial setup", { path <- local_package_copy(test_path("empty"), set_version = FALSE) expect_snapshot(roxygen_setup(path, cur_version = "8.0.0")) }) test_that("warns about non UTF-8 encoding", { path <- local_package_copy(test_path("empty")) desc::desc_set(file = path, Encoding = "latin1", RoxygenNote = "8.0.0") expect_snapshot(roxygen_setup(path, cur_version = "8.0.0")) }) test_that("warns if roxygen version is too new", { path <- local_package_copy(test_path("empty")) desc::desc_set(file = path, RoxygenNote = "10.0.0") expect_snapshot(roxygen_setup(path, cur_version = "8.0.0")) }) test_that("informs about major changes in 7.0.0", { path <- local_package_copy(test_path("empty")) desc::desc_set(file = path, RoxygenNote = "5.0.0") expect_snapshot(roxygen_setup(path, cur_version = "8.0.0")) }) test_that("removes old RoxygenNote field", { path <- local_package_copy(test_path("empty")) desc::desc_set(file = path, RoxygenNote = "7.0.0") suppressMessages(roxygen_setup(path, cur_version = "8.0.0")) desc <- desc::desc(file = path) expect_false(desc$has_fields("RoxygenNote")) expect_equal(desc$get("Config/roxygen2/version")[[1]], "8.0.0") }) roxygen2/tests/testthat/man-roxygen/0000755000176200001440000000000013540202201017262 5ustar liggesusersroxygen2/tests/testthat/man-roxygen/values.R0000644000176200001440000000005013523072405020713 0ustar liggesusers#' <%= x %> #' @param <%= y %> <%= z %> roxygen2/tests/testthat/test-rd.R0000644000176200001440000001303015151411610016526 0ustar liggesuserstest_that("empty file gives empty list", { out <- roc_proc_text(rd_roclet(), "") expect_identical(out, list()) }) test_that("NULL gives empty list", { out <- roc_proc_text(rd_roclet(), "NULL") expect_identical(out, list()) }) test_that("@noRd inhibits documentation", { out <- roc_proc_text( rd_roclet(), " #' Would be title #' @title Overridden title #' @name a #' @noRd NULL" ) expect_equal(length(out), 0) }) test_that("deleted objects not documented", { out <- roc_proc_text( rd_roclet(), " f <- function(){ .a <- 0 function(x = 1){ .a <<- .a + x .a } } #' Addition function. f2 <- f() rm(f) " ) expect_equal(names(out), "f2.Rd") }) test_that("documenting unknown function requires name", { block <- " #' Virtual Class To Enforce Max Slot Length setClass('A') #' Validity function. setValidity('A', function(object) TRUE) " expect_snapshot(. <- roc_proc_text(rd_roclet(), block)) }) test_that("can't set description and re-export", { block <- " #' @description NOPE #' @export magrittr::`%>%` " expect_snapshot(out <- roc_proc_text(rd_roclet(), block)) expect_length(out, 0) }) test_that("documenting NA gives useful error message (#194)", { block <- " #' Missing value NA " expect_snapshot(. <- roc_proc_text(rd_roclet(), block)) }) test_that("@description NULL", { # Just ignore in this case out <- roc_proc_text( rd_roclet(), " #' Title #' #' @description NULL #' @format NULL foobar <- 1:10 " ) expect_identical(out[[1]]$get_value("description"), "Title") # Still ignore out <- roc_proc_text( rd_roclet(), " #' Title #' @description NULL #' @description desc #' @format NULL foobar <- 1:10 " ) expect_identical(out[[1]]$get_value("description"), "desc") # Still ignore for objects as well out <- roc_proc_text( rd_roclet(), " #' Title #' @description NULL #' @format NULL foobar <- 1:10 " ) expect_identical(out[[1]]$get_value("description"), "Title") # But drop for package docs block <- " #' Title #' #' @docType package #' @description NULL #' @name pkg '_PACKAGE' " out <- roc_proc_text(rd_roclet(), block, wd = test_path("empty")) expect_null(out[[1]]$get_value("description")) }) test_that("@details NULL", { # Just ignore in this case out <- roc_proc_text( rd_roclet(), " #' Title #' #' @details NULL #' @format NULL foobar <- 1:10 " ) expect_null(out[[1]]$get_value("details")) # Still ignore out <- roc_proc_text( rd_roclet(), " #' Title #' @details NULL #' @details desc #' @format NULL foobar <- 1:10 " ) expect_identical(out[[1]]$get_value("details"), "desc") # Still ignore for objects as well out <- roc_proc_text( rd_roclet(), " #' Title #' @details NULL #' @format NULL foobar <- 1:10 " ) expect_null(out[[1]]$get_value("details")) }) # package docs ------------------------------------------------------------ test_that("package docs don't get alias if function present", { block <- " #' Title #' '_PACKAGE' #' Empty empty <- function() {} " out <- roc_proc_text(rd_roclet(), block, test_path("empty"))[[1]] expect_equal(out$get_value("alias"), "empty-package") }) test_that("package docs preserve existing aliases", { block <- " #' Title #' @aliases a b #' '_PACKAGE' " out <- roc_proc_text(rd_roclet(), block, test_path("empty"))[[1]] expect_equal(out$get_value("alias"), c("empty", "empty-package", "a", "b")) block <- paste0( block, " #' Empty empty <- function() {} " ) out <- roc_proc_text(rd_roclet(), block, test_path("empty"))[[1]] expect_equal(out$get_value("alias"), c("empty-package", "a", "b")) }) test_that("get correct alias even if user has overridden name", { block <- " #' Title #' @name foo #' @aliases bar #' '_PACKAGE' " out <- roc_proc_text(rd_roclet(), block, test_path("empty"))[[1]] expect_equal( out$get_value("alias"), c("empty", "empty-package", "foo", "bar") ) }) # UTF-8 ------------------------------------------------------------------- test_that("can generate nonASCII document", { path <- local_package_copy(test_path('testNonASCII')) withr::defer(pkgload::unload("testNonASCII")) expect_snapshot({ roxygenise(path, roclets = "rd") "Second run should be idempotent" roxygenise(path, roclets = "rd") }) rd_path <- file.path(path, "man", "printChineseMsg.Rd") expect_true(file.exists(rd_path)) rd <- read_lines(rd_path) expect_true(any(grepl("\u6211\u7231\u4e2d\u6587", rd))) expect_true(any(grepl("\u4e2d\u6587\u6ce8\u91ca", rd))) }) test_that("unicode escapes are ok", { path <- local_package_copy(test_path('testUtf8Escape')) withr::defer(pkgload::unload("testUtf8Escape")) expect_snapshot({ roxygenise(path, roclets = "rd") "Second run should be idempotent" roxygenise(path, roclets = "rd") }) rd_path <- file.path(path, "man", "a.Rd") expect_true(file.exists(rd_path)) rd <- read_lines(rd_path) expect_true(any(grepl("7\u00b0C", rd))) }) test_that("automatically deletes unused files", { path <- local_package_copy(test_path("empty")) dir.create(file.path(path, "man")) suppressMessages(roxygenise(path)) withr::defer(pkgload::unload("empty")) write_lines(made_by("%"), file.path(path, "man/test.Rd")) expect_snapshot(roxygenise(path)) }) roxygen2/tests/testthat/test-markdown.R0000644000176200001440000003657415154546446020010 0ustar liggesusers# code -------------------------------------------------------------------- # see test-markdown-code.R for evaluated code test_that("backticks are converted to \\code & \\verb", { out1 <- roc_proc_text( rd_roclet(), " #' Title #' #' Description with some `code` included. `More code.` #' @md foo <- function() {}" )[[1]] out2 <- roc_proc_text( rd_roclet(), " #' Title #' #' Description with some \\code{code} included. \\verb{More code.} foo <- function() {}" )[[1]] expect_equivalent_rd(out1, out2) }) test_that("code blocks work", { out1 <- roc_proc_text( rd_roclet(), " #' Title #' #' @description #' Before #' ``` #' x %in% 1:10 #' ``` #' After #' @md foo <- function() {}" )[[1]] expect_snapshot_output(cat(out1$get_value("description"))) # And check that extra empty paragraphs don't affect the output out2 <- roc_proc_text( rd_roclet(), " #' Title #' #' @description #' Before #' #' ``` #' x %in% 1:10 #' ``` #' #' After #' @md foo <- function() {}" )[[1]] expect_equal(out1$get_value("description"), out2$get_value("description")) }) test_that("code block with language creates HTML tag", { out1 <- roc_proc_text( rd_roclet(), " #' Title #' #' @description #' Before #' ```r #' x %in% 1:10 #' ``` #' After #' @md foo <- function() {}" )[[1]] expect_snapshot_output(cat(out1$get_value("description"))) }) test_that("inline code escapes %", { expect_equal(markdown("`5%`"), r"(\verb{5\%})") expect_equal(markdown("`'5%'`"), r"(\code{'5\%'})") expect_equal(markdown("`%*%`"), r"(\code{\%*\%})") }) test_that("inline verbatim escapes Rd special chars", { expect_equal(markdown("`{`"), r"(\verb{\{})") expect_equal(markdown("`}`"), r"(\verb{\}})") expect_equal(markdown("`\\`"), r"(\verb{\\})") }) test_that("special operators get \\code{}, not \\verb{}", { expect_equal(markdown("`if`"), "\\code{if}") }) test_that("inline code works with < and >", { out <- roc_proc_text( rd_roclet(), " #' `SELECT FROM ` #' @md f <- function() 1 " )[[1]] expect_equal(out$get_value("title"), r"(\verb{SELECT FROM
})") }) # lists ------------------------------------------------------------------- test_that("itemized lists work", { out1 <- roc_proc_text( rd_roclet(), " #' Title #' #' Description with some #' * itemized #' * list #' #' And then another one: #' * item 1 #' * item 2 #' * item 3 #' @md foo <- function() {}" )[[1]] out2 <- roc_proc_text( rd_roclet(), " #' Title #' #' Description with some #' \\itemize{ #' \\item itemized #' \\item list #' } #' #' And then another one: #' \\itemize{ #' \\item item 1 #' \\item item 2 #' \\item item 3 #' } foo <- function() {}" )[[1]] expect_equivalent_rd(out1, out2) }) test_that("numbered lists work", { out1 <- roc_proc_text( rd_roclet(), " #' Title #' #' Description with some #' 1. numbered #' 2. list #' #' And then another one: #' 1. item 1 #' 1. item 2 #' 1. item 3 #' @md foo <- function() {}" )[[1]] out2 <- roc_proc_text( rd_roclet(), " #' Title #' #' Description with some #' \\enumerate{ #' \\item numbered #' \\item list #' } #' #' And then another one: #' \\enumerate{ #' \\item item 1 #' \\item item 2 #' \\item item 3 #' } foo <- function() {}" )[[1]] expect_equivalent_rd(out1, out2) }) test_that("nested lists are OK", { out1 <- roc_proc_text( rd_roclet(), " #' Title #' #' Description with some #' 1. numbered #' * itemized #' * sublist #' 2. list #' #' And then another one: #' * item 1 #' * item 2 #' * sublist #' * within #' * item 3 #' @md foo <- function() {}" )[[1]] out2 <- roc_proc_text( rd_roclet(), " #' Title #' #' Description with some #' \\enumerate{ #' \\item numbered #' \\itemize{ #' \\item itemized #' \\item sublist #' } #' \\item list #' } #' #' And then another one: #' \\itemize{ #' \\item item 1 #' \\item item 2 #' \\itemize{ #' \\item sublist #' \\item within #' } #' \\item item 3 #' } #' @md foo <- function() {}" )[[1]] expect_equivalent_rd(out1, out2) }) # html -------------------------------------------------------------------- test_that("can insert block and inline html", { out <- roc_proc_text( rd_roclet(), " #' Title #' #'

This is a paragraph

#'

This is another paragraph

#' @md foo <- function() {}" )[[1]] expect_snapshot(out$get_section("description")) out <- roc_proc_text( rd_roclet(), " #' Title #' #' This is a paragraph containing a manually inserted image #' before--after #' @md foo <- function() {}" )[[1]] expect_snapshot(out$get_section("description")) }) # tables ------------------------------------------------------------------ test_that("can convert table to Rd", { txt <- " | x | y | | --- | --- | | 1 | 2 | | x | y | | :-: | --: | | 1 | 2 | | x | y | | ----- | --------- | | 1 _2_ | 3 *4* `5` | " txt <- gsub("\n ", "\n", txt) tables <- strsplit(txt, "\n\n")[[1]] expect_snapshot({ for (table in tables) { cat_line(table) cat_line(markdown(table)) cat_line() } }) }) # inline formatting ------------------------------------------------------- test_that("emphasis works", { out1 <- roc_proc_text( rd_roclet(), " #' Title #' #' Description with some _emphasis_ included. _More emph._ #' @md foo <- function() {}" )[[1]] out2 <- roc_proc_text( rd_roclet(), " #' Title #' #' Description with some \\emph{emphasis} included. \\emph{More emph.} foo <- function() {}" )[[1]] expect_equivalent_rd(out1, out2) }) test_that("strong (bold) text works", { out1 <- roc_proc_text( rd_roclet(), " #' Title #' #' Description with some **bold** included. **More bold.** #' @md foo <- function() {}" )[[1]] out2 <- roc_proc_text( rd_roclet(), " #' Title #' #' Description with some \\strong{bold} included. \\strong{More bold.} foo <- function() {}" )[[1]] expect_equivalent_rd(out1, out2) }) test_that("markdown links are converted", { out1 <- roc_proc_text( rd_roclet(), " #' Title #' #' Description, see [http://acme.com]() for details. #' And here is a named link: [igraph](http://igraph.org). #' Here is another kind of link: . #' @md foo <- function() {}" )[[1]] out2 <- roc_proc_text( rd_roclet(), " #' Title #' #' Description, see \\url{http://acme.com} for details. #' And here is a named link: \\href{http://igraph.org}{igraph}. #' Here is another kind of link: \\url{https://log.r-hub.io}. foo <- function() {}" )[[1]] expect_equivalent_rd(out1, out2) }) test_that("images are recognized", { out1 <- roc_proc_text( rd_roclet(), " #' Title #' #' Description #' #' Details with a plot: ![](example.jpg \"Plot title\") #' @md foo <- function() {}" )[[1]] out2 <- roc_proc_text( rd_roclet(), " #' Title #' #' Description #' #' Details with a plot: \\figure{example.jpg}{Plot title} foo <- function() {}" )[[1]] expect_equivalent_rd(out1, out2) }) test_that("markdown is parsed in all fields where it is supported", { out1 <- roc_proc_text( rd_roclet(), " #' @title Title **with bold** #' #' @description Description **with bold** #' #' @details Details **with bold** #' #' @references References **with bold** #' #' @note Note **with bold** #' #' @seealso See also **with bold** #' #' @return Return **with bold** #' #' @author Author **with bold** #' #' @section Foobar: #' With some **bold text**. #' #' @format Format **with bold** #' #' @source Source **with bold** #' #' @param param Param **with bold** #' #' @slot slot Slot **with bold** #' #' @field field Field **with bold** #' #' @md foo <- function() {}" )[[1]] out2 <- roc_proc_text( rd_roclet(), " #' @title Title \\strong{with bold} #' #' @description Description \\strong{with bold} #' #' @details Details \\strong{with bold} #' #' @references References \\strong{with bold} #' #' @note Note \\strong{with bold} #' #' @seealso See also \\strong{with bold} #' #' @return Return \\strong{with bold} #' #' @author Author \\strong{with bold} #' #' @section Foobar: #' With some \\strong{bold text}. #' #' @format Format \\strong{with bold} #' #' @source Source \\strong{with bold} #' #' @param param Param \\strong{with bold} #' #' @slot slot Slot \\strong{with bold} #' #' @field field Field \\strong{with bold} foo <- function() {}" )[[1]] expect_equivalent_rd(out1, out2) }) test_that("markdown emphasis is ok", { out1 <- roc_proc_text( rd_roclet(), " #' Title #' #' Description with some *keywords* included. #' So far so good. \\preformatted{ *these are not #' emphasised*. Or are they? #' } #' @md foo <- function() {}" )[[1]] desc1 <- r"(Description with some \emph{keywords} included. So far so good. \preformatted{ *these are not emphasised*. Or are they? })" expect_equal(out1$get_section("description")[[2]], desc1) }) test_that("% is automatically escaped", { expect_equal(markdown("5%"), r"(5\%)") }) test_that("Escaping is kept", { out1 <- roc_proc_text( rd_roclet(), " #' Title #' #' Description. It has \\rd \\commands. #' @md foo <- function() {}" )[[1]] out2 <- roc_proc_text( rd_roclet(), " #' Title #' #' Description. It has \\rd \\commands. foo <- function() {}" )[[1]] expect_equivalent_rd(out1, out2) }) test_that("Do not pick up `` in arguments \\item #519", { out1 <- roc_proc_text( rd_roclet(), " #' Title #' #' Description. #' #' @param `_arg1` should not be code. But `this should`. #' @param `_arg2` should not be code, either. `But this.` #' #' @md foo <- function(`_arg1`, `_arg2`) {}" )[[1]] out2 <- roc_proc_text( rd_roclet(), " #' Title #' #' Description. #' #' @param `_arg1` should not be code. But \\verb{this should}. #' @param `_arg2` should not be code, either. \\verb{But this.} #' foo <- function(`_arg1`, `_arg2`) {}" )[[1]] expect_equivalent_rd(out1, out2) }) test_that("unhandled markdown generates warning", { text <- " #' Title #' #' > block quotes do not work, #' > sadly #' #' Blabla #' @md #' @name x NULL " expect_snapshot(. <- roc_proc_text(rd_roclet(), text)) }) test_that("horizontal rules generate warning (#1707)", { text <- " #' Title #' #' Some text #' #' ------------ #' #' More text #' @md #' @name x NULL " expect_snapshot(. <- roc_proc_text(rd_roclet(), text)) }) test_that("level 1 heading in markdown generates warning in some tags", { text <- " #' Title #' #' @seealso this and that #' # This is not good #' #' Blabla #' @md #' @name x NULL " expect_snapshot(. <- roc_proc_text(rd_roclet(), text)) }) test_that("level >2 markdown headings work in @description", { text <- " #' Title #' #' @description #' ## This is good #' yes #' #' @details #' Blabla #' @md #' @name x NULL " out <- roc_proc_text(rd_roclet(), text)[[1]] expect_equal_strings( out$get_value("description"), r"(\subsection{This is good}{ yes })" ) }) test_that("description can start with a heading (#1705)", { text <- " #' Title #' #' # Heading #' @md #' @name x NULL " out <- roc_proc_text(rd_roclet(), text)[[1]] expect_equal(out$get_value("section"), list(title = "Heading", content = "")) }) test_that("level >2 markdown headings work in @details", { text <- " #' Title #' #' Description. #' #' @details #' ## Heading 2 #' ### Heading 3 #' Text. #' @md #' @name x NULL " out <- roc_proc_text(rd_roclet(), text)[[1]] expect_equal_strings( out$get_value("details"), r"(\subsection{Heading 2}{ \subsection{Heading 3}{ Text. } })" ) }) test_that("level >2 markdown headings work in @return", { text <- " #' Title #' #' Description. #' #' @return Even this #' ## Can have a subsection. #' Yes. #' @md #' @name x NULL " out <- roc_proc_text(rd_roclet(), text)[[1]] expect_equal_strings( out$get_value("value"), r"(Even this \subsection{Can have a subsection.}{ Yes. })" ) }) test_that("level 1 heading in @details", { text1 <- " #' Title #' #' Description. #' #' @details #' Leading text goes into details. #' # This is its own section #' ## Can have a subsection #' Yes. #' # Another section #' With text. #' @md #' @name x NULL " out1 <- roc_proc_text(rd_roclet(), text1)[[1]] text2 <- " #' Title #' #' Description. #' @details #' Leading text goes into details. #' @section This is its own section:\\subsection{Can have a subsection}{ #' #' Yes. #' } #' #' @section Another section:With text. #' @name x NULL " out2 <- roc_proc_text(rd_roclet(), text2)[[1]] # make sure sections are in the same order expect_equal(sort(names(out1$sections)), sort(names(out2$sections))) out2$sections <- out2$sections[names(out1$sections)] expect_equivalent_rd(out1, out2) }) test_that("headings and empty sections", { text1 <- " #' Title #' #' Description. #' #' @details #' # This is its own section #' With text. #' @md #' @name x NULL " out1 <- roc_proc_text(rd_roclet(), text1)[[1]] expect_false("details" %in% names(out1$fields)) }) test_that("markdown() on empty input", { expect_identical(markdown(""), "") expect_identical(markdown(" "), "") expect_identical(markdown("\n"), "") }) test_that("markup in headings", { text1 <- " #' Title #' #' Description. #' #' @details #' Leading text goes into details. #' # Section with `code` #' ## Subsection with **strong** #' Yes. #' @md #' @name x NULL " out1 <- roc_proc_text(rd_roclet(), text1)[[1]] expect_equal( out1$get_value("section"), list( title = "Section with \\code{code}", content = paste( sep = "\n", "\\subsection{Subsection with \\strong{strong}}{", "", "Yes.", "}" ) ) ) }) test_that("image formats work", { expect_snapshot( roc_proc_text( rd_roclet(), " #' Title #' #' ![](example.svg \"Plot title 1\") #' ![](example.pdf \"Plot title 2\") #' ![](example.PNG \"Plot title 3\") #' @md foo <- function() { } " )[[1]] ) }) roxygen2/tests/testthat.R0000644000176200001440000000007414262717326015167 0ustar liggesuserslibrary(testthat) library(roxygen2) test_check("roxygen2") roxygen2/MD50000644000176200001440000005363715175154672012372 0ustar liggesuserse343affda32db4b441cb29005c4fb600 *DESCRIPTION b936b80d5074f0d9f8f3414089e14957 *LICENSE 7370f0a84de9d24175c4f9d5532757b2 *NAMESPACE 7ccdb5f47e17e7ab1df653b2caa089c4 *NEWS.md a774889e05e23a76972ecc257438b11d *R/block.R 459f6aa92ea0813341cbc5a8988234cf *R/collate.R 1b26b2d53a174f140d993ac6b71a3d82 *R/cpp11.R ea375ab4a3bbf0e73f1129faf8445b01 *R/field.R 4c8f02cf0cb39078a2275ed1b9cb56e1 *R/import-standalone-obj-type.R 754e065247c6c0636c3fe8440af3bfc9 *R/import-standalone-purrr.R f0289ac3a8cc612523ebf40c6bb9e5c7 *R/import-standalone-types-check.R 2d763d4db2b5f4a28d2ed36bb4ca2c53 *R/load.R 9547e62a188ff5b9be715a87dc1b3273 *R/markdown-code.R a3e3867545afe4a5286ab7d59c1cf498 *R/markdown-escaping.R 78cf6292425aff688407fa7fc1c05aff *R/markdown-link-resolve.R 943c56ffe290b29122a8968213d8ebb9 *R/markdown-link.R 45a339c8cc75d995d204f75315fdc930 *R/markdown-state.R 3fa91c78cd99c71530af0ef1cdf04351 *R/markdown.R ff4f4ced607abc637e6f49c5913a89fc *R/namespace.R 09216e6111a0f89674d6a3612c3f0a92 *R/object-defaults.R 60dec1ab4d5d8202c18aa40f58c2c79a *R/object-format.R 919fd06238331ff815f2ded2169ffae1 *R/object-from-call.R 88f086ac9b421303c2b04f52a54a0f13 *R/object-import.R f3da69dbe72b148708607690b85a813a *R/object-package.R 341d742d0e4ddd97dbedcae045bc6a29 *R/object-r6.R f838d41346ff96e8a0dc6219b528a75b *R/object-rc.R e0b845706c5db3261e3a14ba4fda4523 *R/object-s3.R 2025fe34fc563b171a62def151f7b7b5 *R/options.R c549abfc28b267b880940688cfcc74e1 *R/package_files.R e87cb4619d88cc230fffec7958455f80 *R/parse.R f603287b5cdac8b8a645444594cd9c18 *R/rd-backref.R a7cf53af84fc13d030dba82dc31e911a *R/rd-describe-in.R 5aa5a32c8951b2e06d1b6acbc2ef8a85 *R/rd-eval.R 864acc84cb5e75f912fdf4aad46dba9b *R/rd-examples.R f34fdd70ed1ff086a2b93d5a980949a6 *R/rd-family.R 6bf9941efc39ce3900f133d89e11485c *R/rd-include-rmd.R 1ebd7dad3b14adefafce7d5815528356 *R/rd-inherit.R 0e7ef3843de1fd146a2f944e90904496 *R/rd-markdown.R 939dcf8e26deeb470c5ee098717aedae *R/rd-name-alias.R 521ad52ec1ddf6a50b8c5461918723d3 *R/rd-params.R 502c7c651bed27dde2cc8733cede9015 *R/rd-r6-class.R 207d62a8805d15c701c74ac17609cb26 *R/rd-r6-external.R a6d50bf33b9dcd68751c27f82922a651 *R/rd-r6-field.R 1a7636d602e5c25cf4971e3bac3160b1 *R/rd-r6-methods-inherited.R 2b82a747a942d67e35d721f593cb1d3e *R/rd-r6-methods-self.R fd577c94888c7ecafb4a1280fd5676a8 *R/rd-r6-methods.R 71e80aee7be32229962eab3235622b53 *R/rd-r6-super.R c84683a3b3d01527d9d57022ad6df82f *R/rd-r6.R d2508ef97c835136fd54ba0541cff386 *R/rd-raw.R ded8793753c894c783678b6cbe304234 *R/rd-s4.R 84c8927b7597789625cd70e3b179abb2 *R/rd-s7.R a28bb01f09f05a159701e15a82562ac7 *R/rd-section.R 4b7cee735ef1627ea523bfe9e10e0664 *R/rd-simple.R 79d3d896505a421a2331c437f9956e1f *R/rd-template.R 772939237fd4b03c52dd73239b25c4ad *R/rd-usage.R cf45ac5b715744aaeed6987d17bda84e *R/rd.R 4658bcbdd863a3612b1ac0d45a5d9a6f *R/roclet.R eae0ce35fc2f958222ca246a25f32021 *R/roxygen2-package.R 272aaa91e47921c981d3d01175272ab5 *R/roxygenize-needs.R ba7376c13530499a24dce70dacc5b046 *R/roxygenize-setup.R 77caaa1fbc3962abf6ec155738a7cb49 *R/roxygenize.R b137feae6a5170c73fe94e30a28499fc *R/safety.R 9abd8330a5fd6b9403d77b4bcfb69dd3 *R/select-args.R dc417b82fa4272fcdafe3164970570bd *R/tag-metadata.R 8712a831904c5f689c3ae070bd9fbf4a *R/tag-parser.R 28a64181f5e579a5c901bd829e715131 *R/tag.R 873d809f2a89732901cd72ec0f0e7b05 *R/tokenize.R a78bfddaad1f7f8254a2f592ca9e3a11 *R/topic.R 66d17310d41cae7c166116d34a35d097 *R/topics.R 85d4078c6f5a1a0755d07817c3a0e154 *R/topo-sort.R bd19600f42f96971c72ea9bbffe733a4 *R/util-locale.R d31e18cb0def52e525c0b1ed49d0c6c9 *R/utils-io.R c455399f4b4db0a32a5946861da2603b *R/utils-rd.R 06facaac19a95a7bcae11bd3fab8821e *R/utils-warn.R 513aa5bf1ac28fbd07a9b7edc123bc94 *R/utils.R 7c8930665e5f18d2e2f48c1a73501827 *R/vignette.R 4df50b4923df32583a121ab3f3c4c368 *R/zzz.R 161d574f1b7208e7792cd05a02e9dc1c *README.md 7188e175ada94822cd1da8ae0e08f266 *build/vignette.rds 837e02d408f658236f722fd2a41d483b *inst/doc/extending.R d3c9265da409d2f6cc2f532de94b38e2 *inst/doc/extending.Rmd 42314f630adad70544c4f7c97f995138 *inst/doc/extending.html 609f8f5a95016fd6daa139ae22669aae *inst/doc/index-crossref.R c9a641c4fc37ad7dcebe2651ff178f94 *inst/doc/index-crossref.Rmd 5caab7127621d9562561936749af0361 *inst/doc/index-crossref.html 6dbb10db446f0fbfeeb453ff06189f2e *inst/doc/namespace.R effe663576e0d4ee0aeaeaeea93f1d61 *inst/doc/namespace.Rmd d816ed0b64fcadc59bbfc103307eab3a *inst/doc/namespace.html cc5beca0ba571715ba64caed076eb375 *inst/doc/rd-R6.R 6f664436f18981af7c8261443d0da8bf *inst/doc/rd-R6.Rmd 79dd189083ea9d006d0ed773c9db0158 *inst/doc/rd-R6.html ad5e9f550275fc9c0349c36f71870df1 *inst/doc/rd-S3.R 10b5cc54e9121c35116b475ed31155f0 *inst/doc/rd-S3.Rmd 03ec4b0e50fa7f47fb4f8ec6314de292 *inst/doc/rd-S3.html 451be6d3d9e30e9313c5cf8878e1ab79 *inst/doc/rd-S4.R 5cea2fef1c1a344ddae5de0189538e4e *inst/doc/rd-S4.Rmd c6adc1236aa29e3de95de6bc2f707e7d *inst/doc/rd-S4.html 90b570bdcc5d76bdf2043eb25bc51dc6 *inst/doc/rd-S7.R e724fb01a09c54746ad2ef8332f5f152 *inst/doc/rd-S7.Rmd c3fee7969897b26c6715fa07cdb1f3e8 *inst/doc/rd-S7.html 57b4a5c0d16fd8c7690f120c464d889f *inst/doc/rd-datasets.R ca063377b1d36bbc7a78a7a3053d8d1f *inst/doc/rd-datasets.Rmd 7f8c8b4115d7b1d64b3e49253e064485 *inst/doc/rd-datasets.html d72dd81d716d02fbf89284fa1ac7149e *inst/doc/rd-formatting.R 1bf7ae992ffffeead0a16d72f7ebeaf5 *inst/doc/rd-formatting.Rmd 3e1411c8ddd73ba2aeb1f3017dfecf39 *inst/doc/rd-formatting.html a44c09490474d2b4e9f841b650fed530 *inst/doc/rd-functions.R 70f99b1a49b00d2210bad7e2b1034234 *inst/doc/rd-functions.Rmd fa199001d8f07758b75e91a985134a39 *inst/doc/rd-functions.html 2b4fe2869267e9c497af463d4a6a6534 *inst/doc/rd-packages.R 31df33f6bad5a17cd35fdef8f28e5dab *inst/doc/rd-packages.Rmd a36bf14551049237c2d14ec8fae55c9c *inst/doc/rd-packages.html 15535818039892d00f8529c3f7245305 *inst/doc/reuse.R 536c0a5b883a0a584072d7a7d742ceef *inst/doc/reuse.Rmd cf7b8023134d0290e1e198907c623f43 *inst/doc/reuse.html 8bfd5bbb86ef9f371487f99acfabe952 *inst/doc/roxygen2.R ec36253762b495957d8c04aa10c45d78 *inst/doc/roxygen2.Rmd c2eb0dc74a8ecabf80895c0b620abaf6 *inst/doc/roxygen2.html 4f199d3a36e4fced57254457b75400f7 *inst/roxygen2-tags.yml ddb616baee106d36f51089c184038bf5 *man/RoxyTopic.Rd 52664c282da032ca2717fa97076ede2c *man/double_escape_md.Rd 7a90f135e7865de509a8d7518e3938f8 *man/escape_examples.Rd 391f696f961e28914508628a7af31b74 *man/figures/lifecycle-deprecated.svg 691b1eb2aec9e1bec96b79d11ba5e631 *man/figures/lifecycle-experimental.svg ed42e3fbd7cc30bc6ca8fa9b658e24a8 *man/figures/lifecycle-stable.svg bf2f1ad432ecccee3400afe533404113 *man/figures/lifecycle-superseded.svg 495cd86c483b2786a06bd04931ba42b3 *man/figures/logo.png 60843ab425df1c9bf55cad7525060e87 *man/figures/test-figure-1.png c1b1bef29ede8fca2f0c9f508c122aef *man/is_s3_generic.Rd a69627fbb9aad7681c7003f71274d5d7 *man/load.Rd 83623099744c222fd040915debd29001 *man/load_options.Rd adbca3052f25f95c0d0648ac47d62bf9 *man/markdown-internals.Rd f4fabfa9c9b3ba236875a6edba4889b7 *man/markdown-test.Rd 2273ff7f0ac47c5d6893bce97a6b058e *man/markdown_evaluate.Rd 649145201f9e37c8ad66146f8bff71e3 *man/markdown_pass1.Rd efa3364922468ecd10574b82c220e3c0 *man/namespace_roclet.Rd a4e92d1ae30ef7975aeb743b1e913ee8 *man/needs_roxygenize.Rd 6f2683fb17672059797617fdff99ae71 *man/object.Rd ddef0c00ca079d6ef0b46a8c80fa1737 *man/object_format.Rd 17f52f9ffc5ab68f3f0d2b0263655a9e *man/parse_package.Rd 01788cfc552533b525d53cb0e1005bb1 *man/rd_roclet.Rd 3869352420c19f2ae63af7be34fc6005 *man/rd_section.Rd 68b093e9ee3e813f6cb8610efa87b1df *man/roc_proc_text.Rd 24a87f99378bb2a31ec6402334831f66 *man/roclet.Rd f25d67925437393d0cd25483c935bc2b *man/roclet_find.Rd 5f03b7e60f6f2f7ecab84558b5843602 *man/roxy_block.Rd bac9ab70f80be189c6aa47137ad1c0e2 *man/roxy_tag.Rd 4dae2c9ac9e0c6437b1aabafef55938c *man/roxy_tag_rd.Rd 24891c213bf4910807829b11413b73ce *man/roxygen2-package.Rd 985ee6d6da61d89e60e40ae9f064c917 *man/roxygenize.Rd 9b70f1f2289264b4dc24b874b9e8b521 *man/tag_parsers.Rd dc027764052c815a1ebfe8228008048e *man/tags-index-crossref.Rd c9f263d6eaba226696a6ef635e1233d0 *man/tags-namespace.Rd 0c220d0f02c887ae33fbefffda8dc812 *man/tags-rd-R6.Rd d471e395228d1003e3e4a7b4bda568b9 *man/tags-rd-S3.Rd 4276036649085e109ccdeb6abb00d76c *man/tags-rd-S4.Rd 5dbfe2a6a5b9ddff61216d650282d42b *man/tags-rd-S7.Rd 3d3307f6dc6ece562d23ca30e0791f1e *man/tags-rd-datasets.Rd 7210931562523b652e07741ff810a4a3 *man/tags-rd-formatting.Rd 8c9dbe10c82719fb92ad721b55f475b9 *man/tags-rd-functions.Rd a7183272043c4fbdb073a08fec97f163 *man/tags-reuse.Rd 2ed2bdbc2e5eaa64c86e4a16d903dbee *man/tags_list.Rd 209c449f51daa8c75e825aafba65bb45 *man/update_collate.Rd 6ede81b72e63d439a987644f66e62786 *man/vignette_roclet.Rd 1c7853647289d7598e5d1271023368e1 *src/cpp11.cpp fecfbfb6f902fe7c544a801e5d94f43e *src/escapeExamples.cpp 1556891739e654a8a557cdcddbaaf290 *src/isComplete.cpp c2031a287457e490af02408283d4091e *src/leadingSpaces.cpp a7ded8de1428bbfc5723fc1710f9a1e0 *src/parser2.cpp a64de44fd9259df16c9c8e5c43ab4b2d *src/wrapUsage.cpp 60b281e01728cb05a4ca8c9fc26f75e5 *tests/testthat.R e686310a87142a0e59ff1e55565b8fbc *tests/testthat/Rd-example-1.R 4453aeecf77fbf36b95091b2a814fd4e *tests/testthat/Rd-example-2.R c0b393cde4b9539bbf2434630c0945d7 *tests/testthat/Rd-example-3.R dc7e3daa600d9358a557f9f5733684dd *tests/testthat/Rd-example-4.txt c46d818f7e2d5478276636b0175fcd51 *tests/testthat/_snaps/block.md a5d4630ae57537e7877a6087f747a05a *tests/testthat/_snaps/collate.md 74ed43847e7e75ce2e9939f45df6590f *tests/testthat/_snaps/load.md 9ecd3c910efb54c55512034a514d293e *tests/testthat/_snaps/markdown-code.md 116d78f577b9d90cce8ec383960ac4db *tests/testthat/_snaps/markdown-link-resolve.md be7e200bd1cc730a3f3b0b73cdf99595 *tests/testthat/_snaps/markdown-link.md 5d011e2db10a5b5d687b7a71680f8a2e *tests/testthat/_snaps/markdown-state.md 5484ca7c2b84f5fdee485071b6ea2843 *tests/testthat/_snaps/markdown.md b1c43986d37968bad69589d3b5436ebf *tests/testthat/_snaps/namespace.md 6d87127c3f780ee2afc8695c386704aa *tests/testthat/_snaps/object-format.md 45f4d22afc396372a74917316272d723 *tests/testthat/_snaps/object-from-call.md dac65f5cf5876a618ac29a84684f130f *tests/testthat/_snaps/object-import.md 4ca2da653576c5fd93645ef0c18dac59 *tests/testthat/_snaps/object-package.md c5196f40c98f940885f0c590a9770e4c *tests/testthat/_snaps/object-r6.md 9a24e2647f2de3184b7137bc022dedd1 *tests/testthat/_snaps/rd-describe-in.md 0f9479b3d20befb8d25c2cb0d60ebbde *tests/testthat/_snaps/rd-examples.md acc614ff6e5cd50209c39ff782056e1e *tests/testthat/_snaps/rd-family.md b1c8c57c30eeafc22249ba199f2f85be *tests/testthat/_snaps/rd-include-rmd.md 1d5d13e760330daf34c6fc29361698dd *tests/testthat/_snaps/rd-inherit.md 53e47f8ecb31eb7346377c3bd0f5c3f3 *tests/testthat/_snaps/rd-markdown.md cae6d19fe83f3b9b5cc25cc6e1118095 *tests/testthat/_snaps/rd-params.md 7d3f39af6ca2bc183d6460bd59c44d64 *tests/testthat/_snaps/rd-r6-class.md c21de30b6e5cb214d39491c5ecf2d88e *tests/testthat/_snaps/rd-r6-external.md fd8dbb0011f96344135ce26cca597bcf *tests/testthat/_snaps/rd-r6-field.md d0423b8d459dd41a9b4bf93787a4648d *tests/testthat/_snaps/rd-r6-methods-inherited.md fbe077b4b1bc1093c371c9e4a69f2e79 *tests/testthat/_snaps/rd-r6-methods-self.md 407c563b43f3cf1ad1e1a46d29c82374 *tests/testthat/_snaps/rd-r6-methods.md 5d06f1bd6805b269a7a21fb78476939a *tests/testthat/_snaps/rd-r6-super.md f19d636a950ad7e08ee03f12f487228f *tests/testthat/_snaps/rd-r6.md 50648766181aa026088a46b1969f1d58 *tests/testthat/_snaps/rd-raw.md 4c093a0c2b03c611458676adf3ed48df *tests/testthat/_snaps/rd-s4.md 3e63ab7443b3c36a93f741a81e7a1ab4 *tests/testthat/_snaps/rd-s7.md 8ee8dab346e24bb59229e45001c73428 *tests/testthat/_snaps/rd-section.md d572077bcf2a083bbef28a8e3eac0697 *tests/testthat/_snaps/rd-template.md 8418cbaabe49facc175ba169f1fb0c12 *tests/testthat/_snaps/rd-usage.md f44b3cbaa01d3e374622cb7b3fbb193a *tests/testthat/_snaps/rd.md 1e289b752b5ba8e63062d93c42e80e31 *tests/testthat/_snaps/roxygenize-needs.md a63455186ba906456a5104919c695dc5 *tests/testthat/_snaps/roxygenize-setup.md 2bed32627a994f55e06d4c2c87086eb1 *tests/testthat/_snaps/roxygenize.md 9e1f9ba42a50bce404618872920cbaa6 *tests/testthat/_snaps/select-args.md 7674b8e3baf6701b1c4ef696c58aa2b5 *tests/testthat/_snaps/tag-parser.md 9648a2fa905626ac83e5c691b270f3e9 *tests/testthat/_snaps/tag.md 196a134a7b778498e4e8fdd8bd183754 *tests/testthat/_snaps/topics.md 6d7e5f6fed6fd75d644b09271debde63 *tests/testthat/_snaps/utils-warn.md 6e9027c944a1e9e31b887cb01e715fbd *tests/testthat/_snaps/utils.md b0ea92dd4219c40e136ef265bd8489c5 *tests/testthat/broken-namespace/DESCRIPTION d797dae979c147108a67e2e838320d6b *tests/testthat/broken-namespace/NAMESPACE 5d7e2f495ad27ac5d47f703a08b981fe *tests/testthat/broken-namespace/R/x.R c8e3c693f5892d7c420ef06a58cec8af *tests/testthat/collate/belt.R 29edb1140b31a818b5514e75134b789e *tests/testthat/collate/jacket.R 8d463634a317b5ba8c0b5377d1a8e9e4 *tests/testthat/collate/pants.R ca35c56c0c379f292f8fab68b3a19f61 *tests/testthat/collate/shirt.R d1240ac026fdfb6e30bb2092caa57cc4 *tests/testthat/collate/shoes.R ca35c56c0c379f292f8fab68b3a19f61 *tests/testthat/collate/socks.R 1e1e34f6c314f9a3f28a0d8782494713 *tests/testthat/collate/tie.R ca35c56c0c379f292f8fab68b3a19f61 *tests/testthat/collate/undershorts.R 37baf697a226b7582659e1ffc440cee5 *tests/testthat/collate/watch.R f3bc4193339c6aa00cb00e9b65596a4c *tests/testthat/empty/DESCRIPTION ca35c56c0c379f292f8fab68b3a19f61 *tests/testthat/empty/R/empty-package.R 328347034d6b99d5fc52d553331869f8 *tests/testthat/escapes.Rd 2be5a63dd4469129533100f7cd712079 *tests/testthat/example.Rmd e379d876492b0bd73321a443cb94547b *tests/testthat/helper-test.R d41d8cd98f00b204e9800998ecf8427e *tests/testthat/made-by-roxygen/empty.Rd 8e73ac305e731a371f92350f610bed57 *tests/testthat/made-by-roxygen/with-header.Rd b5f24ca1515530c7e8ce1fcc85b671d4 *tests/testthat/made-by-roxygen/without-header.Rd 073104afbb7d5c6d43390603875598a4 *tests/testthat/man-roxygen/values.R 8b54e5a89fbda3af5e077053d40bec76 *tests/testthat/no-desc/NAMESPACE ca35c56c0c379f292f8fab68b3a19f61 *tests/testthat/no-desc/R/no-description.R 05573ff82dfeb474063ba887ff2b11aa *tests/testthat/roxygen-example-1.R d41d8cd98f00b204e9800998ecf8427e *tests/testthat/templates/man-roxygen/UCase.R d41d8cd98f00b204e9800998ecf8427e *tests/testthat/templates/man-roxygen/lcase.r 073104afbb7d5c6d43390603875598a4 *tests/testthat/templates/man/roxygen/templates/new-path.R e38af59d8c5d83701b288cbf64cfe7dd *tests/testthat/test-block.R fc0a770206f2ba851d232126bbdf549b *tests/testthat/test-collate.R eef746841e1a7f8d3046aa310e7848f0 *tests/testthat/test-isComplete.R 4b5689cbbba634613c8340ebd51c10f0 *tests/testthat/test-load.R 7c17eec72365ffff06657ddcea190232 *tests/testthat/test-markdown-code.R 8fc8ed246efd530b98017eb40ef0e71c *tests/testthat/test-markdown-link-resolve.R 4e058e270614a9421a5ef3d3f5a32752 *tests/testthat/test-markdown-link.R 0b6c1920d6a9bb30d7eef63715d948a1 *tests/testthat/test-markdown-state.R a80ab02a1a91a5e4d69b8c276231c757 *tests/testthat/test-markdown.R b80b920299351db74be90be4398f5928 *tests/testthat/test-namespace.R 91fc13c343f81cadbe49e60c6e864d63 *tests/testthat/test-object-defaults.R 17af45987d787a2a5bd99ad47ad342f5 *tests/testthat/test-object-format.R 300ce27226ab294d7e1e9a4f3d217c88 *tests/testthat/test-object-from-call.R a5d2107fea4f78d35f7c3470abb63852 *tests/testthat/test-object-import.R 288b13065380da2ffaa6ced106a20499 *tests/testthat/test-object-package.R 25d812f7a3858c8e775ef144400243b6 *tests/testthat/test-object-r6.R ccd01328c0b638e6335dbd32c7a70f78 *tests/testthat/test-object-rc.R f8d0273ecb76c8087927dda384d8bdd7 *tests/testthat/test-object-s3.R 11f1c46e27304e9f305f46ec7502fa03 *tests/testthat/test-options-both/DESCRIPTION 25129560cf86ae800a55b357dbf890d3 *tests/testthat/test-options-config/DESCRIPTION 664ce1e7e8576185ce71ea017d4a48d7 *tests/testthat/test-options.R 0becdf0926faf7bcfe1924d375f05a43 *tests/testthat/test-options/DESCRIPTION c0d9bd2424dbc167e528dedd53b98822 *tests/testthat/test-options/man/roxygen/meta.R ff338a7c1899a53efa682a196526ba6b *tests/testthat/test-options/meta-character.R 52a3c7d9ac1c4434e2f891e89491d6e7 *tests/testthat/test-options/meta-error.R 4d0c2825e67cee2503ce34339eeeb6de *tests/testthat/test-package_files.R e42b20f9c58c32b34c6e15723a674e3d *tests/testthat/test-parse.R 8298af91fb5a49967d86676f0d3fe9b5 *tests/testthat/test-rd-backref.R d894c24df4ffc3ca438359f179ea47b3 *tests/testthat/test-rd-describe-in.R feff2d0700ce0b34d81b80660101fae3 *tests/testthat/test-rd-examples.R 5e3f4f72a35b86bf3f5649ced2b8857f *tests/testthat/test-rd-family.R 0f91d8efc17caf6f895e24c1168fa435 *tests/testthat/test-rd-include-rmd.R 3aaf0b6c5eacebef365d33e405fb4b76 *tests/testthat/test-rd-inherit.R 13935a68393759220a2ed9679f780b83 *tests/testthat/test-rd-markdown-escaping.R 6913ccb654a4a58c1872e9bd0d828df4 *tests/testthat/test-rd-markdown.R b62bdfdf6e615d601efd0856112eb9ed *tests/testthat/test-rd-name-alias.R adfd9c377fba82d6b4c54f94f143e2a7 *tests/testthat/test-rd-params.R 2527c029d61a0b26e3293d7869dd22f9 *tests/testthat/test-rd-r6-class.R 194786ad17a62ac60533cd8b049b77ad *tests/testthat/test-rd-r6-external.R ca68d9d123a77aa186183eb0deb66ff8 *tests/testthat/test-rd-r6-field.R 4f1e3c5faf5785e6d2dd3c737a257ee2 *tests/testthat/test-rd-r6-methods-inherited.R 9a499a0257724c89b542f38c60edbef0 *tests/testthat/test-rd-r6-methods-self.R 8eb5060cb4e34a2babbf049971762948 *tests/testthat/test-rd-r6-methods.R 6600ca3fca98b83f1ad4f9a453cdbb62 *tests/testthat/test-rd-r6-super.R 4d69836207f4731d453e1372f5fec8f9 *tests/testthat/test-rd-r6.R 0bd735bfbbeb69c5ccf3afd8d1b798d9 *tests/testthat/test-rd-raw.R abb003f916fa91de00606e2f9518b395 *tests/testthat/test-rd-s4.R 94ecfbcb57a1c8fe40744cd6c4f537f1 *tests/testthat/test-rd-s7.R f6b0e18863006f027a66df1f9204c7e3 *tests/testthat/test-rd-section.R 49770aa15880eecf70f57257399e9174 *tests/testthat/test-rd-simple.R 2c5988cc6b5a8fc50fb683bae7ac1d28 *tests/testthat/test-rd-template.R 833fb89f11eac62202c4393a6cf97dbb *tests/testthat/test-rd-usage.R 3433fab0bd33d24b1423bb1d82518b11 *tests/testthat/test-rd.R 0010ce61ad6383afdbb34f27524ceb53 *tests/testthat/test-roxygenize-needs.R 39ff28ce0042aead436449754c3cd171 *tests/testthat/test-roxygenize-setup.R 1bb94eaf389703762788f39f7734c86e *tests/testthat/test-roxygenize.R 597557e126f6d36d1dabaf2db7d33dbd *tests/testthat/test-safety.R 8754392e2adf9522d2f56a289652b9d1 *tests/testthat/test-select-args.R d5e6893560295b7eaa7f95889265523b *tests/testthat/test-tag-metadata.R 4b98dc51e1d50f59b22eec3aa265cc1c *tests/testthat/test-tag-parser.R cdfed65dc046fc778a26e3920857378d *tests/testthat/test-tag.R cc728bbc9d11d52c3cfc4f187dd8fc57 *tests/testthat/test-tokenize.R 13b62df4a3bfa6c96f1f874a10f6c416 *tests/testthat/test-topic.R 7f64980a88ca48647f6fbeba7366b156 *tests/testthat/test-topics.R dda3ebdc4445bab0b96ad601c1253ad2 *tests/testthat/test-utils-io.R 3bc9f804f4bec5df30a1e9480413afed *tests/testthat/test-utils-rd.R 24469c728cce232d8f8bfc78533e6a1b *tests/testthat/test-utils-warn.R c838e242cd22223f88dad6d13b79ab4b *tests/testthat/test-utils.R 01cb1ff828a61822e4997c64c461ce88 *tests/testthat/testCollateNoIncludes/DESCRIPTION 012592abf4b40a8d741b299df2ded05a *tests/testthat/testCollateNoIncludes/NAMESPACE 1f3f7b97b56d21d1993d2951eec8541a *tests/testthat/testCollateNoIncludes/R/a.R 5dc6a5b47f4272a583e1a76ce68ae884 *tests/testthat/testCollateNoIncludes/R/b.R 79ed5a94dcd98a4ca9473eff4201f290 *tests/testthat/testCollateOverwrite/DESCRIPTION cb4f0cf07e53d57ddd5624ef9414ad2f *tests/testthat/testCollateOverwrite/R/a.R aa6661f9ad2794e7d2c6d8a36023e78b *tests/testthat/testCollateOverwrite/R/b.R 09795627d4c31c38e6b7e32364e1a3b8 *tests/testthat/testCollateParse/DESCRIPTION a6f8025f6e85cfe8050cf006d6eb320c *tests/testthat/testCollateParse/R/b.R a3afc17bc030e6f64689f885f96407d4 *tests/testthat/testCollateParse/R/c.R 38018efd31973c00e191d80641db81a2 *tests/testthat/testEagerData/DESCRIPTION 1c9a6674a40bfc331c5d06dabfd3b5a5 *tests/testthat/testEagerData/R/a.R 8b7270f2ffa38e863594416e35322ecb *tests/testthat/testEagerData/data/a.rda d8d8b6755004489343585533cb890a56 *tests/testthat/testLazyData/DESCRIPTION dc21c19f0d6968ee25d441b2cf46017d *tests/testthat/testLazyData/NAMESPACE 1c9a6674a40bfc331c5d06dabfd3b5a5 *tests/testthat/testLazyData/R/a.R 8b7270f2ffa38e863594416e35322ecb *tests/testthat/testLazyData/data/a.rda 9fcc2a4db31598b3cee291222b6d50e5 *tests/testthat/testMdLinks/DESCRIPTION 5d1a4a93b7a858d3d5af877cc03be184 *tests/testthat/testNamespace/DESCRIPTION b1b61b677cc8c9a85d50edffc7d21e74 *tests/testthat/testNamespace/NAMESPACE 38d13539cf9635b93a0810e1f452f44c *tests/testthat/testNamespace/R/a.R f79b5e26f0b92d1545bd22db4084a0ef *tests/testthat/testNonASCII/DESCRIPTION e4121e1e0ce965e8099ac75aaa7faca7 *tests/testthat/testNonASCII/R/a.R 8c35ab17cd30b89955a7848adfbe6652 *tests/testthat/testR6/DESCRIPTION 2e47d822fcff2cad1c97b82e3f4e6b03 *tests/testthat/testR6/R/classes.R dbd94e29feb226d99c617a8027993abc *tests/testthat/testR6/example.R 035afd94f6af182846ecdea37d661768 *tests/testthat/testRawNamespace/DESCRIPTION 47099257df52bac585873af9e3e45eef *tests/testthat/testRawNamespace/NAMESPACE ebbeb3944c797b87fef85fecb3f9132a *tests/testthat/testRawNamespace/R/a.R 796877413bf89f4760a687d3ecb0e053 *tests/testthat/testRbuildignore/DESCRIPTION 0b8f7598d11eec67e849a9d01d4adbe4 *tests/testthat/testRbuildignore/R/a.R 8ac0741e375b73b0582a2c6c1ee50296 *tests/testthat/testRbuildignore/R/ignore_me.R 816bcfd7d7d9023f71bcf15a55bc435b *tests/testthat/testUtf8Escape/DESCRIPTION d1e8df72ac79d2182b48f682ee7a4f38 *tests/testthat/testUtf8Escape/R/a.R 760653a2ca5e544bf90b858685229e17 *tests/testthat/up-to-date/DESCRIPTION cc78dd8e17fcc9728a27152a7848c4d5 *tests/testthat/up-to-date/R/foo.R d3c9265da409d2f6cc2f532de94b38e2 *vignettes/extending.Rmd c9a641c4fc37ad7dcebe2651ff178f94 *vignettes/index-crossref.Rmd effe663576e0d4ee0aeaeaeea93f1d61 *vignettes/namespace.Rmd 6f664436f18981af7c8261443d0da8bf *vignettes/rd-R6.Rmd 10b5cc54e9121c35116b475ed31155f0 *vignettes/rd-S3.Rmd 5cea2fef1c1a344ddae5de0189538e4e *vignettes/rd-S4.Rmd e724fb01a09c54746ad2ef8332f5f152 *vignettes/rd-S7.Rmd ca063377b1d36bbc7a78a7a3053d8d1f *vignettes/rd-datasets.Rmd 1bf7ae992ffffeead0a16d72f7ebeaf5 *vignettes/rd-formatting.Rmd 70f99b1a49b00d2210bad7e2b1034234 *vignettes/rd-functions.Rmd 31df33f6bad5a17cd35fdef8f28e5dab *vignettes/rd-packages.Rmd 536c0a5b883a0a584072d7a7d742ceef *vignettes/reuse.Rmd ec36253762b495957d8c04aa10c45d78 *vignettes/roxygen2.Rmd roxygen2/R/0000755000176200001440000000000015174453534012243 5ustar liggesusersroxygen2/R/rd-r6-methods-inherited.R0000644000176200001440000000431415157043550016726 0ustar liggesusersrd_r6_inherited <- function( package = character(), classname = character(), name = character() ) { structure( list(package = package, classname = classname, name = name), class = "rd_r6_inherited" ) } #' @export format.rd_r6_inherited <- function(x, ...) { if (length(x$name) == 0) { return() } details <- paste0( "Inherited methods" ) # Which parent classes are documented? cls <- unique(x$classname) pkgs <- x$package[match(cls, x$classname)] ht <- map2_lgl(cls, pkgs, has_topic) topic_ok <- ht[match(x$classname, cls)] # Build display components for each inherited method anchor <- sprintf("method-%s-%s", x$classname, x$name) self_pkg <- roxy_meta_get("current_package") %||% "" same_pkg <- x$package == self_pkg prefix <- ifelse(same_pkg, "", paste0(x$package, "::")) label <- sprintf("%s%s$%s()", prefix, x$classname, x$name) # Linked version: clickable reference to parent class docs href <- sprintf("../../%s/html/%s.html#%s", x$package, x$classname, anchor) data_attrs <- paste( sprintf('data-pkg="%s"', x$package), sprintf('data-topic="%s"', x$classname), sprintf('data-id="%s"', x$name) ) linked <- sprintf( '%s', data_attrs, href, label ) # Plain version: just the method name, no link plain <- sprintf("%s", label) items <- paste0("
  • ", ifelse(topic_ok, linked, plain), "
  • ") rd_if_html(paste( c(details, "
      ", items, "
    ", ""), collapse = "\n" )) } r6_extract_inherited_methods <- function(r6data) { super <- r6data$super if (is.null(super)) { return(rd_r6_inherited()) } super_meth <- super$members[super$members$type == "method", ] self <- r6data$self super_meth <- super_meth[!super_meth$name %in% self$name, ] super_meth <- super_meth[!duplicated(super_meth$name), ] if (nrow(super_meth) == 0) { return(rd_r6_inherited()) } super_meth <- super_meth[rev(seq_len(nrow(super_meth))), ] rd_r6_inherited( package = super_meth$package, classname = super_meth$classname, name = super_meth$name ) } roxygen2/R/rd-markdown.R0000644000176200001440000000752615166171276014626 0ustar liggesusers# Without sections -------------------------------------------------------- #' @export roxy_tag_parse.roxy_tag_author <- function(x) tag_markdown(x) #' @export roxy_tag_rd.roxy_tag_author <- function(x, base_path, env) { rd_section(x$tag, x$val) } #' @export format.rd_section_author <- function(x, ...) { format_collapse(x, ...) } #' @export merge.rd_section_author <- function(x, y, ...) { stopifnot(identical(class(x), class(y))) # Remove duplicated authors, e.g. when using @rdname rd_section(x$type, unique(c(x$value, y$value))) } #' @export roxy_tag_parse.roxy_tag_format <- function(x) tag_markdown(x) #' @export roxy_tag_rd.roxy_tag_format <- function(x, base_path, env) { rd_section(x$tag, x$val) } #' @export format.rd_section_format <- function(x, ...) { format_collapse(x, ...) } #' @export roxy_tag_parse.roxy_tag_note <- function(x) tag_markdown(x) #' @export roxy_tag_rd.roxy_tag_note <- function(x, base_path, env) { rd_section(x$tag, x$val) } #' @export format.rd_section_note <- function(x, ...) { format_collapse(x, ...) } #' @export roxy_tag_parse.roxy_tag_references <- function(x) tag_markdown(x) #' @export roxy_tag_rd.roxy_tag_references <- function(x, base_path, env) { rd_section(x$tag, x$val) } #' @export format.rd_section_references <- function(x, ...) { format_collapse(x, ...) } #' @export roxy_tag_parse.roxy_tag_return <- function(x) tag_markdown(x) #' @export roxy_tag_parse.roxy_tag_returns <- roxy_tag_parse.roxy_tag_return #' @export roxy_tag_rd.roxy_tag_return <- function(x, base_path, env) { rd_section("value", x$val) } #' @export roxy_tag_rd.roxy_tag_returns <- roxy_tag_rd.roxy_tag_return #' @export format.rd_section_value <- function(x, ...) { format_collapse(x, ...) } #' @export roxy_tag_parse.roxy_tag_seealso <- function(x) tag_markdown(x) #' @export roxy_tag_rd.roxy_tag_seealso <- function(x, base_path, env) { rd_section(x$tag, x$val) } #' @export format.rd_section_seealso <- function(x, ...) { format_collapse(x, ...) } #' @export merge.rd_section_seealso <- function(x, y, ...) { stopifnot(identical(class(x), class(y))) rd_section(x$type, unique(c(x$value, y$value))) } #' @export roxy_tag_parse.roxy_tag_source <- function(x) tag_markdown(x) #' @export roxy_tag_rd.roxy_tag_source <- function(x, base_path, env) { rd_section(x$tag, x$val) } #' @export format.rd_section_source <- function(x, ...) { format_collapse(x, ...) } #' @export roxy_tag_parse.roxy_tag_title <- function(x) { if (re_count(x$raw, "\n\n") >= 1) { warn_roxy_tag(x, "must be a single paragraph") } tag_markdown(x) } #' @export roxy_tag_rd.roxy_tag_title <- function(x, base_path, env) { rd_section(x$tag, x$val) } #' @export format.rd_section_title <- function(x, ...) { format_first(x, ...) } # With sections ----------------------------------------------------------- #' @export roxy_tag_parse.roxy_tag_description <- function(x) { tag_markdown_with_sections(x) } #' @export roxy_tag_rd.roxy_tag_description <- function(x, base_path, env) { rd_section_markdown(x$tag, x$val) } #' @export format.rd_section_description <- function(x, ...) { format_collapse(x, ...) } #' @export roxy_tag_parse.roxy_tag_details <- function(x) { tag_markdown_with_sections(x) } #' @export roxy_tag_rd.roxy_tag_details <- function(x, base_path, env) { rd_section_markdown(x$tag, x$val) } #' @export format.rd_section_details <- function(x, ...) { format_collapse(x, ...) } rd_section_markdown <- function(name, value) { # Any additional components are sections if (length(value) > 1) { titles <- names(value) value <- unname(value) name <- c(name, rep("section", length(value) - 1)) value <- c( list(value[[1]]), map2(titles[-1], value[-1], \(x, y) list(title = x, content = y)) ) if (value[[1]] == "") { name <- name[-1] value <- value[-1] } } map2(name, value, rd_section) } roxygen2/R/rd.R0000644000176200001440000001726515170121342012767 0ustar liggesusers#' Roclet: make Rd files #' #' @description #' This [roclet] automates the production of the `.Rd` files that #' R uses to document functions, datasets, packages, classes, and more. #' See `vignette("rd-functions")` for details. #' #' It is run by default by [roxygenize()]. #' #' @seealso [tags-rd-functions], [tags-rd-datasets], [tags-rd-S3], [tags-rd-S4], #' [tags-rd-S7], [tags-rd-R6], [tags-reuse], #' [tags-index-crossref] for tags provided by this roclet. #' @export #' @examples #' #' Add together two numbers #' #' @param x A number. #' #' @param y A number. #' #' @return A number. #' #' @export #' #' @examples #' #' add(1, 1) #' #' add(10, 1) #' add <- function(x, y) { #' x + y #' } rd_roclet <- function() { roclet("rd") } #' @export roclet_process.roclet_rd <- function(x, blocks, env, base_path) { blocks <- merge_external_r6methods(blocks) # Convert each block into a topic, indexed by filename topics <- RoxyTopics$new() for (block in blocks) { rd <- block_to_rd(block, base_path, env) topics$add(rd, block) } topics_process_r6_inherit(topics) topics_process_family(topics, env) topics_process_inherit(topics, env) topics$drop_invalid() topics_fix_params_order(topics) topics_add_default_description(topics) topics_add_package_alias(topics) topics$topics } #' @export roclet_output.roclet_rd <- function( x, results, base_path, ..., is_first = FALSE ) { man <- normalizePath(file.path(base_path, "man")) contents <- map_chr(results, format) paths <- file.path(man, names(results)) names <- unname(map_chr(results, \(x) x$get_name()[[1]])) if (length(names) > 0) { commands <- paste0("pkgload::dev_help('", names, "')") } else { commands <- character() } # Always check for roxygen2 header before overwriting NAMESPACE (#436), # even when running for the first time mapply(write_if_different, paths, contents, command = commands) if (!is_first) { # Automatically delete any files in man directory that were generated # by roxygen in the past, but weren't generated in this sweep. old_paths <- setdiff(dir(man, full.names = TRUE), paths) old_paths <- old_paths[!file.info(old_paths)$isdir] old_roxygen <- Filter(made_by_roxygen, old_paths) if (length(old_roxygen) > 0) { cli::cli_inform("Deleting {.file {basename(old_roxygen)}}") unlink(old_roxygen) } } paths } #' @export roclet_clean.roclet_rd <- function(x, base_path) { rd <- dir(file.path(base_path, "man"), full.names = TRUE) rd <- rd[!file.info(rd)$isdir] unlink(keep(rd, made_by_roxygen)) } # Does this block get an Rd file? needs_doc <- function(block) { if (block_has_tags(block, "noRd")) { return(FALSE) } block_has_tags( block, c( "description", "param", "return", "title", "example", "examples", "name", "rdname", "details", "inherit", "describeIn" ) ) } # Tag processing functions ------------------------------------------------ block_to_rd <- function(block, base_path, env) { UseMethod("block_to_rd") } #' @export block_to_rd.default <- function(block, ...) { cli::cli_abort("Unknown block type.", .internal = TRUE) } #' @export block_to_rd.roxy_block <- function(block, base_path, env) { # Must start by processing templates block <- process_templates(block, base_path) if (!needs_doc(block)) { return() } name <- block_get_tag(block, "name")$val %||% block$object$topic if (is.null(name)) { warn_roxy_block( block, c( "Block must have a @name", i = "Either document an existing object or manually specify with @name" ) ) return() } rd <- RoxyTopic$new() topic_add_name_aliases(rd, block, name) for (tag in block$tags) { rd$add(roxy_tag_rd(tag, env = env, base_path = base_path)) } if (rd$has_section("description") && rd$has_section("reexport")) { warn_roxy_block( block, "Block must not include a description when re-exporting a function" ) return() } describe_rdname <- topic_add_describe_in(rd, block, env) filename <- describe_rdname %||% block_get_tag(block, "rdname")$val %||% nice_name(name) rd$filename <- paste0(filename, ".Rd") rd } #' @export block_to_rd.roxy_block_r6class <- function(block, base_path, env) { r6on <- roxy_meta_get("r6", TRUE) if (!isTRUE(r6on)) { return(NextMethod()) } # Must start by processing templates block <- process_templates(block, base_path) if (!needs_doc(block)) { return() } name <- block_get_tag(block, "name")$val %||% block$object$topic if (is.null(name)) { warn_roxy_block(block, "must have a @name") return() } rd <- RoxyTopic$new() topic_add_name_aliases(rd, block, name) rd$add(roxy_tag_rd( block_get_tag(block, "name"), env = env, base_path = base_path )) rd$add(roxy_tag_rd( block_get_tag(block, "title"), env = env, base_path = base_path )) if (rd$has_section("description") && rd$has_section("reexport")) { warn_roxy_block( block, "Block must not include a description when re-exporting a function" ) return() } topic_add_r6_methods(rd, block, env, base_path) describe_rdname <- topic_add_describe_in(rd, block, env) filename <- describe_rdname %||% block_get_tag(block, "rdname")$val %||% nice_name(name) rd$filename <- paste0(filename, ".Rd") rd } # Special cases ----------------------------------------------------------- topics_add_default_description <- function(topics) { for (topic in topics$topics) { if (length(topic$get_section("description")) > 0) { next } # rexport manually generates a own description, so don't need to if ( !topic$has_section("reexport") && !identical(topic$get_value("docType"), "package") ) { topic$add(rd_section("description", topic$get_value("title"))) } } invisible() } topics_add_package_alias <- function(topics) { aliases <- unlist(topics$simple_values("alias"), use.names = FALSE) for (topic in topics$topics) { if (!identical(topic$get_value("docType"), "package")) { next } package <- topic$get_value("package") defaults <- c(package, package_suffix(package)) aliases <- union(setdiff(defaults, aliases), topic$get_value("alias")) topic$add(rd_section("alias", aliases), overwrite = TRUE) break } invisible(NULL) } # Tag-wise processing ----------------------------------------------------- #' Generate Rd output from a tag #' #' Provide a method for this generic if you want a tag to generate output #' in `.Rd` files. See `vignette("extending")` for more details. #' #' @param x The tag #' @param base_path Path to package root directory. #' @param env Environment in which to evaluate code (if needed) #' @return Methods must return a [rd_section]. #' @export #' @family extending roxy_tag_rd <- function(x, base_path, env) { UseMethod("roxy_tag_rd") } #' @export roxy_tag_rd.default <- function(x, base_path, env) {} # Special tags ------------------------------------------------------------ # These tags do not directly affect the output, and are no complicated enough # to require their own files. #' @export roxy_tag_rd.roxy_tag_.formals <- function(x, base_path, env) { rd_section("formals", x$val) } #' @export format.rd_section_formals <- function(x, ...) NULL #' @export roxy_tag_rd.roxy_tag_.package <- function(x, base_path, env) { rd_section("package", x$val) } #' @export format.rd_section_package <- function(x, ...) NULL #' @export roxy_tag_parse.roxy_tag_method <- function(x) tag_words(x, 2, 2) #' @export roxy_tag_parse.roxy_tag_noRd <- function(x) tag_toggle(x) #' @export roxy_tag_parse.roxy_tag_rdname <- function(x) tag_value(x) roxygen2/R/util-locale.R0000644000176200001440000000061614550054451014573 0ustar liggesusersset_collate <- function(locale) { cur <- Sys.getlocale(category = "LC_COLLATE") Sys.setlocale(category = "LC_COLLATE", locale = locale) cur } with_collate <- function(locale, code) { old <- set_collate(locale) on.exit(set_collate(old)) force(code) } sort_c <- function(x, ...) { with_collate("C", sort(x, ...)) } order_c <- function(x, ...) { with_collate("C", order(x, ...)) } roxygen2/R/import-standalone-types-check.R0000644000176200001440000002531015154546446020247 0ustar liggesusers# Standalone file: do not edit by hand # Source: https://github.com/r-lib/rlang/blob/HEAD/R/standalone-types-check.R # Generated by: usethis::use_standalone("r-lib/rlang", "types-check") # ---------------------------------------------------------------------- # # --- # repo: r-lib/rlang # file: standalone-types-check.R # last-updated: 2023-03-13 # license: https://unlicense.org # dependencies: standalone-obj-type.R # imports: rlang (>= 1.1.0) # --- # # ## Changelog # # 2025-09-19: # - `check_logical()` gains an `allow_na` argument (@jonthegeek, #1724) # # 2024-08-15: # - `check_character()` gains an `allow_na` argument (@martaalcalde, #1724) # # 2023-03-13: # - Improved error messages of number checkers (@teunbrand) # - Added `allow_infinite` argument to `check_number_whole()` (@mgirlich). # - Added `check_data_frame()` (@mgirlich). # # 2023-03-07: # - Added dependency on rlang (>= 1.1.0). # # 2023-02-15: # - Added `check_logical()`. # # - `check_bool()`, `check_number_whole()`, and # `check_number_decimal()` are now implemented in C. # # - For efficiency, `check_number_whole()` and # `check_number_decimal()` now take a `NULL` default for `min` and # `max`. This makes it possible to bypass unnecessary type-checking # and comparisons in the default case of no bounds checks. # # 2022-10-07: # - `check_number_whole()` and `_decimal()` no longer treat # non-numeric types such as factors or dates as numbers. Numeric # types are detected with `is.numeric()`. # # 2022-10-04: # - Added `check_name()` that forbids the empty string. # `check_string()` allows the empty string by default. # # 2022-09-28: # - Removed `what` arguments. # - Added `allow_na` and `allow_null` arguments. # - Added `allow_decimal` and `allow_infinite` arguments. # - Improved errors with absent arguments. # # # 2022-09-16: # - Unprefixed usage of rlang functions with `rlang::` to # avoid onLoad issues when called from rlang (#1482). # # 2022-08-11: # - Added changelog. # # nocov start # Scalars ----------------------------------------------------------------- .standalone_types_check_dot_call <- .Call check_bool <- function( x, ..., allow_na = FALSE, allow_null = FALSE, arg = caller_arg(x), call = caller_env() ) { if ( !missing(x) && .standalone_types_check_dot_call( ffi_standalone_is_bool_1.0.7, x, allow_na, allow_null ) ) { return(invisible(NULL)) } stop_input_type( x, c("`TRUE`", "`FALSE`"), ..., allow_na = allow_na, allow_null = allow_null, arg = arg, call = call ) } check_string <- function( x, ..., allow_empty = TRUE, allow_na = FALSE, allow_null = FALSE, arg = caller_arg(x), call = caller_env() ) { if (!missing(x)) { is_string <- .rlang_check_is_string( x, allow_empty = allow_empty, allow_na = allow_na, allow_null = allow_null ) if (is_string) { return(invisible(NULL)) } } stop_input_type( x, "a single string", ..., allow_na = allow_na, allow_null = allow_null, arg = arg, call = call ) } .rlang_check_is_string <- function(x, allow_empty, allow_na, allow_null) { if (is_string(x)) { if (allow_empty || !is_string(x, "")) { return(TRUE) } } if (allow_null && is_null(x)) { return(TRUE) } if (allow_na && (identical(x, NA) || identical(x, na_chr))) { return(TRUE) } FALSE } check_name <- function( x, ..., allow_null = FALSE, arg = caller_arg(x), call = caller_env() ) { if (!missing(x)) { is_string <- .rlang_check_is_string( x, allow_empty = FALSE, allow_na = FALSE, allow_null = allow_null ) if (is_string) { return(invisible(NULL)) } } stop_input_type( x, "a valid name", ..., allow_na = FALSE, allow_null = allow_null, arg = arg, call = call ) } IS_NUMBER_true <- 0 IS_NUMBER_false <- 1 IS_NUMBER_oob <- 2 check_number_decimal <- function( x, ..., min = NULL, max = NULL, allow_infinite = TRUE, allow_na = FALSE, allow_null = FALSE, arg = caller_arg(x), call = caller_env() ) { if (missing(x)) { exit_code <- IS_NUMBER_false } else if ( 0 == (exit_code <- .standalone_types_check_dot_call( ffi_standalone_check_number_1.0.7, x, allow_decimal = TRUE, min, max, allow_infinite, allow_na, allow_null )) ) { return(invisible(NULL)) } .stop_not_number( x, ..., exit_code = exit_code, allow_decimal = TRUE, min = min, max = max, allow_na = allow_na, allow_null = allow_null, arg = arg, call = call ) } check_number_whole <- function( x, ..., min = NULL, max = NULL, allow_infinite = FALSE, allow_na = FALSE, allow_null = FALSE, arg = caller_arg(x), call = caller_env() ) { if (missing(x)) { exit_code <- IS_NUMBER_false } else if ( 0 == (exit_code <- .standalone_types_check_dot_call( ffi_standalone_check_number_1.0.7, x, allow_decimal = FALSE, min, max, allow_infinite, allow_na, allow_null )) ) { return(invisible(NULL)) } .stop_not_number( x, ..., exit_code = exit_code, allow_decimal = FALSE, min = min, max = max, allow_na = allow_na, allow_null = allow_null, arg = arg, call = call ) } .stop_not_number <- function( x, ..., exit_code, allow_decimal, min, max, allow_na, allow_null, arg, call ) { if (allow_decimal) { what <- "a number" } else { what <- "a whole number" } if (exit_code == IS_NUMBER_oob) { min <- min %||% -Inf max <- max %||% Inf if (min > -Inf && max < Inf) { what <- sprintf("%s between %s and %s", what, min, max) } else if (x < min) { what <- sprintf("%s larger than or equal to %s", what, min) } else if (x > max) { what <- sprintf("%s smaller than or equal to %s", what, max) } else { abort("Unexpected state in OOB check", .internal = TRUE) } } stop_input_type( x, what, ..., allow_na = allow_na, allow_null = allow_null, arg = arg, call = call ) } check_symbol <- function( x, ..., allow_null = FALSE, arg = caller_arg(x), call = caller_env() ) { if (!missing(x)) { if (is_symbol(x)) { return(invisible(NULL)) } if (allow_null && is_null(x)) { return(invisible(NULL)) } } stop_input_type( x, "a symbol", ..., allow_na = FALSE, allow_null = allow_null, arg = arg, call = call ) } check_arg <- function( x, ..., allow_null = FALSE, arg = caller_arg(x), call = caller_env() ) { if (!missing(x)) { if (is_symbol(x)) { return(invisible(NULL)) } if (allow_null && is_null(x)) { return(invisible(NULL)) } } stop_input_type( x, "an argument name", ..., allow_na = FALSE, allow_null = allow_null, arg = arg, call = call ) } check_call <- function( x, ..., allow_null = FALSE, arg = caller_arg(x), call = caller_env() ) { if (!missing(x)) { if (is_call(x)) { return(invisible(NULL)) } if (allow_null && is_null(x)) { return(invisible(NULL)) } } stop_input_type( x, "a defused call", ..., allow_na = FALSE, allow_null = allow_null, arg = arg, call = call ) } check_environment <- function( x, ..., allow_null = FALSE, arg = caller_arg(x), call = caller_env() ) { if (!missing(x)) { if (is_environment(x)) { return(invisible(NULL)) } if (allow_null && is_null(x)) { return(invisible(NULL)) } } stop_input_type( x, "an environment", ..., allow_na = FALSE, allow_null = allow_null, arg = arg, call = call ) } check_function <- function( x, ..., allow_null = FALSE, arg = caller_arg(x), call = caller_env() ) { if (!missing(x)) { if (is_function(x)) { return(invisible(NULL)) } if (allow_null && is_null(x)) { return(invisible(NULL)) } } stop_input_type( x, "a function", ..., allow_na = FALSE, allow_null = allow_null, arg = arg, call = call ) } check_closure <- function( x, ..., allow_null = FALSE, arg = caller_arg(x), call = caller_env() ) { if (!missing(x)) { if (is_closure(x)) { return(invisible(NULL)) } if (allow_null && is_null(x)) { return(invisible(NULL)) } } stop_input_type( x, "an R function", ..., allow_na = FALSE, allow_null = allow_null, arg = arg, call = call ) } check_formula <- function( x, ..., allow_null = FALSE, arg = caller_arg(x), call = caller_env() ) { if (!missing(x)) { if (is_formula(x)) { return(invisible(NULL)) } if (allow_null && is_null(x)) { return(invisible(NULL)) } } stop_input_type( x, "a formula", ..., allow_na = FALSE, allow_null = allow_null, arg = arg, call = call ) } # Vectors ----------------------------------------------------------------- # TODO: Figure out what to do with logical `NA` and `allow_na = TRUE` check_character <- function( x, ..., allow_na = TRUE, allow_null = FALSE, arg = caller_arg(x), call = caller_env() ) { if (!missing(x)) { if (is_character(x)) { if (!allow_na && any(is.na(x))) { abort( sprintf("`%s` can't contain NA values.", arg), arg = arg, call = call ) } return(invisible(NULL)) } if (allow_null && is_null(x)) { return(invisible(NULL)) } } stop_input_type( x, "a character vector", ..., allow_null = allow_null, arg = arg, call = call ) } check_logical <- function( x, ..., allow_na = TRUE, allow_null = FALSE, arg = caller_arg(x), call = caller_env() ) { if (!missing(x)) { if (is_logical(x)) { if (!allow_na && any(is.na(x))) { abort( sprintf("`%s` can't contain NA values.", arg), arg = arg, call = call ) } return(invisible(NULL)) } if (allow_null && is_null(x)) { return(invisible(NULL)) } } stop_input_type( x, "a logical vector", ..., allow_na = FALSE, allow_null = allow_null, arg = arg, call = call ) } check_data_frame <- function( x, ..., allow_null = FALSE, arg = caller_arg(x), call = caller_env() ) { if (!missing(x)) { if (is.data.frame(x)) { return(invisible(NULL)) } if (allow_null && is_null(x)) { return(invisible(NULL)) } } stop_input_type( x, "a data frame", ..., allow_null = allow_null, arg = arg, call = call ) } # nocov end roxygen2/R/parse.R0000644000176200001440000000520715165177023013500 0ustar liggesusers#' Parse a package, file, or inline code #' #' `parse_package()`, `parse_file()`, and `parse_text()` allow you to use #' roxygen's parsing code to parse the roxygen blocks from a package, file, or #' character vector of code. `env_package()` and `env_file()` provide #' defaults that generate a temporary environment making it possible to #' associate each block with the corresponding live object. #' #' @param path,file,text Either specify a `path` to the root directory of #' a package, an R `file`, or a character vector `text`. #' @param env An environment environment containing the result of evaluating #' the input code. The defaults will do this for you in a test environment: #' for real code you'll need to generate the environment yourself. #' #' You can also set to `NULL` if you only want to get the tokenized code #' blocks only. This suppresses evaluation of `@eval` tags, and will not #' find the code object associated with each block. #' @return A list of roxy_block objects #' @export #' @family extending parse_package <- function(path = ".", env = env_package(path)) { files <- package_files(path) list_of_blocks <- lapply(files, tokenize_file) blocks <- unlist(list_of_blocks, recursive = FALSE) if (!is.null(env)) { blocks <- lapply(blocks, block_set_env, env = env) } blocks <- order_blocks(blocks) blocks } #' @export #' @rdname parse_package #' @param srcref_path Path to be used as source ref. parse_file <- function(file, env = env_file(file), srcref_path = NULL) { blocks <- tokenize_file(file, srcref_path = srcref_path) if (!is.null(env)) { blocks <- lapply(blocks, block_set_env, env = env) } blocks <- order_blocks(blocks) blocks } #' @export #' @rdname parse_package parse_text <- function(text, env = env_file(file)) { file <- tempfile() write_lines(text, file) on.exit(unlink(file)) blocks <- parse_file(file, env = env, srcref_path = "") blocks } #' @export #' @rdname parse_package env_file <- function(file) { env <- new.env(parent = parent.env(globalenv())) methods::setPackageName("roxygen_devtest", env) sys.source(file, envir = env, keep.source = TRUE) env } #' @export #' @rdname parse_package env_package <- function(path) { load_pkgload(path) } # helpers ----------------------------------------------------------------- order_blocks <- function(blocks) { block_order <- function(x) { if (block_has_tags(x, "order")) { ord <- block_get_tag_value(x, "order") as.double(ord) } else { Inf } } ord <- map_dbl(blocks, block_order) blocks[order(ord)] } #' @export roxy_tag_parse.roxy_tag_order <- function(x) { tag_value(x) } roxygen2/R/topics.R0000644000176200001440000000461415151411610013654 0ustar liggesusers# Manage a list of topics, indexed by file name. # Adding a topic with an existing file name merges it with the existing topic RoxyTopics <- R6::R6Class( "RoxyTopics", public = list( topics = list(), add = function(topic, block) { if (is.null(topic)) { return() } stopifnot(inherits(topic, "RoxyTopic")) filename <- topic$filename if (filename %in% names(self$topics)) { self$topics[[filename]]$add(topic, block) } else { self$topics[[filename]] <- topic } invisible() }, # Drop any topics that don't have a title drop_invalid = function() { for (topic in names(self$topics)) { if (!self$topics[[topic]]$is_valid()) { warn_roxy_topic(topic, "Skipping; no name and/or title") self$topics[[topic]] <- NULL } } invisible() }, get = function(filename) { self$topics[[filename]] }, # Given a topic name, find its file name. find_filename = function(name) { for (i in seq_along(self$topics)) { if (name %in% self$topics[[i]]$get_value("name")) { return(names(self$topics)[[i]]) } } NA_character_ }, # Topologically sort the topics. # # @param deps A function. Is passed RoxyTopic, and should return a character # vector of topic names topo_order = function(dependencies) { topo <- TopoSort$new() for (i in seq_along(self$topics)) { name <- names(self$topics)[[i]] topo$add(name) dep_topics <- dependencies(self$topics[[i]]) for (dep_topic in dep_topics) { dep_rd <- self$find_filename(dep_topic) if (!is.na(dep_rd)) { topo$add_ancestor(name, dep_rd) } } } topo$sort() }, # Call fun in topological order defined by dep. topo_apply = function(dep, fun, ...) { topics_topo <- self$topo_order(dep) for (topic_name in topics_topo) { topic <- self$get(topic_name) fun(topic, self, ...) } invisible() }, apply = function(fun, ...) { for (topic in self$topics) { fun(topic, self, ...) } invisible() }, # Extract values for simple fields simple_values = function(field) { fields <- lapply(self$topics, \(rd) rd$get_section(field)) lapply(compact(fields), "[[", "value") } ) ) roxygen2/R/rd-backref.R0000644000176200001440000000107315163737466014376 0ustar liggesusers#' @export roxy_tag_parse.roxy_tag_backref <- function(x) { tag_value(x) } #' @export roxy_tag_rd.roxy_tag_backref <- function(x, base_path, env) { rd_section("backref", x$val) } #' @export format.rd_section_backref <- function(x, ...) { filename <- unique(x$value) filename <- file.path( basename(dirname(filename)), basename(filename), fsep = "/" ) lines <- strwrap( paste0("Please edit documentation in ", paste(filename, collapse = ", ")), initial = "% ", prefix = "% ", width = 80 ) paste0(lines, collapse = "\n") } roxygen2/R/markdown.R0000644000176200001440000003633215174170772014217 0ustar liggesusersmarkdown <- function(text, tag = NULL, sections = FALSE) { tag <- tag %||% list(file = NA, line = NA) expanded_text <- tryCatch( markdown_evaluate(text), error = function(e) { warn_roxy_tag(tag, "failed to evaluate inline markdown code", parent = e) text } ) escaped_text <- escape_rd_for_md(expanded_text) tryCatch( markdown_pass2(escaped_text, tag = tag, sections = sections), error = function(e) { warn_roxy_tag(tag, "markdown failed to process", parent = e) text } ) } #' Expand the embedded inline code #' #' @details #' For example this becomes two: `r 1+1`. #' Variables can be set and then reused, within the same #' tag: `r x <- 100; NULL` #' The value of `x` is `r x`. #' #' We have access to the internal functions of the package, e.g. #' since this is _roxygen2_, we can refer to the internal `markdown` #' function, and this is `TRUE`: `r is.function(markdown)`. #' #' To insert the name of the current package: `r packageName()`. #' #' The `iris` data set has `r ncol(iris)` columns: #' `r paste0("\x60\x60", colnames(iris), "\x60\x60", collapse = ", ")`. #' #' ```{r} #' # Code block demo #' x + 1 #' ``` #' #' Chunk options: #' #' ```{r results = "hold"} #' names(mtcars) #' nrow(mtcars) #' ``` #' #' Plots: #' #' ```{r test-figure} #' plot(1:10) #' ``` #' #' Alternative knitr engines: #' #' ```{verbatim} #' #| file = "tests/testthat/example.Rmd" #' ``` #' #' Also see `vignette("rd-formatting")`. #' #' @param text Input text. #' @return #' Text with R code expanded. #' A character vector of the same length as the input `text`. #' #' @keywords internal markdown_pass1 <- function(text) { text <- paste(text, collapse = "\n") mdxml <- xml_ns_strip(md_to_mdxml(text, sourcepos = TRUE)) code_nodes <- xml_find_all(mdxml, ".//code | .//code_block") rcode_nodes <- keep(code_nodes, is_markdown_code_node) if (length(rcode_nodes) == 0) { return(text) } rcode_pos <- parse_md_pos(map_chr(rcode_nodes, xml_attr, "sourcepos")) rcode_pos <- work_around_cmark_sourcepos_bug(text, rcode_pos) out <- eval_code_nodes(rcode_nodes) re_set_all_pos(text, rcode_pos, out, rcode_nodes) } # Work around commonmark sourcepos bug for inline R code # https://github.com/r-lib/roxygen2/issues/1353 work_around_cmark_sourcepos_bug <- function(text, rcode_pos) { if (Sys.getenv("ROXYGEN2_NO_SOURCEPOS_WORKAROUND", "") != "") { return(rcode_pos) } lines <- strsplit(text, "\n", fixed = TRUE)[[1]] for (l in seq_len(nrow(rcode_pos))) { # Do not try to fix multi-line code, we error for that (below) if (rcode_pos$start_line[l] != rcode_pos$end_line[l]) { next } line <- lines[rcode_pos$start_line[l]] start <- rcode_pos$start_column[l] # Maybe correct? At some point this will be fixed upstream, hopefully. if (substr(line, start - 1, start + 1) == "`r ") { next } # Maybe indented and we can shift it? # It is possible that the shift that we try accidentally matches # "`r ", but it seems to be extremely unlikely. An example is this: # #' ``1`r `` `r 22*10` # (seven spaces after the #', so an indent of six spaces. If we shift # the real "`r " left by six characters, there happens to be another # "`r " there. m <- regexpr("^[ ]+", line) indent <- attr(m, "match.length") if ( m > 0L && substr(line, start - 1 + indent, start + 1 + indent) == "`r " ) { rcode_pos$start_column[l] <- rcode_pos$start_column[l] + indent rcode_pos$end_column[l] <- rcode_pos$end_column[l] + indent } } rcode_pos } is_markdown_code_node <- function(x) { info <- xml_attr(x, "info") substr(xml_text(x), 1, 2) == "r " || (!is.na(info) && grepl("^[{][a-zA-z]+[}, ]", info)) } parse_md_pos <- function(text) { nums <- map(strsplit(text, "[:-]"), as.integer) data.frame( start_line = map_int(nums, \(x) x[[1]]), start_column = map_int(nums, \(x) x[[2]]), end_line = map_int(nums, \(x) x[[3]]), end_column = map_int(nums, \(x) x[[4]]) ) } eval_code_nodes <- function(nodes) { evalenv <- roxy_meta_get("evalenv") # This should only happen in our test cases if (is.null(evalenv)) { evalenv <- new.env(parent = baseenv()) } map_chr(nodes, eval_code_node, env = evalenv) } eval_code_node <- function(node, env) { if (xml_name(node) == "code") { # write knitr markup for inline code text <- paste0("`", xml_text(node), "`") } else { lang <- xml_attr(node, "info") # write knitr markup for fenced code text <- paste0("```", if (!is.na(lang)) lang, "\n", xml_text(node), "```\n") } chunk_opts <- utils::modifyList( knitr_chunk_defaults(), as.list(roxy_meta_get("knitr_chunk_options", NULL)) ) roxy_knit(text, env, chunk_opts) } knitr_chunk_defaults <- function() { list( error = FALSE, fig.path = "man/figures/", fig.process = basename, comment = "#>", collapse = TRUE ) } re_set_all_pos <- function(text, pos, value, nodes) { # Cmark has a bug when reporting source positions for multi-line # code tags, and it does not count the indenting space in the # continuation lines: https://github.com/commonmark/cmark/issues/296 types <- xml_name(nodes) if (any(types == "code" & pos$start_line != pos$end_line)) { cli::cli_abort("Multi-line `r ` markup is not supported.", call = NULL) } # Need to split the string, because of the potential multi-line # code tags, and then also recode the positions lens <- nchar(strsplit(text, "\n", fixed = TRUE)[[1]]) shifts <- c(0, cumsum(lens + 1L)) shifts <- shifts[-length(shifts)] start <- shifts[pos$start_line] + pos$start_column end <- shifts[pos$end_line] + pos$end_column # Create intervals for the parts we keep keep_start <- c(1, end + 2L) keep_end <- c(start - 2L, nchar(text)) # Now piece them together out <- paste0( substring(text, keep_start, keep_end), c(value, ""), collapse = "" ) attributes(out) <- attributes(text) out } markdown_pass2 <- function(text, tag = NULL, sections = FALSE) { esc_text_linkrefs <- add_linkrefs_to_md(text) mdxml <- md_to_mdxml(esc_text_linkrefs) state <- new.env(parent = emptyenv()) state$tag <- tag state$has_sections <- sections rd <- mdxml_children_to_rd_top(mdxml, state) map_chr(rd, unescape_rd_for_md, text) } md_to_mdxml <- function(x, ...) { md <- commonmark::markdown_xml( x, hardbreaks = TRUE, extensions = "table", ... ) xml2::read_xml(md) } mdxml_children_to_rd_top <- function(xml, state) { state$section_tag <- uuid() out <- map_chr(xml_children(xml), mdxml_node_to_rd, state) out <- c(out, mdxml_close_sections(state)) rd <- trimws(paste0(out, collapse = "")) if (state$has_sections) { secs <- strsplit(rd, state$section_tag, fixed = TRUE)[[1]] %||% "" titles <- c("", state$titles) # strsplit drops trailing empty strings, so pad to match titles length secs <- c(secs, rep("", length(titles) - length(secs))) rd <- structure(trimws(secs), names = titles) } rd } mdxml_children_to_rd <- function(xml, state) { out <- map_chr(xml_children(xml), mdxml_node_to_rd, state) paste0(out, collapse = "") } mdxml_node_to_rd <- function(xml, state) { if ( !inherits(xml, "xml_node") || !xml_type(xml) %in% c("text", "element") ) { warn_roxy_tag( state$tag, c( "markdown translation failed", x = "Unexpected internal error", i = "Please file an issue at https://github.com/r-lib/roxygen2/issues" ) ) return("") } switch( xml_name(xml), html = , document = , unknown = mdxml_children_to_rd(xml, state), paragraph = paste0("\n\n", mdxml_children_to_rd(xml, state)), text = if (is_true(state$in_link_code)) { escape_verb(xml_text(xml)) } else { escape_comment(xml_text(xml)) }, emph = paste0("\\emph{", mdxml_children_to_rd(xml, state), "}"), strong = paste0("\\strong{", mdxml_children_to_rd(xml, state), "}"), softbreak = mdxml_break(state), linebreak = mdxml_break(state), code = mdxml_code(xml, state), code_block = mdxml_code_block(xml, state), table = mdxml_table(xml, state), list = mdxml_list(xml, state), item = mdxml_item(xml, state), link = mdxml_link(xml, state), image = mdxml_image(xml), heading = mdxml_heading(xml, state), # Only supported when including Rmds html_block = mdxml_html_block(xml, state), html_inline = mdxml_html_inline(xml, state), # Not supported block_quote = mdxml_unsupported(xml, state$tag, "block quotes"), thematic_break = mdxml_unsupported(xml, state$tag, "horizontal rules"), mdxml_unknown(xml, state$tag) ) } mdxml_unknown <- function(xml, tag) { warn_roxy_tag( tag, c( "markdown translation failed", x = "Internal error: unknown xml node {xml_name(xml)}", i = "Please file an issue at https://github.com/r-lib/roxygen2/issues" ) ) escape_comment(xml_text(xml)) } mdxml_unsupported <- function(xml, tag, feature) { warn_roxy_tag( tag, c( "markdown translation failed", x = "{feature} are not currently supported" ) ) escape_comment(xml_text(xml)) } mdxml_break <- function(state) { if (isTRUE(state$inlink)) " " else "\n" } mdxml_code <- function(xml, tag) { code <- xml_text(xml) if (grepl("^Rd ", code)) { paste0( "\\Sexpr[stage=render,results=rd]{", substr(code, 4, nchar(code)), "}" ) } else if (can_parse(code) || code %in% special) { # See escaping details at # https://cran.rstudio.com/doc/manuals/r-devel/R-exts.html#Insertions paste0("\\code{", gsub("%", "\\\\%", code), "}") } else { paste0("\\verb{", escape_verb(code), "}") } } special <- c( "-", ":", "::", ":::", "!", "!=", "(", "[", "[[", "@", "*", "/", "&", "&&", "%*%", "%/%", "%%", "%in%", "%o%", "%x%", "^", "+", "<", "<=", "=", "==", ">", ">=", "|", "||", "~", "$", "for", "function", "if", "repeat", "while" ) mdxml_code_block <- function(xml, state) { info <- xml_attr(xml, "info", default = "")[1] if (nchar(info[1]) == 0) { info <- NA_character_ } paste0( "\n\n", "\\if{html}{\\out{
    }}", "\\preformatted{", escape_verb(xml_text(xml)), "}", "\\if{html}{\\out{
    }}" ) } can_parse <- function(x) { tryCatch( { parse_expr(x) TRUE }, error = function(x) FALSE ) } escape_verb <- function(x) { # Don't need to escape \\ because that's already handled in double_escape_md() x <- gsub("%", "\\%", x, fixed = TRUE) x <- gsub("{", "\\{", x, fixed = TRUE) x <- gsub("}", "\\}", x, fixed = TRUE) x } mdxml_table <- function(xml, state) { head <- xml_children(xml)[[1]] align <- substr(xml_attr(xml_children(head), "align", default = "left"), 1, 1) rows <- xml_find_all(xml, "d1:table_row|d1:table_header") cells <- map(rows, xml_find_all, "d1:table_cell") cells_rd <- map(cells, \(x) map(x, mdxml_children_to_rd, state = state)) rows_rd <- map_chr(cells_rd, paste0, collapse = " \\tab ") paste0( "\\tabular{", paste(align, collapse = ""), "}{\n", paste(" ", rows_rd, "\\cr\n", collapse = ""), "}\n" ) } # A list, either bulleted or numbered mdxml_list <- function(xml, state) { type <- xml_attr(xml, "type") if (type == "ordered") { paste0("\n\\enumerate{", mdxml_children_to_rd(xml, state), "\n}") } else { paste0("\n\\itemize{", mdxml_children_to_rd(xml, state), "\n}") } } mdxml_item <- function(xml, state) { ## A single item within a list. We remove the first paragraph ## tag, to avoid an empty line at the beginning of the first item. children <- xml_children(xml) if (length(children) == 0) { cnts <- "" } else if (xml_name(children[[1]]) == "paragraph") { cnts <- paste0( mdxml_children_to_rd(children[[1]], state), paste0(map_chr(children[-1], mdxml_node_to_rd, state), collapse = "") ) } else { cnts <- mdxml_children_to_rd(xml, state) } paste0("\n\\item ", cnts) } mdxml_link <- function(xml, state) { ## Hyperlink, this can also be a link to a function dest <- xml_attr(xml, "destination") contents <- xml_contents(xml) link <- parse_link(dest, contents, state) if (!is.null(link)) { link } else if (dest == "" || dest == xml_text(xml)) { paste0("\\url{", escape_comment(xml_text(xml)), "}") } else { paste_c( c("\\href{", escape_comment(dest), "}"), c("{", mdxml_link_text(contents, state), "}") ) } } mdxml_link_text <- function(xml_contents, state) { # Newlines in markdown get converted to softbreaks/linebreaks by # markdown_xml(), which then get interpreted as empty strings by # xml_text(). So we preserve newlines as spaces. inlink <- state$inlink on.exit(state$inlink <- inlink, add = TRUE) state$inlink <- TRUE text <- map_chr(xml_contents, mdxml_node_to_rd, state) paste0(text, collapse = "") } mdxml_image <- function(xml) { dest <- xml_attr(xml, "destination") title <- xml_attr(xml, "title") fmt <- get_image_format(dest) paste0( if (fmt == "html") "\\if{html}{", if (fmt == "pdf") "\\if{pdf}{", "\\figure{", dest, "}", if (nchar(title)) paste0("{", title, "}"), if (fmt %in% c("html", "pdf")) "}" ) } get_image_format <- function(path) { should_restrict <- roxy_meta_get("restrict_image_formats") %||% TRUE if (!should_restrict) { return("all") } path <- tolower(path) rx <- default_image_formats() html <- grepl(rx$html, path) pdf <- grepl(rx$pdf, path) if (html && pdf) { "all" } else if (html) { "html" } else if (pdf) { "pdf" } else { "all" } } default_image_formats <- function() { list( html = "[.](jpg|jpeg|gif|png|svg)$", pdf = "[.](jpg|jpeg|gif|png|pdf)$" ) } escape_comment <- function(x) { gsub("%", "\\%", x, fixed = TRUE) } mdxml_heading <- function(xml, state) { level <- xml_attr(xml, "level") if (!state$has_sections && level == 1) { warn_roxy_tag( state$tag, c( "markdown translation failed", x = "Level 1 headings are not supported in @{state$tag$tag}", i = "Do you want to put the heading in @description or @details?" ) ) return(escape_comment(xml_text(xml))) } txt <- map_chr(xml_contents(xml), mdxml_node_to_rd, state) if (level == 1) { state$titles <- c(state$titles, paste(txt, collapse = "")) } head <- paste0( mdxml_close_sections(state, level), "\n", if (level == 1) state$section_tag else "\\subsection{", if (level > 1) paste(txt, collapse = ""), if (level > 1) "}{" ) state$section <- c(state$section, level) head } mdxml_html_block <- function(xml, state) { txt <- xml_text(xml) txt <- gsub("}", "\\}", txt, fixed = TRUE) txt <- gsub("{", "\\{", txt, fixed = TRUE) paste0( "\\if{html}{\\out{\n", txt, "}}\n" ) } mdxml_html_inline <- function(xml, state) { paste0( "\\if{html}{\\out{", gsub("}", "\\}", xml_text(xml), fixed = TRUE), "}}" ) } mdxml_close_sections <- function(state, upto = 1L) { hmy <- 0L upto <- max(upto, 2L) while (length(state$section) && tail(state$section, 1) >= upto) { hmy <- hmy + 1L state$section <- head(state$section, -1L) } paste0(rep("\n}\n", hmy), collapse = "") } roxygen2/R/object-format.R0000644000176200001440000000265215154314332015115 0ustar liggesusers#' Default format for data #' #' This function is called to generate the default "Format" section for each #' data object. The default implementation will return the class and dimension #' information. #' #' @param x A data object #' @return A `character` value with valid `Rd` syntax, or `NULL`. #' @keywords internal #' @export object_format <- function(x) { UseMethod("object_format") } #' @export object_format.default <- function(x) { paste0("An object of class ", format_classes(x), " ", format_dim(x), ".") } format_classes <- function(x) { classes <- paste0("\\code{", escape_rd_braces(class(x)), "}") base_classes <- NULL if (length(classes) > 1L) { base_classes <- paste0( " (inherits from ", paste(classes[-1L], collapse = ", "), ")" ) } paste0(classes[[1L]], base_classes) } format_dim <- function(x) { if (length(dim(x)) == 2L) { paste0("with ", nrow(x), " rows and ", ncol(x), " columns") } else if (length(dim(x)) > 2L) { paste0("of dimension ", paste(dim(x), collapse = " x ")) } else { paste0("of length ", length(x)) } } escape_rd_braces <- function(x) { x <- gsub("{", "\\{", x, fixed = TRUE) x <- gsub("}", "\\}", x, fixed = TRUE) x } # helpers ----------------------------------------------------------------- # used for testing call_to_format <- function(code, env = pkg_env()) { obj <- call_to_object(!!enexpr(code), env) object_format(obj$value) } roxygen2/R/markdown-link-resolve.R0000644000176200001440000001047315156266043016622 0ustar liggesusersfind_package <- function(topic, tag = NULL) { cur_pkg <- roxy_meta_get("current_package") cur_pkg_dir <- roxy_meta_get("current_package_dir") if (is.null(cur_pkg)) { # Don't try and link in basic tests return(NA_character_) } pkg <- find_package_cached(topic, pkg = cur_pkg, pkg_dir = cur_pkg_dir) if (length(pkg) == 0) { warn_roxy_tag( tag, c( "Could not resolve link to topic {.val {topic}} in the dependencies or base packages.", "i" = paste( "If you haven't documented {.val {topic}} yet, or just changed its name, this is normal.", "Once {.val {topic}} is documented, this warning goes away." ), "i" = "Make sure that the name of the topic is spelled correctly.", "i" = "Always list the linked package as a dependency.", "i" = "Alternatively, you can fully qualify the link with a package name." ) ) NA_character_ } else if (length(pkg) == 1) { pkg } else { warn_roxy_tag( tag, c( "Topic {.val {topic}} is available in multiple packages: {.pkg {pkg}}.", i = "Qualify topic explicitly with a package name when linking to it." ) ) NA_character_ } } find_package_cache <- new_environment() # run in roxygenize() because the documented functions might change between runs find_package_cache_reset <- function() { env_unbind(find_package_cache, env_names(find_package_cache)) # also reset the cache used by dev_help() (used by has_topic()) pkg <- roxy_meta_get("current_package") if (!is.null(pkg)) pkgload::dev_topic_index_reset(pkg) } find_package_cached <- function(topic, pkg, pkg_dir) { key <- paste0(pkg, "::", topic) env_cache(find_package_cache, key, find_package_lookup(topic, pkg, pkg_dir)) } # NA_character = found, doesn't need qualification # character(0) = not found # character(1) = one match # character(>1) = multiple matches find_package_lookup <- function(topic, pkg, pkg_dir) { # if it is in the current package, then no need for package name if (has_topic(topic, pkg)) { return(NA_character_) } pkgs <- pkg_deps(pkg_dir) pkg_has_topic <- pkgs[map_lgl(pkgs, has_topic, topic = topic)] pkg_has_topic <- map_chr(pkg_has_topic, \(pkg) find_source(topic, pkg)) pkg_has_topic <- unique(pkg_has_topic) base <- base_packages() if (length(pkg_has_topic) == 0) { # can't find it anywhere character() } else if (length(pkg_has_topic) == 1) { if (pkg_has_topic %in% base) { # never qualify links to base packages NA_character_ } else { pkg_has_topic } } else if (all(pkg_has_topic %in% base)) { # multiple base packages, no qualification needed NA_character_ } else { pkg_has_topic } } pkg_deps <- function(pkgdir) { deps <- desc::desc_get_deps(pkgdir) deps <- deps[deps$package != "R", ] deps <- deps[deps$type %in% c("Depends", "Imports", "Suggests"), ] c(deps$package, base_packages()) } base_packages <- function() { if (getRversion() >= "4.4.0") { asNamespace("tools")$standard_package_names()[["base"]] } else { c( "base", "compiler", "datasets", "graphics", "grDevices", "grid", "methods", "parallel", "splines", "stats", "stats4", "tcltk", "tools", "utils" ) } } # Adapted from downlit:::find_reexport_source find_source <- function(topic, package) { if (package %in% base_packages()) { return(package) } ns <- ns_env(package) if (!env_has(ns, topic, inherit = TRUE)) { return(package) } obj <- env_get(ns, topic, inherit = TRUE) if (is.primitive(obj)) { # primitive functions all live in base "base" } else if (is.function(obj)) { ## For functions, we can just take their environment. ns_env_name(get_env(obj)) } else { ## For other objects, we need to check the import env of the package, ## to see where 'topic' is coming from. The import env has redundant ## information. It seems that we just need to find a named list ## entry that contains `topic`. imp <- getNamespaceImports(ns) imp <- imp[names(imp) != ""] wpkgs <- map_lgl(imp, `%in%`, x = topic) if (!any(wpkgs)) { return(package) } pkgs <- names(wpkgs)[wpkgs] # Take the last match, in case imports have name clashes. pkgs[[length(pkgs)]] } } roxygen2/R/object-rc.R0000644000176200001440000000504015154546446014240 0ustar liggesusers#' @export roxy_tag_rd.roxy_tag_.methods <- function(x, base_path, env) { desc <- lapply(x$val, \(x) docstring(x$value@.Data)) usage <- map_chr(x$val, function(x) { function_usage(x$value@name, formals(x$value@.Data)) }) has_docs <- !map_lgl(desc, is.null) desc <- desc[has_docs] usage <- usage[has_docs] rd_section("rcmethods", setNames(desc, usage)) } #' @export format.rd_section_rcmethods <- function(x, ...) { rd_section_description("Methods", names(x$value), x$value) } # Extract all methods from an RC definition, returning a list of "objects". rc_methods <- function(obj) { stopifnot(methods::is(obj, "refClassRepresentation")) parents <- obj@refSuperClasses parent_methods <- unlist(lapply(parents, function(x) { methods::getRefClass(x)$methods() })) method_names <- sort_c(setdiff(ls(envir = obj@refMethods), parent_methods)) methods <- mget(method_names, envir = obj@refMethods) lapply(methods, object, alias = NULL, type = "rcmethod") } add_rc_metadata <- function(val, name, class) { class(val) <- c("rcmethod", "function") attr(val, "rcclass") <- class attr(val, "rcmethod") <- name val } get_method <- function(obj, method_name) { eval(call("$", quote(obj), as.name(method_name))) } # Modified from methods:::.refMethodDoc - a function has a doc string # if it's a call to {, with more than 1 element, and the first element is # a character vector. docstring <- function(f) { check_function(f) if (is.primitive(f)) { return(NULL) } b <- body(f) if (length(b) <= 2 || !identical(b[[1]], quote(`{`))) { return(NULL) } first <- b[[2]] if (!is.character(first)) { return(NULL) } if (first == "") { return(NULL) } trim_docstring(first) } # Implementation converted from # http://www.python.org/dev/peps/pep-0257/#handling-docstring-indentation trim_docstring <- function(docstring) { if (docstring == "") { return("") } # Convert tabs to spaces (using four spaces for tabs) # and split into a vector of lines: lines <- strsplit(gsub("\t", " ", docstring), "\n")[[1]] if (length(lines) == 1) { return(strip(lines)) } # Determine minimum indentation (first line doesn't count): indent <- min(leadingSpaces(lines[-1])) # Remove indentation (first line is special): trimmed <- c( strip(lines[1]), substr(lines[-1], indent + 1, 1000L) ) # Return a single string: string <- paste0(trimmed, collapse = "\n") # Strip off trailing and leading blank lines: gsub("^\n+|\n+$", "", string) } strip <- function(x) gsub("^ +| + $", "", x) roxygen2/R/select-args.R0000644000176200001440000000360015166171276014577 0ustar liggesusersselect_args_text <- function(args, select, topic_name) { check_character(args, allow_null = TRUE) check_string(select) check_string(topic_name) pieces <- strsplit(select, " +")[[1]] tryCatch( { parsed <- lapply(pieces, \(x) parse(text = x)[[1]]) select_args(args, parsed) }, error = function(e) { warn_roxy_topic(topic_name, "argument selection failed", parent = e) character() } ) } # Figure out which arguments that the user wants given a character vector of # arg names and an unevaluated list of selections select_args <- function(args, select = list()) { stopifnot(is.list(select)) args <- args[args != "..."] if (length(select) == 0) { return(args) } # Construct environment that allow minimal select-style semantics arg_idx <- as.list(setNames(seq_along(args), args)) arg_env <- list2env(arg_idx, parent = emptyenv()) arg_env$`:` <- `:` arg_env$`-` <- `-` arg_env$`(` <- `(` indices <- lapply(select, eval, envir = arg_env) for (i in seq_along(select)) { select_check(indices[[i]], select[[i]]) } # If first is negative, start with all vars # If first is positive, start with no vars select <- rep(select_sign(indices[[1]]) < 0, length(args)) for (idx in indices) { select[abs(idx)] <- select_sign(idx) > 0 } args[select] } select_check <- function(x, call) { if (!is.numeric(x)) { cli::cli_abort(c( "Argument specification must evaluate to a numeric vector.", "Problem in {.code {deparse(call)}}." )) } if (!(all(x > 0) || all(x < 0))) { cli::cli_abort( c( "Argument specification must be all positive or all negative, not a mixture.", i = "Problem in {.code {deparse(call)}}." ), call = NULL ) } invisible() } select_sign <- function(x) { if (all(x > 0)) { 1 } else if (all(x < 0)) { -1 } else { NA } } roxygen2/R/namespace.R0000644000176200001440000002710115173670265014324 0ustar liggesusers#' Roclet: make `NAMESPACE` #' #' @description #' This [roclet] automates the production of a `NAMESPACE` file, which controls #' the functions imported and exported by your package, as described in #' [Writing R extensions](https://cran.r-project.org/doc/manuals/r-release/R-exts.html). #' It is run by default by [roxygenize()]. #' #' The `NAMESPACE` is generated in two passes: the first generates only #' import directives (because this can be computed without evaluating package #' code), and the second generates everything (after the package has been #' loaded). #' #' See `vignette("namespace")` for details. #' #' @export #' @seealso [tags-namespace] for tags that generate `NAMESPACE` directives. #' @examples #' # The most common namespace tag is @@export, which declares that a function #' # is part of the external interface of your package #' #' @export #' foofy <- function(x, y, z) { #' } #' # This results in the following line in `NAMESPACE`: #' # export(foofy) #' #' # You'll also often find global imports living in a file called #' # R/{package}-package.R. #' #' @importFrom magrittr %>% #' #' @import rlang #' NULL #' #' # This results in the following lines in `NAMESPACE`: #' # importFrom(magrittr,"%>%") #' # import(rlang) namespace_roclet <- function() { roclet("namespace") } #' @export roclet_process.roclet_namespace <- function(x, blocks, env, base_path) { warn_missing_s3_exports(blocks, env) blocks_to_ns(blocks, env) } #' @export roclet_output.roclet_namespace <- function(x, results, base_path, ...) { NAMESPACE <- file.path(base_path, "NAMESPACE") results <- c(made_by("#"), results) # Always check for roxygen2 header before overwriting NAMESPACE (#436), # even when running for the first time write_if_different(NAMESPACE, results, check = TRUE) NAMESPACE } #' @export roclet_clean.roclet_namespace <- function(x, base_path) { NAMESPACE <- file.path(base_path, "NAMESPACE") if (made_by_roxygen(NAMESPACE)) { unlink(NAMESPACE) } } # NAMESPACE updates ------------------------------------------------------- import_directives <- c( "import", "importFrom", "importClassesFrom", "importMethodsFrom", "useDynLib" ) update_namespace_imports <- function(base_path) { NAMESPACE <- file.path(base_path, "NAMESPACE") if (!made_by_roxygen(NAMESPACE) || !file.exists(NAMESPACE)) { return(invisible()) } lines <- c(namespace_imports(base_path), namespace_exports(NAMESPACE)) results <- c(made_by("#"), sort_c(unique(trimws(lines)))) write_if_different(NAMESPACE, results, check = TRUE) invisible() } # Here we hand roll parsing and tokenisation from roxygen2 primitives so # we can filter tags that we know don't require package code. namespace_imports <- function(base_path = ".") { paths <- package_files(base_path) parsed <- lapply(paths, parse, keep.source = TRUE) srcrefs <- lapply(parsed, utils::getSrcref) # Ensure parse warnings only fire once during the main parse_package() pass suppressMessages( blocks <- unlist( lapply(srcrefs, namespace_imports_blocks), recursive = FALSE ) ) blocks_to_ns(blocks, emptyenv()) } namespace_imports_blocks <- function(srcref) { comment_refs <- comments(srcref) tokens <- lapply(comment_refs, tokenise_ref) import_tags <- c(import_directives, "rawNamespace") tokens_filtered <- lapply(tokens, function(tokens) { tokens[map_lgl(tokens, \(x) x$tag %in% import_tags)] }) compact(lapply(tokens_filtered, function(tokens) { block_create( call = NULL, srcref = srcref(srcfile("NAMESPACE"), rep(1, 4)), tokens = tokens ) })) } # NB: this is designed as the conjugate of namespace_imports(), so also # includes @rawNamespace entries which may/may not also include import directives. namespace_exports <- function(path) { parsed <- as.list(parse(path, keep.source = TRUE)) is_import_directive <- function(x) is_call(x, import_directives) export_lines <- attr(parsed, "srcref")[!map_lgl(parsed, is_import_directive)] # Each multiline directives are a single element so they're sorted correctly unlist(lapply(export_lines, \(x) paste(as.character(x), collapse = "\n"))) } # NAMESPACE generation ---------------------------------------------------- blocks_to_ns <- function(blocks, env) { lines <- map(blocks, block_to_ns, env = env) lines <- unlist(lines) %||% character() sort_c(unique(lines)) } block_to_ns <- function(block, env) { map(block$tags, roxy_tag_ns, block = block, env = env) } # Namespace tag methods --------------------------------------------------- roxy_tag_ns <- function(x, block, env) { UseMethod("roxy_tag_ns") } #' @export roxy_tag_ns.default <- function(x, block, env) {} #' @export roxy_tag_parse.roxy_tag_evalNamespace <- function(x) { tag_code(x) } #' @export roxy_tag_ns.roxy_tag_evalNamespace <- function(x, block, env) { roxy_tag_eval(x, env) } #' @export roxy_tag_parse.roxy_tag_export <- function(x) { tag_words(x) } #' @export roxy_tag_ns.roxy_tag_export <- function(x, block, env) { if (identical(x$val, "")) { # FIXME: check for empty exports (i.e. no name) default_export(block$object, block) } else { export(x$val) } } #' @export roxy_tag_parse.roxy_tag_exportClass <- function(x) { tag_words(x, 1) } #' @export roxy_tag_ns.roxy_tag_exportClass <- function(x, block, env) { export_class(x$val) } #' @export roxy_tag_parse.roxy_tag_exportMethod <- function(x) { tag_words(x, min = 1) } #' @export roxy_tag_ns.roxy_tag_exportMethod <- function(x, block, env) { export_s4_method(x$val) } #' @export roxy_tag_parse.roxy_tag_exportPattern <- function(x) { tag_words(x, min = 1) } #' @export roxy_tag_ns.roxy_tag_exportPattern <- function(x, block, env) { one_per_line("exportPattern", x$val) } #' @export roxy_tag_parse.roxy_tag_exportS3Method <- function(x) { tag_words(x, min = 0, max = 2) } #' @export roxy_tag_ns.roxy_tag_exportS3Method <- function(x, block, env) { obj <- block$object if (identical(x$val, "NULL")) { return() } if (identical(x$val, "")) { if (!inherits(obj, "s3method")) { warn_roxy_tag(x, "must be used with a known S3 method") return() } method <- attr(obj$value, "s3method") } else if (length(x$val) == 1) { if (!inherits(obj, "function") && !inherits(obj, "s3method")) { warn_roxy_tag(x, "must be used with a function") return() } if (!grepl("::", x$val, fixed = TRUE)) { warn_roxy_tag(x, "must have form package::generic") return() } generic <- re_split_half(x$val, "::") generic_re <- paste0("^", generic[[2]], "\\.") if (!grepl(generic_re, obj$alias)) { warn_roxy_tag( x, "generic ({.str {generic[[2]]}}) doesn't match function ({.str {obj$alias}})", ) return() } class <- sub(generic_re, "", obj$alias) method <- c(x$val, class) } else { method <- x$val } export_s3_method(method) } #' @export roxy_tag_parse.roxy_tag_import <- function(x) { tag_words(x, min = 1) } #' @export roxy_tag_ns.roxy_tag_import <- function(x, block, env) { one_per_line_ignore_current("import", x$val) } #' @export roxy_tag_parse.roxy_tag_importClassesFrom <- function(x) { tag_words(x, min = 2) } #' @export roxy_tag_ns.roxy_tag_importClassesFrom <- function(x, block, env) { repeat_first_ignore_current("importClassesFrom", x$val) } #' @export roxy_tag_parse.roxy_tag_importFrom <- function(x) { tag_words(x, min = 2) } #' @export roxy_tag_ns.roxy_tag_importFrom <- function(x, block, env) { pkg <- x$val[1L] if (requireNamespace(pkg, quietly = TRUE)) { importing <- x$val[-1L] # be sure to match '%>%', `%>%`, "%>%" all to %>% given by getNamespaceExports, #1570 unknown_idx <- !strip_quotes(importing) %in% getNamespaceExports(pkg) if (any(unknown_idx)) { warn_roxy_tag( x, "Excluding unknown {cli::qty(sum(unknown_idx))} export{?s} from {.package {pkg}}: {.code {importing[unknown_idx]}}" ) if (all(unknown_idx)) { return(NULL) } x$val <- c(pkg, importing[!unknown_idx]) } } repeat_first_ignore_current("importFrom", x$val) } #' @export roxy_tag_parse.roxy_tag_importMethodsFrom <- function(x) { tag_words(x, min = 2) } #' @export roxy_tag_ns.roxy_tag_importMethodsFrom <- function(x, block, env) { repeat_first_ignore_current("importMethodsFrom", x$val) } #' @export roxy_tag_parse.roxy_tag_rawNamespace <- function(x) { tag_code(x) } #' @export roxy_tag_ns.roxy_tag_rawNamespace <- function(x, block, env) { x$raw } #' @export roxy_tag_parse.roxy_tag_useDynLib <- function(x) { tag_words(x, min = 1) } #' @export roxy_tag_ns.roxy_tag_useDynLib <- function(x, block, env) { if (length(x$val) == 1) { return(paste0("useDynLib(", auto_quote(x$val), ")")) } if (any(grepl(",", x$val))) { # If there's a comma in list, don't quote output. This makes it possible # for roxygen2 to support other NAMESPACE forms not otherwise mapped args <- paste0(x$val, collapse = " ") paste0("useDynLib(", args, ")") } else { repeat_first("useDynLib", x$val) } } # Default export methods -------------------------------------------------- default_export <- function(x, block) { UseMethod("default_export") } #' @export default_export.s4class <- function(x, block) { c( if (!is.null(block$object$alias)) export(block$object$alias), export_class(x$value@className) ) } #' @export default_export.s4generic <- function(x, block) export(x$value@generic) #' @export default_export.s4method <- function(x, block) export_s4_method(x$value@generic) #' @export default_export.s3method <- function(x, block) { export_s3_method(auto_quote(attr(x$value, "s3method"))) } #' @export default_export.rcclass <- function(x, block) export_class(x$value@className) #' @export default_export.default <- function(x, block) export(x$alias) #' @export default_export.NULL <- function(x, block) { export(block_get_tag_value(block, "name")) } # Helpers ----------------------------------------------------------------- export <- function(x) one_per_line("export", x) export_class <- function(x) one_per_line("exportClasses", x) export_s4_method <- function(x) one_per_line("exportMethods", x) export_s3_method <- function(x) { args <- paste0(x, collapse = ",") paste0("S3method(", args, ")") } one_per_line <- function(name, x) { if (length(x)) { paste0(name, "(", auto_quote(x), ")") } else { NULL } } repeat_first <- function(name, x) { paste0(name, "(", auto_quote(x[1]), ",", auto_quote(x[-1]), ")") } one_per_line_ignore_current <- function(name, x) { current <- peek_roxygen_pkg() # Ignore any occurrence of `current` inside `x` if (is_string(current)) { x <- x[x != current] } one_per_line(name, x) } repeat_first_ignore_current <- function(name, x) { current <- peek_roxygen_pkg() # Ignore the whole command if "first" is `current` if (is_string(current) && length(x) && x[[1]] == current) { NULL } else { repeat_first(name, x) } } # missing s3 exports ------------------------------------------------------ warn_missing_s3_exports <- function(blocks, env) { objs <- as.list(env) funs <- Filter(is.function, objs) methods <- funs[map_lgl(names(funs), is_s3_method, env = env)] s3blocks <- blocks[map_lgl( blocks, block_has_tags, c("export", "exportS3Method") )] s3objects <- map(s3blocks, \(block) block$object$value) s3functions <- Filter(is.function, s3objects) undocumented <- methods[!methods %in% s3functions] srcrefs <- map(undocumented, attr, "srcref") map2(undocumented, names(undocumented), function(fun, name) { warn_roxy_function( fun, "S3 method {.arg {name}} needs @export or @exportS3Method tag" ) }) } roxygen2/R/rd-r6-external.R0000644000176200001440000000361715172443612015141 0ustar liggesusers#' @export roxy_tag_parse.roxy_tag_R6method <- function(x) { raw <- trimws(x$raw) if (raw == "") { warn_roxy_tag(x, "requires a value like {.code Class$method}") return(NULL) } warn_if_multiline(x, raw, multiline = FALSE) pieces <- strsplit(raw, "\\$")[[1]] if (length(pieces) != 2 || pieces[1] == "" || pieces[2] == "") { warn_roxy_tag(x, "must be of the form {.code Class$method}") return(NULL) } x$val <- list(class = pieces[1], method = pieces[2]) x } # Merge external @R6method blocks into their target R6 class blocks ----------- merge_external_r6methods <- function(blocks) { is_external <- map_lgl(blocks, function(b) { block_has_tags(b, "R6method") && !inherits(b, "roxy_block_r6class") }) external <- blocks[is_external] if (length(external) == 0) { return(blocks) } blocks <- blocks[!is_external] # Build index of R6 class blocks by class name r6_index <- list() for (i in seq_along(blocks)) { if (inherits(blocks[[i]], "roxy_block_r6class")) { classname <- blocks[[i]]$object$value$classname if (!is.null(classname)) { r6_index[[classname]] <- i } } } for (ext_block in external) { r6method_tag <- block_get_tag(ext_block, "R6method") classname <- r6method_tag$val$class methodname <- r6method_tag$val$method idx <- r6_index[[classname]] if (is.null(idx)) { warn_roxy_tag(r6method_tag, "Can't find R6 class {.cls {classname}}") next } target <- blocks[[idx]] # Stamp all tags with the method name so r6_extract_methods() # can assign them by name; non-method tags will be filtered out # by the existing r6_tag_type() logic ext_tags <- discard(ext_block$tags, \(t) t$tag == "R6method") for (j in seq_along(ext_tags)) { ext_tags[[j]]$r6method <- methodname } target$tags <- c(target$tags, ext_tags) blocks[[idx]] <- target } blocks } roxygen2/R/tag.R0000644000176200001440000000516015156771760013147 0ustar liggesusers#' `roxy_tag` S3 constructor #' #' `roxy_tag()` is the constructor for tag objects. #' `roxy_tag_warning()` is superseded by `warn_roxy_tag()`; use to generate a #' warning that includes the location of the tag. #' #' @section Methods: #' Define a method for `roxy_tag_parse` to support new tags. See [tag_parsers] #' for more details. #' #' @family extending #' @export #' @param tag Tag name. Arguments starting with `.` are reserved for internal #' usage. #' @param raw Raw tag value, a string. #' @param val Parsed tag value, typically a character vector, but sometimes #' a list. Usually filled in by `tag_parsers` #' @param file,line Location of the tag roxy_tag <- function( tag, raw, val = NULL, file = NA_character_, line = NA_character_ ) { structure( list( file = file, line = line, raw = raw, tag = tag, val = val ), class = c(paste0("roxy_tag_", tag), "roxy_tag") ) } roxy_generated_tag <- function(block, tag, val) { roxy_tag( tag = tag, raw = NULL, val = val, file = block$file, line = block$line ) } roxy_test_tag <- function(raw = "", val = NULL) { roxy_tag("test", raw = raw, val = val, file = "test.R", line = 1) } #' @rdname roxy_tag #' @param x A tag #' @export roxy_tag_parse <- function(x) { UseMethod("roxy_tag_parse") } #' @export roxy_tag_parse.default <- function(x) { warn_roxy_tag(x, "is not a known tag") NULL } is.roxy_tag <- function(x) inherits(x, "roxy_tag") #' @export format.roxy_tag <- function(x, ..., file = NULL) { if (identical(x$file, file)) { file <- "line" } else if (is.na(x$file)) { file <- "????" } else { file <- basename(x$file) } line <- if (is.na(x$line)) "???" else format(x$line, width = 3) loc <- paste0("[", file, ":", line, "]") if (!is.null(x$raw)) { lines <- strsplit(x$raw, "\n")[[1]] ellipsis <- FALSE if (length(lines) > 1) { raw <- lines[[1]] ellipsis <- TRUE } else { raw <- x$raw } if (nchar(raw) > 50) { raw <- substr(raw, 1, 47) ellipsis <- TRUE } raw <- paste0(raw, if (ellipsis) "...") } else { raw <- "" } parsed <- if (is.null(x$val)) "{unparsed}" else "{parsed}" paste0(loc, " @", x$tag, " '", raw, "' ", parsed) } #' @export print.roxy_tag <- function(x, ...) { cat_line(format(x, ...)) } #' @export #' @rdname roxy_tag roxy_tag_warning <- function(x, ...) { # Should no longer be used internally message <- paste0( if (!is.na(x$file)) paste0("[", x$file, ":", x$line, "] "), ..., collapse = " " ) warning(message, call. = FALSE, immediate. = TRUE) NULL } roxygen2/R/import-standalone-obj-type.R0000644000176200001440000002105715154610530017547 0ustar liggesusers# Standalone file: do not edit by hand # Source: https://github.com/r-lib/rlang/blob/HEAD/R/standalone-obj-type.R # Generated by: usethis::use_standalone("r-lib/rlang", "obj-type") # ---------------------------------------------------------------------- # # --- # repo: r-lib/rlang # file: standalone-obj-type.R # last-updated: 2025-10-02 # license: https://unlicense.org # imports: rlang (>= 1.1.0) # --- # # ## Changelog # # 2025-10-02: # - `obj_type_friendly()` now shows the dimensionality of arrays. # # 2024-02-14: # - `obj_type_friendly()` now works for S7 objects. # # 2023-05-01: # - `obj_type_friendly()` now only displays the first class of S3 objects. # # 2023-03-30: # - `stop_input_type()` now handles `I()` input literally in `arg`. # # 2022-10-04: # - `obj_type_friendly(value = TRUE)` now shows numeric scalars # literally. # - `stop_friendly_type()` now takes `show_value`, passed to # `obj_type_friendly()` as the `value` argument. # # 2022-10-03: # - Added `allow_na` and `allow_null` arguments. # - `NULL` is now backticked. # - Better friendly type for infinities and `NaN`. # # 2022-09-16: # - Unprefixed usage of rlang functions with `rlang::` to # avoid onLoad issues when called from rlang (#1482). # # 2022-08-11: # - Prefixed usage of rlang functions with `rlang::`. # # 2022-06-22: # - `friendly_type_of()` is now `obj_type_friendly()`. # - Added `obj_type_oo()`. # # 2021-12-20: # - Added support for scalar values and empty vectors. # - Added `stop_input_type()` # # 2021-06-30: # - Added support for missing arguments. # # 2021-04-19: # - Added support for matrices and arrays (#141). # - Added documentation. # - Added changelog. # # nocov start #' Return English-friendly type #' @param x Any R object. #' @param value Whether to describe the value of `x`. Special values #' like `NA` or `""` are always described. #' @param length Whether to mention the length of vectors and lists. #' @return A string describing the type. Starts with an indefinite #' article, e.g. "an integer vector". #' @noRd obj_type_friendly <- function(x, value = TRUE) { if (is_missing(x)) { return("absent") } if (is.object(x)) { if (inherits(x, "quosure")) { type <- "quosure" } else { type <- class(x)[[1L]] } return(sprintf("a <%s> object", type)) } if (!is_vector(x)) { return(.rlang_as_friendly_type(typeof(x))) } n_dim <- length(dim(x)) if (!n_dim) { if (!is_list(x) && length(x) == 1) { if (is_na(x)) { return(switch( typeof(x), logical = "`NA`", integer = "an integer `NA`", double = if (is.nan(x)) { "`NaN`" } else { "a numeric `NA`" }, complex = "a complex `NA`", character = "a character `NA`", .rlang_stop_unexpected_typeof(x) )) } show_infinites <- function(x) { if (x > 0) { "`Inf`" } else { "`-Inf`" } } str_encode <- function(x, width = 30, ...) { if (nchar(x) > width) { x <- substr(x, 1, width - 3) x <- paste0(x, "...") } encodeString(x, ...) } if (value) { if (is.numeric(x) && is.infinite(x)) { return(show_infinites(x)) } if (is.numeric(x) || is.complex(x)) { number <- as.character(round(x, 2)) what <- if (is.complex(x)) "the complex number" else "the number" return(paste(what, number)) } return(switch( typeof(x), logical = if (x) "`TRUE`" else "`FALSE`", character = { what <- if (nzchar(x)) "the string" else "the empty string" paste(what, str_encode(x, quote = "\"")) }, raw = paste("the raw value", as.character(x)), .rlang_stop_unexpected_typeof(x) )) } return(switch( typeof(x), logical = "a logical value", integer = "an integer", double = if (is.infinite(x)) show_infinites(x) else "a number", complex = "a complex number", character = if (nzchar(x)) "a string" else "\"\"", raw = "a raw value", .rlang_stop_unexpected_typeof(x) )) } if (length(x) == 0) { return(switch( typeof(x), logical = "an empty logical vector", integer = "an empty integer vector", double = "an empty numeric vector", complex = "an empty complex vector", character = "an empty character vector", raw = "an empty raw vector", list = "an empty list", .rlang_stop_unexpected_typeof(x) )) } } vec_type_friendly(x) } vec_type_friendly <- function(x, length = FALSE) { if (!is_vector(x)) { abort("`x` must be a vector.") } type <- typeof(x) n_dim <- length(dim(x)) add_length <- function(type) { if (length && !n_dim) { paste0(type, sprintf(" of length %s", length(x))) } else { type } } if (type == "list") { if (n_dim == 0) { return(add_length("a list")) } else if (n_dim == 2) { if (is.data.frame(x)) { return("a data frame") } else { return("a list matrix") } } else { return(sprintf("a list %sD array", n_dim)) } } type <- switch( type, logical = "a logical %s", integer = "an integer %s", numeric = , double = "a double %s", complex = "a complex %s", character = "a character %s", raw = "a raw %s", type = paste0("a ", type, " %s") ) if (n_dim == 0) { kind <- "vector" } else if (n_dim == 2) { kind <- "matrix" } else { kind <- sprintf("%sD array", n_dim) } out <- sprintf(type, kind) if (n_dim >= 2) { out } else { add_length(out) } } .rlang_as_friendly_type <- function(type) { switch( type, list = "a list", NULL = "`NULL`", environment = "an environment", externalptr = "a pointer", weakref = "a weak reference", S4 = "an S4 object", name = , symbol = "a symbol", language = "a call", pairlist = "a pairlist node", expression = "an expression vector", char = "an internal string", promise = "an internal promise", ... = "an internal dots object", any = "an internal `any` object", bytecode = "an internal bytecode object", primitive = , builtin = , special = "a primitive function", closure = "a function", type ) } .rlang_stop_unexpected_typeof <- function(x, call = caller_env()) { abort( sprintf("Unexpected type <%s>.", typeof(x)), call = call ) } #' Return OO type #' @param x Any R object. #' @return One of `"bare"` (for non-OO objects), `"S3"`, `"S4"`, #' `"R6"`, or `"S7"`. #' @noRd obj_type_oo <- function(x) { if (!is.object(x)) { return("bare") } class <- inherits(x, c("R6", "S7_object"), which = TRUE) if (class[[1]]) { "R6" } else if (class[[2]]) { "S7" } else if (isS4(x)) { "S4" } else { "S3" } } #' @param x The object type which does not conform to `what`. Its #' `obj_type_friendly()` is taken and mentioned in the error message. #' @param what The friendly expected type as a string. Can be a #' character vector of expected types, in which case the error #' message mentions all of them in an "or" enumeration. #' @param show_value Passed to `value` argument of `obj_type_friendly()`. #' @param ... Arguments passed to [rlang::abort()]. #' @inheritParams args_error_context #' @noRd stop_input_type <- function( x, what, ..., allow_na = FALSE, allow_null = FALSE, show_value = TRUE, arg = caller_arg(x), call = caller_env() ) { # From standalone-cli.R cli <- env_get_list( nms = c("format_arg", "format_code"), last = topenv(), default = function(x) sprintf("`%s`", x), inherit = TRUE ) if (allow_na) { what <- c(what, cli$format_code("NA")) } if (allow_null) { what <- c(what, cli$format_code("NULL")) } if (length(what)) { what <- oxford_comma(what) } if (inherits(arg, "AsIs")) { format_arg <- identity } else { format_arg <- cli$format_arg } message <- sprintf( "%s must be %s, not %s.", format_arg(arg), what, obj_type_friendly(x, value = show_value) ) abort(message, ..., call = call, arg = arg) } oxford_comma <- function(chr, sep = ", ", final = "or") { n <- length(chr) if (n < 2) { return(chr) } head <- chr[seq_len(n - 1)] last <- chr[n] head <- paste(head, collapse = sep) # Write a or b. But a, b, or c. if (n > 2) { paste0(head, sep, final, " ", last) } else { paste0(head, " ", final, " ", last) } } # nocov end roxygen2/R/options.R0000644000176200001440000001210015166171276014054 0ustar liggesusers#' Load roxygen2 options #' #' @description #' Options can be stored in `DESCRIPTION` using `Config/roxygen2/` fields, #' or in `man/roxygen/meta.R`. Call `roxy_meta_get()` to access current #' option values from within tag and roclet methods. #' #' Options in `man/roxygen/meta.R` override those present in `DESCRIPTION`. #' #' @section Possible options: #' #' * `roclets` ``: giving names of [roclets][roclet] to run. See #' [roclet_find()] for details. #' #' * `packages` ``: packages to load that implement new tags. #' #' * `load` ``: how to load R code. See [load] for details. #' #' * `old_usage` ``: use old style usage formatting? #' #' * `markdown` ``: translate markdown syntax to Rd? #' #' * `r6` ``: document R6 classes? #' #' * `current_package` `` (read only): name of package being documented. #' #' * `rd_family_title` ``: overrides for `@family` titles. See the #' _rd-functions_ vignette for details: `vignette("rd-functions")` #' #' * `knitr_chunk_options` ``: default chunk options used for knitr. #' #' * `restrict_image_formats` ``: if `TRUE` then PDF images are only #' included in the PDF manual, and SVG images are only included in the HTML #' manual. (This only applies to images supplied via markdown.) #' #' @section How to set: #' Either set in `DESCRIPTION` using `Config/roxygen2/` fields: #' #' ``` #' Config/roxygen2/markdown: TRUE #' Config/roxygen2/load: installed #' ``` #' #' Or if you need more complex options (like `rd_family_title` or #' `knitr_chunk_options`), put them in `man/roxygen/meta.R`: #' #' ``` #' list( #' rd_family_title = list(models = "Model functions"), #' knitr_chunk_options = list(fig.width = 7) #' ) #' ``` #' #' @param base_path Path to package. #' @export #' @family extending load_options <- function(base_path = ".") { old <- load_options_roxygen(base_path) config <- load_options_config(base_path) desc <- utils::modifyList(old, config) meta <- load_options_meta(base_path) opts <- utils::modifyList(desc, meta) defaults <- list( roclets = c("collate", "namespace", "rd"), packages = character(), load = "pkgload", old_usage = FALSE, markdown = FALSE, r6 = TRUE, current_package = NA_character_, current_package_dir = NA_character_, rd_family_title = list(), knitr_chunk_options = NULL, restrict_image_formats = TRUE, lazy_data = FALSE ) unknown_opts <- setdiff(names(opts), names(defaults)) if (length(unknown_opts) > 0) { warn(paste0( "Unknown Roxygen options ", paste(unknown_opts, collapse = ", "), ".\n", "Supported options: ", paste(names(defaults), collapse = ", ") )) } utils::modifyList(defaults, opts) } load_options_roxygen <- function(base_path = ".") { desc_path <- file.path(base_path, "DESCRIPTION") dcf <- read.dcf(desc_path, fields = c("Roxygen", "Package", "LazyData")) desc_opts <- dcf[[1, 1]] if (is.na(desc_opts)) { opts <- list() } else { opts <- eval(parse(text = desc_opts), child_env(baseenv())) } opts$current_package <- dcf[[1, 2]] opts$current_package_dir <- normalizePath(base_path) opts$lazy_data <- identical(dcf[[1, 3]], "true") opts } config_fields <- c( "markdown", "load", "old_usage", "r6", "restrict_image_formats", "packages", "roclets" ) load_options_config <- function(base_path = ".") { fields <- paste0("Config/roxygen2/", config_fields) values <- desc::desc_get(fields, file = base_path) names(values) <- config_fields values <- values[!is.na(values)] lapply(values, parse_config_value) } parse_config_value <- function(x) { values <- scan( text = x, what = "character", sep = ",", strip.white = TRUE, quiet = TRUE ) utils::type.convert(values, as.is = TRUE) } load_options_meta <- function(base_path = ".", path = "man/roxygen/meta.R") { # Only look for .R for consistency with style advice meta_path <- file.path(base_path, path) if (!file.exists(meta_path)) { return(list()) } value <- tryCatch( source(meta_path, local = child_env(baseenv()))$value, error = function(err) { warn("Failed to source `man/roxygen/meta.R`") list() } ) if (!is.list(value)) { warn("`man/roxygen/meta.R` must yield a named list") return(list()) } value } # Global binding management ----------------------------------------------- roxy_meta <- new_environment() #' @export #' @rdname load_options #' @param key Key of the options, e.g. `"packages"`. #' @param default Default value. roxy_meta_get <- function(key = NULL, default = NULL) { env_get(roxy_meta, key, default = default) } roxy_meta_set <- function(key, value = NULL) { env_poke(roxy_meta, key, value) } roxy_meta_clear <- function() { env_unbind(roxy_meta, env_names(roxy_meta)) } roxy_meta_load <- function(base_path = getwd()) { roxy_meta_clear() env_bind(roxy_meta, !!!load_options(base_path)) } local_roxy_meta_set <- function(key, value, envir = caller_env()) { old_value <- roxy_meta_set(key, value) withr::defer(roxy_meta_set(key, old_value), envir = envir) } roxygen2/R/rd-examples.R0000644000176200001440000001120615166171276014610 0ustar liggesusers#' @export roxy_tag_parse.roxy_tag_examples <- function(x) { tag_examples(x) } #' @export roxy_tag_parse.roxy_tag_examplesIf <- function(x) { lines <- unlist(strsplit(x$raw, "\r?\n")) condition <- lines[1] parse_err <- tryCatch( { suppressWarnings(parse(text = condition)) NULL }, error = function(err) err ) if (!is.null(parse_err)) { warn_roxy_tag(x, "condition failed to parse", parent = parse_err) return(NULL) } example_lines <- lines[-1] if (length(example_lines) == 0 || all(trimws(example_lines) == "")) { warn_roxy_tag(x, "requires example code after the condition") return(NULL) } x$raw <- paste( c( paste0("\\dontshow{if (", condition, ") withAutoprint(\\{ # examplesIf}"), lines[-1], "\\dontshow{\\}) # examplesIf}" ), collapse = "\n" ) tag_examples(x) } #' @export roxy_tag_parse.roxy_tag_example <- function(x) { x <- tag_value(x, multiline = TRUE) nl <- re_count(x$val, "\n") if (any(nl) > 0) { warn_roxy_tag( x, c( "must be a single line", i = "Do you want @examples?" ) ) return() } x } #' @export roxy_tag_rd.roxy_tag_examples <- function(x, base_path, env) { rd_section("examples", x$val) } #' @export roxy_tag_rd.roxy_tag_examplesIf <- function(x, base_path, env) { rd_section("examples", x$val) } #' @export roxy_tag_rd.roxy_tag_example <- function(x, base_path, env) { path <- file.path(base_path, x$val) if (!file.exists(path)) { warn_roxy_tag(x, "{.path {path}} doesn't exist") return() } code <- read_lines(path) rd_section("examples", escape_examples(code)) } #' @export format.rd_section_examples <- function(x, ...) { value <- paste0(x$value, collapse = "\n") rd_macro(x$type, value, space = TRUE) } #' Escape examples #' #' This documentation topic is used primarily for testing and to record #' our understanding of the `\example{}` escaping rules. #' See for the details provided #' by R core. #' #' @keywords internal #' @export #' @examples #' # In examples we automatically escape Rd comments (%): #' 100 %% 30 #' # even if they are in strings #' "50%" #' #' # and \\ and \v inside of strings and symbols #' "\v" # vertical tab #' "\\" #' # but not comments: \l \v #' #' # other string escapes are left as is #' "\"" #' "\n" #' #' # Otherwise, backslashes and parentheses are left as is. This #' # means that you need to escape unbalanced parentheses, which typically only #' # occur in \dontshow{}: #' \dontshow{if (FALSE) \{ } #' print("Hello") #' \dontshow{ \} } #' #' # You also need to escape backslashes in infix operators and comments #' # (this is generally rare) #' `%\\%` <- function(x, y) x + y #' 10 %\\% 20 #' # \\\\ (renders as two backslashes) escape_examples <- function(x) { x <- paste0(x, collapse = "\n") rd(escapeExamples(x)) } # Strip \dontrun{}, \donttest{}, and \dontshow{} from example code. # Used for R6 method examples displayed in \preformatted{} blocks where # Rd macros are not interpreted. \dontrun{} and \donttest{} are unwrapped # (content kept), while \dontshow{} is removed entirely. strip_rd_example_tags <- function(x) { # Process each tag type for (tag in c("\\dontrun", "\\donttest")) { x <- strip_rd_tag(x, tag, keep_contents = TRUE) } x <- strip_rd_tag(x, "\\dontshow", keep_contents = FALSE) # Clean up any resulting blank lines at start/end x <- gsub("^\n+", "", x) x <- gsub("\n+$", "", x) x } strip_rd_tag <- function(x, tag, keep_contents) { needle <- paste0(tag, "{") repeat { start <- regexpr(needle, x, fixed = TRUE) if (start == -1L) { break } tag_len <- attr(start, "match.length") # findEndOfTag uses 0-based indexing open_brace_0 <- start + tag_len - 2L end_0 <- findEndOfTag(x, is_code = TRUE, start = open_brace_0) if (end_0 == -1L) { break } close_pos <- end_0 + 1L inner <- substring(x, start + tag_len, close_pos - 1L) if (keep_contents) { # Remove leading/trailing whitespace from inner content inner <- gsub("^\n", "", inner) inner <- gsub("\n$", "", inner) } else { inner <- "" } before <- substring(x, 1L, start - 1L) after <- substring(x, close_pos + 1L) # Clean up surrounding newlines before <- gsub("\n$", "", before) if (nzchar(inner)) { after <- gsub("^\n", "", after) x <- paste0( before, if (nzchar(before)) "\n", inner, if (nzchar(after)) "\n", after ) } else { after <- gsub("^\n", "", after) x <- paste0(before, if (nzchar(before) && nzchar(after)) "\n", after) } } x } roxygen2/R/rd-r6-methods-self.R0000644000176200001440000001245515170121342015700 0ustar liggesusersrd_r6_method <- function( name, class, formals, description = character(), details = character(), params = list(), return = NULL, examples = character() ) { structure( list( name = name, class = class, formals = formals, description = description, details = details, params = params, return = return, examples = examples ), class = "rd_r6_method" ) } #' @export format.rd_r6_method <- function(x, ...) { lines <- character() push <- function(...) lines <<- c(lines, ...) push_subsection <- function(title, ...) { push( paste0(" \\subsection{", title, "}{"), paste0(" ", c(...)), " }" ) } # Anchor and heading call <- r6_method_name(x$class, x$name) id <- paste0("method-", x$class, "-", x$name) push(rd_if_html("
    ")) push(rd_if_html('')) push(rd_if_latex("\\hypertarget{", id, "}{}")) push(paste0("\\subsection{\\code{", call, "()}}{")) # Description if (length(x$description) > 0) { push( paste0( " ", sub("\n?\n?$", "\n\n", head(x$description, -1)), recycle0 = TRUE ), paste0(" ", utils::tail(x$description, 1), recycle0 = TRUE) ) } # Usage fake <- paste(rep("X", nchar(call)), collapse = "") usage <- format(function_usage(fake, x$formals)) push_subsection( "Usage", rd_if_html('
    '), paste0("\\preformatted{", sub(paste0("^", fake), call, usage), "}"), rd_if_html("
    ") ) # Params if (length(x$params) > 0) { nms <- map_chr(x$params, \(p) p$name) vals <- map_chr(x$params, \(p) p$description) push_subsection( "Arguments", rd_if_html('
    '), "\\describe{", paste0(" \\item{\\code{", nms, "}}{", vals, "}"), "}", rd_if_html("
    ") ) } # Details if (length(x$details) > 0) { push_subsection( "Details", sub("\n?\n?$", "\n\n", head(x$details, -1)), utils::tail(x$details, 1) ) } # Return if (!is.null(x$return)) { push_subsection("Returns", x$return) } # Examples if (length(x$examples) > 0) { push_subsection( "Examples", rd_if_html('
    '), paste0("\\preformatted{", strip_rd_example_tags(x$examples), "\n", "}"), rd_if_html("
    ") ) } # End push("}\n") lines } r6_method_from_row <- function(method, block) { tags <- method$tags[[1]] desc_tags <- keep(tags, \(t) t$tag == "description") description <- map_chr(desc_tags, \(x) x[["val"]]) det_tags <- keep(tags, \(t) t$tag == "details") details <- map_chr(det_tags, \(x) x[["val"]]) params <- r6_resolve_params(method, block) ret_tags <- keep(tags, \(t) t$tag %in% c("return", "returns")) if (length(ret_tags) > 1) { warn_roxy_block(block, "Must use one @return(s) per R6 method") } ret <- if (length(ret_tags) > 0) ret_tags[[1]]$val else NULL exa_tags <- keep(tags, \(t) t$tag == "examples") examples <- map_chr(exa_tags, \(x) x[["val"]]) rd_r6_method( name = method$name, class = method$class, formals = method$formals[[1]], description = description, details = details, params = params, return = ret, examples = examples ) } # Resolve params within a class: method @param -> class-level @param -> @field. # Cross-class inheritance from parent classes is handled later by # r6_resolve_method_params(). r6_resolve_params <- function(method, block) { tags <- method$tags[[1]] par_tags <- keep(tags, \(t) t$tag == "param") params <- r6_tags_to_params(par_tags) pnames <- r6_param_names(params) dup <- unique(pnames[duplicated(pnames)]) for (m in dup) { warn_roxy_block( block, c( "Must use one @param for each argument", x = "${method$name}({m}) is documented multiple times" ) ) } fnames <- names(method$formals[[1]]) if (length(fnames) == 0) { return(list()) } # 1. Add class-level @param miss <- setdiff(fnames, pnames) cls_tags <- keep(block$tags, function(t) { !is.na(t$line) && t$line < block$line && tag_is(t, "param") && tag_has_name(t, miss) }) params <- c(params, r6_tags_to_params(cls_tags)) # 2. For initialize() only, inherit from @field if (method$name == "initialize") { miss <- setdiff(fnames, r6_param_names(params)) if (length(miss) > 0) { field_tags <- keep(block$tags, function(t) { tag_is(t, "field") && tag_has_name(t, miss) }) params <- c(params, r6_tags_to_params(field_tags)) } } # Order them according to formals firstnames <- map_chr( strsplit(map_chr(params, \(x) x$name), ","), \(x) trimws(x[[1]]) ) params[order(match(firstnames, fnames))] } r6_tags_to_params <- function(tags) { lapply(tags, function(t) { list( name = gsub(",", ", ", t$val$name), description = t$val$description ) }) } r6_param_names <- function(params) { if (length(params) == 0) { return(character()) } trimws(unlist(strsplit(map_chr(params, \(x) x$name), ","))) } r6_method_name <- function(class, method) { paste0(class, "$", ifelse(method == "initialize", "new", method)) } rd_if_html <- function(...) { paste0("\\if{html}{\\out{", ..., "}}") } rd_if_latex <- function(...) { paste0("\\if{latex}{\\out{", ..., "}}") } roxygen2/R/rd-r6-class.R0000644000176200001440000000216115163737466014432 0ustar liggesusersrd_r6_class <- function( class, alias = class, superclasses = rd_r6_super(class), fields = rd_r6_fields(type = "field"), active_bindings = rd_r6_fields(type = "active"), methods = rd_r6_methods(alias) ) { structure( list( superclasses = superclasses, fields = fields, active_bindings = active_bindings, methods = methods ), class = "rd_r6_class" ) } #' @export format.rd_r6_class <- function(x, ...) { c( format(x$superclasses), format(x$fields), format(x$active_bindings), format(x$methods) ) } # Extraction --------------------------------------------------------------- r6_class_from_block <- function(block, env) { r6data <- block_get_tag_value(block, ".r6data") class <- block$object$value$classname alias <- block$object$alias rd_r6_class( class = class, alias = alias, superclasses = r6_extract_superclasses(r6data, env, class), fields = r6_extract_field_tags(block, r6data, type = "field"), active_bindings = r6_extract_field_tags(block, r6data, type = "active"), methods = r6_extract_methods(r6data, alias, block) ) } roxygen2/R/rd-r6.R0000644000176200001440000002036215171700424013311 0ustar liggesusers# Pass 1: within-class resolution --------------------------------------------- # Resolves params from method @param -> class-level @param -> @field. # See r6_resolve_params() for the core logic. topic_add_r6_methods <- function(rd, block, env, base_path) { docs <- r6_class_from_block(block, env) block <- r6_fix_intro(block) # Add class-level tags (skip tags stamped for a specific method) for (tag in block$tags) { if (r6_tag_type(tag, block) == "class") { rd$add(roxy_tag_rd(tag, env = env, base_path = base_path)) } } # Store unresolved R6 docs; pass 2 will inherit and format. The value is a # named list keyed by classname so that multiple R6 classes sharing a topic # (via @rdname) can coexist and be looked up individually. classname <- block$object$value$classname %||% "" rd$add(rd_section("r6_class", set_names(list(docs), classname))) } #' @export format.rd_section_r6_class <- function(x, ...) NULL #' @export merge.rd_section_r6_class <- function(x, y, ...) { rd_section("r6_class", c(x$value, y$value)) } # When an R6 class has inline @description tags for methods, parse_description() # parser puts the class description into @details instead of @description. # This function detects that case and promotes the class-level @details back # to @description. r6_fix_intro <- function(block) { types <- map_chr(block$tags, \(t) r6_tag_type(t, block)) tags <- map_chr(block$tags, \(t) t$tag) has_class_desc <- any(tags == "description" & types == "class") has_class_details <- any(tags == "details" & types == "class") has_method_desc <- any(tags == "description" & types == "method") if (!has_class_desc && has_class_details && has_method_desc) { # Promote the first class-level @details to @description for (i in seq_along(block$tags)) { if (tags[[i]] == "details" && types[[i]] == "class") { block$tags[[i]]$tag <- "description" break } } } block } # Classify an R6 block tag: # - "class": top-level Rd (e.g. @title, @description before class body) # - "method": inline tag associated with a method # - "other": @field/@param tags consumed by field/param extraction r6_tag_type <- function(tag, block) { if (!is.null(tag$r6method)) { return("method") } inline <- !is.na(tag$line) && tag$line >= block$line method_tags <- c( "description", "details", "return", "returns", "examples", "noRd" ) if (tag$tag %in% c("field", "name", "title")) { "other" } else if (tag$tag %in% method_tags && inline) { "method" } else if (tag$tag == "param") { # class-level @param are used as defaults for all methods if (inline) "method" else "other" } else { "class" } } tag_is <- function(tag, name) { tag$tag == name } tag_names <- function(tag) { trimws(strsplit(tag$val$name, ",")[[1]]) } tag_has_name <- function(tag, names) { any(tag_names(tag) %in% names) } # Pass 2: R6 inheritance resolution ----------------------------------------- # Resolve R6 superclass inheritance after all blocks have been converted # to topics. Mirrors topics_process_inherit() but for R6 field/param docs. topics_process_r6_inherit <- function(topics) { r6_deps <- function(topic) { docs_list <- topic$get_value("r6_class") unlist(lapply(docs_list, \(d) d$superclasses$classname)) } topics$topo_apply(r6_deps, r6_resolve_topic) } r6_resolve_topic <- function(topic, topics) { docs_list <- topic$get_value("r6_class") if (is.null(docs_list)) { return() } topic_name <- topic$get_name() resolved <- lapply(docs_list, function(docs) { r6_resolve_class(docs, topic_name, topics) }) # Update stored docs (now resolved, so child classes can read them) topic$sections$r6_class <- rd_section("r6_class", resolved) # Format and inject into topic. Each class produces its own block of Rd. rd_lines <- unlist(lapply(resolved, format)) topic$add(rd_section("rawRd", paste(rd_lines, collapse = "\n"))) # Add combined examples across all methods of all classes in the topic all_methods <- list( self = unlist(lapply(resolved, \(d) d$methods$self), recursive = FALSE) ) ex_lines <- r6_all_examples(all_methods) if (length(ex_lines) > 0) { ex_txt <- paste0(ex_lines, collapse = "\n") topic$add(rd_section("examples", ex_txt), overwrite = FALSE) } } r6_resolve_class <- function(docs, topic_name, topics) { # Collect resolved parent docs from already-processed topics parent_docs <- list() for (classname in docs$superclasses$classname) { parent_file <- topics$find_filename(classname) if (is.na(parent_file)) { next } parent_topic <- topics$get(parent_file) if (is.null(parent_topic)) { next } parent_r6 <- parent_topic$get_value("r6_class") # Parent topic may contain multiple R6 classes; pick the one by name parent_docs[[classname]] <- parent_r6[[classname]] } # Inherit fields and active bindings docs$fields <- r6_resolve_fields(docs$fields, parent_docs, topic_name) docs$active_bindings <- r6_resolve_fields( docs$active_bindings, parent_docs, topic_name ) # Inherit method params docs$methods$self <- lapply(docs$methods$self, function(method) { r6_resolve_method_params(method, parent_docs, topic_name) }) docs } # Field inheritance ---------------------------------------------------------- r6_resolve_fields <- function(fields_obj, parent_docs, topic_name) { label <- if (fields_obj$type == "field") "field" else "active binding" section <- if (fields_obj$type == "field") "fields" else "active_bindings" docd <- r6_field_names(fields_obj$fields) expected <- fields_obj$expected miss <- setdiff(expected, docd) inherited <- r6_find_super_fields(miss, parent_docs, section) fields_obj$fields <- c(fields_obj$fields, inherited) docd <- c(docd, r6_field_names(inherited)) miss <- setdiff(expected, docd) if (length(miss) > 0) { warn_roxy_topic(topic_name, "Undocumented R6 {label}{?s}: {miss}") } fields_obj } r6_find_super_fields <- function(missing, parent_docs, section) { if (length(missing) == 0 || length(parent_docs) == 0) { return(list()) } result <- list() for (super_doc in parent_docs) { if (is.null(super_doc)) { next } for (field in super_doc[[section]]$fields) { if (field$name %in% missing) { result <- c(result, list(field)) missing <- setdiff(missing, field$name) } } if (length(missing) == 0) break } result } # Param inheritance ---------------------------------------------------------- # Inherit params from parent classes (child -> parent -> grandparent). # Within-class param resolution (method -> class-level -> @field) is handled # earlier by r6_resolve_params(). r6_resolve_method_params <- function(method, parent_docs, topic_name) { fnames <- names(method$formals) if (length(fnames) == 0) { return(method) } miss <- setdiff(fnames, r6_param_names(method$params)) if (length(miss) > 0) { inherited <- r6_find_super_params(method$name, miss, parent_docs) method$params <- c(method$params, inherited) # Re-order according to formals firstnames <- map_chr( strsplit(map_chr(method$params, \(x) x$name), ","), \(x) trimws(x[[1]]) ) method$params <- method$params[order(match(firstnames, fnames))] } # Only now, after two rounds of resolution can we warn about missing params. miss <- setdiff(fnames, r6_param_names(method$params)) for (m in miss) { warn_roxy_topic( topic_name, c( "Must use one @param for each argument", x = "{method$name}({m}) is not documented" ) ) } method } r6_find_super_params <- function(method_name, missing, parent_docs) { if (length(parent_docs) == 0) { return(list()) } for (super_doc in parent_docs) { if (is.null(super_doc)) { next } super_method <- Find( \(m) m$name == method_name, super_doc$methods$self ) if (is.null(super_method)) { next } result <- list() for (param in super_method$params) { param_names <- trimws(unlist(strsplit(param$name, ","))) if (any(param_names %in% missing)) { result <- c(result, list(param)) missing <- setdiff(missing, param_names) } } if (length(result) > 0) return(result) } list() } roxygen2/R/tag-parser.R0000644000176200001440000001702315172443612014430 0ustar liggesusers#' Parse tags #' #' These functions parse the `raw` tag value, convert a string into a richer R #' object and storing it in `val`, or provide an informative warning and #' returning `NULL`. #' #' @section New tag: #' To create a new `@mytag` define `roxy_tag_parse.roxy_tag_mytag()`. It should #' either call one of the functions here, or directly set `x$val`. #' #' @param x A [roxy_tag] object to parse #' @returns A [roxy_tag] object with the `val` field set to the parsed value. #' @name tag_parsers #' @family extending NULL #' @export #' @rdname tag_parsers #' @param multiline If `FALSE` (the default), tags that span multiple lines #' will generate a warning. Set to `TRUE` for tags where multiline content #' is expected (e.g., `@usage`, `@rawRd`). tag_value <- function(x, multiline = FALSE) { x$val <- trimws(x$raw) if (x$val == "") { warn_roxy_tag(x, "requires a value") return(NULL) } warn_if_multiline(x, x$val, multiline) if (!rdComplete(x$raw, is_code = FALSE)) { warn_roxy_tag(x, "has mismatched braces or quotes") return(NULL) } x } # Also recorded in tags.yml inherit_components <- c( "params", "return", "title", "description", "details", "seealso", "sections", "references", "examples", "author", "source", "note", "format" ) #' @export #' @rdname tag_parsers tag_inherit <- function(x) { if (trimws(x$raw) == "") { warn_roxy_tag(x, "requires a value") NULL } else if (!rdComplete(x$raw, is_code = FALSE)) { warn_roxy_tag(x, "has mismatched braces or quotes") NULL } else { pieces <- strsplit(trimws(x$raw), "\\s+")[[1]] fields <- pieces[-1] all <- inherit_components if (length(fields) == 0) { fields <- all } else { unknown <- setdiff(fields, all) if (length(unknown) > 0) { warn_roxy_tag( x, "attempts to inherit from unknown type {.str {unknown}}" ) fields <- intersect(fields, all) } } x$val <- list( source = pieces[1], fields = fields ) x } } #' @export #' @rdname tag_parsers tag_name <- function(x) { if (trimws(x$raw) == "") { warn_roxy_tag(x, "requires a value") NULL } else if (!rdComplete(x$raw, is_code = FALSE)) { warn_roxy_tag(x, "has mismatched braces or quotes") NULL } else { n <- re_count(x$raw, "\\s+") if (n > 1) { warn_roxy_tag(x, "must have only one argument, not {n + 1}") NULL } else { x$val <- trimws(x$raw) x } } } #' @export #' @rdname tag_parsers #' @param first,second Name of first and second parts of two part tags #' @param required Is the second part required (TRUE) or can it be blank #' (FALSE)? #' @param markdown Should the second part be parsed as markdown? tag_two_part <- function( x, first, second, required = TRUE, markdown = TRUE, multiline = FALSE ) { if (trimws(x$raw) == "") { if (!required) { warn_roxy_tag(x, "requires {first}") } else { warn_roxy_tag(x, "requires two parts: {first} and {second}") } NULL } else if (!rdComplete(x$raw, is_code = FALSE)) { warn_roxy_tag(x, "has mismatched braces or quotes") NULL } else { warn_if_multiline(x, trimws(x$raw), multiline) pieces <- split_two_part(trimws(x$raw)) if (required && pieces[[2]] == "") { warn_roxy_tag(x, "requires two parts: {first} and {second}") return(NULL) } pieces[[2]] <- trim_docstring(pieces[[2]]) if (markdown) { pieces[[2]] <- markdown_if_active(pieces[[2]], x) } x$val <- list( name = pieces[[1]], description = pieces[[2]] ) x } } # Split a string into two parts: a name and a description. # Handles backtick-quoted names that may contain spaces (e.g. `arg 1`). split_two_part <- function(x) { if (grepl("^`", x)) { match <- regexpr("^`[^`]*`", x) if (match == -1L || attr(match, "match.length") == -1L) { # No closing backtick; fall back to space splitting re_split_half(x, "[[:space:]]+") } else { end <- attr(match, "match.length") # Strip backticks so name matches names(formals(fn)) name <- substr(x, 2, end - 1) rest <- trimws(substr(x, end + 1, nchar(x))) c(name, rest) } } else { re_split_half(x, "[[:space:]]+") } } #' @export #' @rdname tag_parsers tag_name_description <- function(x) { tag_two_part(x, "a name", "a description") } #' @export #' @rdname tag_parsers #' @param min,max Minimum and maximum number of words tag_words <- function(x, min = 0, max = Inf, multiline = FALSE) { val <- trimws(x$raw) warn_if_multiline(x, val, multiline) if (!rdComplete(x$raw, is_code = FALSE)) { warn_roxy_tag(x, "has mismatched braces or quotes") return(NULL) } words <- if (nzchar(val)) strsplit(val, "\\s+")[[1]] else "" if (length(words) < min) { warn_roxy_tag(x, "must have at least {min} word{?s}, not {length(words)}") NULL } else if (length(words) > max) { warn_roxy_tag(x, "must have at most {max} word{?s}, not {length(words)}") NULL } else { x$val <- words x } } #' @export #' @rdname tag_parsers tag_words_line <- function(x) { lifecycle::deprecate_warn("8.0.0", "tag_words_line()", "tag_words()") tag_words(x) } # Warns if multiline is FALSE and val contains multiple lines. warn_if_multiline <- function(x, val, multiline) { if (multiline) { return(invisible()) } n_lines <- re_count(val, "\n") if (n_lines >= 1) { first_line <- re_split_half(val, "\n")[[1]] warn_roxy_tag( x, c( "must be only 1 line long, not {n_lines + 1}", i = "The first line is {.str {first_line}}" ) ) } invisible() } #' @export #' @rdname tag_parsers tag_toggle <- function(x) { x$val <- trimws(x$raw) if (x$val != "") { warn_roxy_tag(x, "must not be followed by any text") NULL } else { x } } #' @export #' @rdname tag_parsers tag_code <- function(x) { if (trimws(x$raw) == "") { warn_roxy_tag(x, "requires a value") NULL } else { tryCatch( { x$val <- parse(text = x$raw) x }, error = function(e) { warn_roxy_tag(x, "failed to parse", parent = e) NULL } ) } } # Examples need special parsing because escaping rules are different #' @export #' @rdname tag_parsers tag_examples <- function(x) { if (trimws(x$raw) == "") { warn_roxy_tag(x, "requires a value") return(NULL) } x$val <- escape_examples(gsub("^\n", "", x$raw)) if (!rdComplete(x$val, is_code = TRUE)) { warn_roxy_tag(x, "has mismatched braces or quotes") NULL } else { x } } #' @export #' @rdname tag_parsers tag_markdown <- function(x) { if (trimws(x$raw) == "") { warn_roxy_tag(x, "requires a value") NULL } else { x$val <- markdown_if_active(x$raw, x) x } } #' @export #' @rdname tag_parsers tag_markdown_with_sections <- function(x) { if (trimws(x$raw) == "") { warn_roxy_tag(x, "requires a value") return(NULL) } x$val <- markdown_if_active(x$raw, x, sections = TRUE) x } markdown_if_active <- function(text, tag, sections = FALSE) { if (markdown_on()) { out <- markdown(text, tag, sections) for (i in seq_along(out)) { if (sections && !rdComplete(out[[i]], is_code = FALSE)) { warn_roxy_tag(tag, "has mismatched braces or quotes") out[[i]] <- "" } else { out[[i]] <- trimws(out[[i]]) } } out } else { if (!rdComplete(text, is_code = FALSE)) { warn_roxy_tag(tag, "has mismatched braces or quotes") "" } else { trimws(text) } } } roxygen2/R/object-defaults.R0000644000176200001440000001001215166171276015434 0ustar liggesusersobject_defaults <- function(x, block) UseMethod("object_defaults") #' @export object_defaults.default <- function(x, block) list() #' @export object_defaults.r6method <- function(x, block) { list( roxy_generated_tag(block, "R6method", x$value) ) } #' @export object_defaults.function <- function(x, block) { list( roxy_generated_tag(block, "usage", object_usage(x)), # Used in process_inherit_params() roxy_generated_tag(block, ".formals", names(formals(x$value))) ) } #' @export object_defaults.s3generic <- object_defaults.function #' @export object_defaults.s3method <- object_defaults.function #' @export object_defaults.s4generic <- object_defaults.function #' @export object_defaults.s4method <- object_defaults.function #' @export object_defaults.data <- function(x, block) { str_out <- rd(object_format(x$value)) list( roxy_generated_tag(block, "docType", "data"), roxy_generated_tag(block, "format", str_out), roxy_generated_tag(block, "keywords", "datasets"), roxy_generated_tag(block, "usage", object_usage(x)) ) } #' @export object_defaults.value <- function(x, block) { list( roxy_generated_tag(block, "usage", object_usage(x)) ) } #' @export object_defaults.package <- function(x, block) { desc <- x$value$desc logo_path <- file.path(x$value$path, "man", "figures", "logo.svg") if (!file.exists(logo_path)) { logo_path <- file.path(x$value$path, "man", "figures", "logo.png") } if (file.exists(logo_path)) { logo_file <- basename(logo_path) fig <- paste_c( c("\\if{html}{\\figure{", logo_file, "}"), "{options: style='float: right' alt='logo' width='120'}}\n\n" ) } else { fig <- "" } name <- desc$get_field("Package") title <- desc$get_field("Title") description <- desc$get_field("Description") description <- package_url_parse(description) description <- paste0(fig, description) seealso <- package_seealso( desc$get_field("URL", NULL), desc$get_field("BugReports", NULL) ) authors <- package_authors(desc$get_field("Authors@R", NULL)) list( roxy_generated_tag(block, ".package", name), roxy_generated_tag(block, "docType", "package"), roxy_generated_tag(block, "name", package_suffix(name)), # default aliases are added in topics_add_package_alias() roxy_generated_tag(block, "title", paste0(name, ": ", title)), roxy_generated_tag(block, "description", description), roxy_generated_tag(block, "seealso", seealso), roxy_generated_tag(block, "author", authors) ) } #' @export object_defaults.import <- function(x, block) { importFrom <- roxy_generated_tag( block, "importFrom", c(x$value$pkg, x$value$fun) ) if (block_has_tags(block, c("rdname", "name"))) { obj <- object_from_name(x$value$fun, asNamespace(x$value$pkg), block) return(c(list(importFrom), object_defaults(obj, block))) } list( importFrom, roxy_generated_tag(block, "docType", "import"), roxy_generated_tag(block, "name", "reexports"), roxy_generated_tag(block, "keywords", "internal"), roxy_generated_tag(block, "title", "Objects exported from other packages"), roxy_generated_tag( block, ".reexport", list(pkg = x$value$pkg, fun = x$value$fun) ) ) } #' @export object_defaults.s7class <- object_defaults.function #' @export object_defaults.s7generic <- object_defaults.function #' @export object_defaults.s7method <- function(x, block) { list( roxy_generated_tag(block, "usage", object_usage(x)), roxy_generated_tag(block, ".formals", names(formals(x$value$fn))) ) } #' @export object_defaults.s4class <- function(x, block) { list( roxy_generated_tag(block, "docType", "class") ) } #' @export object_defaults.rcclass <- function(x, block) { list( roxy_generated_tag(block, "docType", "class"), if (!is.null(x$methods)) { roxy_generated_tag(block, ".methods", x$methods) } ) } # Helpers ----------------------------------------------------------------- package_suffix <- function(name) { paste0(name, "-package") } roxygen2/R/object-package.R0000644000176200001440000002670515166171276015240 0ustar liggesuserspackage_seealso <- function(URL, BugReports) { itemize("Useful links:", package_seealso_urls(URL, BugReports)) } package_seealso_urls <- function(URL = NULL, BugReports = NULL) { if (!is.null(URL)) { links <- strsplit(URL, ",\\s+")[[1]] links <- map_chr(links, wrap_urls) links <- gsub("\\url\\{https://doi.org/", "\\doi{", links) } else { links <- character() } if (!is.null(BugReports)) { links <- c(links, paste0("Report bugs at \\url{", escape(BugReports), "}")) } links } wrap_urls <- function(x) { gsub( "(https?://[^\\s,]+|ftp://[^\\s,]+)", "\\\\url{\\1}", escape(x), perl = TRUE ) } package_authors <- function(authors) { authors <- tryCatch(eval(parse(text = authors %||% "")), error = function(e) { cli::cli_inform(c(x = "Failed to evaluate Authors@R."), parent = e) NULL }) if (is.null(authors)) { return() } desc <- map_chr(unclass(authors), author_desc) type <- map_chr(unclass(authors), author_type) # People who are both maintainer and author should appear in both sections is_cre_aut <- map_lgl(unclass(authors), \(x) all(c("cre", "aut") %in% x$role)) by_type <- split(desc, type) by_type$aut <- c(desc[is_cre_aut], by_type$aut) paste( c( paste0("\\strong{Maintainer}: ", by_type$cre[[1]], "\n"), itemize("Authors:", by_type$aut), itemize("Other contributors:", by_type$other) ), collapse = "\n" ) } author_desc <- function(x) { if (inherits(x, "person")) { cli::cli_abort("Person class must be stripped.", .internal = FALSE) } desc <- paste0(x$given, collapse = " ") if (!is.null(x$family)) { desc <- paste0(desc, " ", paste0(x$family, collapse = " ")) } if (!is.null(x$email)) { desc <- paste0(desc, " ", paste0("\\email{", x$email, "}", collapse = " ")) } if (!is.null(x$comment)) { if (has_name(x$comment, "ORCID")) { orcid <- x$comment[["ORCID"]] if (grepl("https?://", orcid)) { desc <- paste0(desc, " (\\href{", orcid, "}{ORCID})") } else { desc <- paste0(desc, " (\\href{https://orcid.org/", orcid, "}{ORCID})") } x$comment <- x$comment[!names(x$comment) %in% "ORCID"] } if (has_name(x$comment, "ROR")) { ror <- x$comment[["ROR"]] if (grepl("https?://", ror)) { desc <- paste0(desc, " (\\href{", ror, "}{ROR})") } else { desc <- paste0(desc, " (\\href{https://ror.org/", ror, "}{ROR})") } x$comment <- x$comment[!names(x$comment) %in% "ROR"] } if (length(x$comment) > 0) { sep <- ifelse(nzchar(names2(x$comment)), ": ", "") comments <- paste0(names2(x$comment), sep, x$comment) desc <- paste0(desc, " (", paste(comments, collapse = ", "), ")") } } extra_roles <- setdiff(x$role, c("cre", "aut")) if (length(extra_roles) > 0) { desc <- paste0( desc, " [", paste0(role_lookup[extra_roles], collapse = ", "), "]" ) } desc } author_type <- function(x) { if ("cre" %in% x$role) { "cre" } else if ("aut" %in% x$role) { "aut" } else { "other" } } role_lookup <- c( "abr" = "abridger", "act" = "actor", "adp" = "adapter", "rcp" = "addressee", "anl" = "analyst", "anm" = "animator", "ann" = "annotator", "apl" = "appellant", "ape" = "appellee", "app" = "applicant", "arc" = "architect", "arr" = "arranger", "acp" = "art copyist", "adi" = "art director", "art" = "artist", "ard" = "artistic director", "asg" = "assignee", "asn" = "associated name", "att" = "attributed name", "auc" = "auctioneer", "aut" = "author", "aqt" = "author in quotations or text abstracts", "aft" = "author of afterword, colophon, etc.", "aud" = "author of dialog", "aui" = "author of introduction, etc.", "ato" = "autographer", "ant" = "bibliographic antecedent", "bnd" = "binder", "bdd" = "binding designer", "blw" = "blurb writer", "bkd" = "book designer", "bkp" = "book producer", "bjd" = "bookjacket designer", "bpd" = "bookplate designer", "bsl" = "bookseller", "brl" = "braille embosser", "brd" = "broadcaster", "cll" = "calligrapher", "ctg" = "cartographer", "cas" = "caster", "cns" = "censor", "chr" = "choreographer", "cng" = "cinematographer", "cli" = "client", "cor" = "collection registrar", "col" = "collector", "clt" = "collotyper", "clr" = "colorist", "cmm" = "commentator", "cwt" = "commentator for written text", "com" = "compiler", "cpl" = "complainant", "cpt" = "complainant-appellant", "cpe" = "complainant-appellee", "cmp" = "composer", "cmt" = "compositor", "ccp" = "conceptor", "cnd" = "conductor", "con" = "conservator", "csl" = "consultant", "csp" = "consultant to a project", "cos" = "contestant", "cot" = "contestant-appellant", "coe" = "contestant-appellee", "cts" = "contestee", "ctt" = "contestee-appellant", "cte" = "contestee-appellee", "ctr" = "contractor", "ctb" = "contributor", "cpc" = "copyright claimant", "cph" = "copyright holder", "crr" = "corrector", "crp" = "correspondent", "cst" = "costume designer", "cou" = "court governed", "crt" = "court reporter", "cov" = "cover designer", "cre" = "creator", "cur" = "curator", "dnc" = "dancer", "dtc" = "data contributor", "dtm" = "data manager", "dte" = "dedicatee", "dto" = "dedicator", "dfd" = "defendant", "dft" = "defendant-appellant", "dfe" = "defendant-appellee", "dgg" = "degree granting institution", "dgs" = "degree supervisor", "dln" = "delineator", "dpc" = "depicted", "dpt" = "depositor", "dsr" = "designer", "drt" = "director", "dis" = "dissertant", "dbp" = "distribution place", "dst" = "distributor", "dnr" = "donor", "drm" = "draftsman", "dub" = "dubious author", "edt" = "editor", "edc" = "editor of compilation", "edm" = "editor of moving image work", "elg" = "electrician", "elt" = "electrotyper", "enj" = "enacting jurisdiction", "eng" = "engineer", "egr" = "engraver", "etr" = "etcher", "evp" = "event place", "exp" = "expert", "fac" = "facsimilist", "fld" = "field director", "fmd" = "film director", "fds" = "film distributor", "flm" = "film editor", "fmp" = "film producer", "fmk" = "filmmaker", "fpy" = "first party", "frg" = "forger", "fmo" = "former owner", "fnd" = "funder", "gis" = "geographic information specialist", "hnr" = "honoree", "hst" = "host", "his" = "host institution", "ilu" = "illuminator", "ill" = "illustrator", "ins" = "inscriber", "itr" = "instrumentalist", "ive" = "interviewee", "ivr" = "interviewer", "inv" = "inventor", "isb" = "issuing body", "jud" = "judge", "jug" = "jurisdiction governed", "lbr" = "laboratory", "ldr" = "laboratory director", "lsa" = "landscape architect", "led" = "lead", "len" = "lender", "lil" = "libelant", "lit" = "libelant-appellant", "lie" = "libelant-appellee", "lel" = "libelee", "let" = "libelee-appellant", "lee" = "libelee-appellee", "lbt" = "librettist", "lse" = "licensee", "lso" = "licensor", "lgd" = "lighting designer", "ltg" = "lithographer", "lyr" = "lyricist", "mfp" = "manufacture place", "mfr" = "manufacturer", "mrb" = "marbler", "mrk" = "markup editor", "med" = "medium", "mdc" = "metadata contact", "mte" = "metal-engraver", "mtk" = "minute taker", "mod" = "moderator", "mon" = "monitor", "mcp" = "music copyist", "msd" = "musical director", "mus" = "musician", "nrt" = "narrator", "osp" = "onscreen presenter", "opn" = "opponent", "orm" = "organizer", "org" = "originator", "oth" = "other", "own" = "owner", "pan" = "panelist", "ppm" = "papermaker", "pta" = "patent applicant", "pth" = "patent holder", "pat" = "patron", "prf" = "performer", "pma" = "permitting agency", "pht" = "photographer", "ptf" = "plaintiff", "ptt" = "plaintiff-appellant", "pte" = "plaintiff-appellee", "plt" = "platemaker", "pra" = "praeses", "pre" = "presenter", "prt" = "printer", "pop" = "printer of plates", "prm" = "printmaker", "prc" = "process contact", "pro" = "producer", "prn" = "production company", "prs" = "production designer", "pmn" = "production manager", "prd" = "production personnel", "prp" = "production place", "prg" = "programmer", "pdr" = "project director", "pfr" = "proofreader", "prv" = "provider", "pup" = "publication place", "pbl" = "publisher", "pbd" = "publishing director", "ppt" = "puppeteer", "rdd" = "radio director", "rpc" = "radio producer", "rce" = "recording engineer", "rcd" = "recordist", "red" = "redaktor", "ren" = "renderer", "rpt" = "reporter", "rps" = "repository", "rth" = "research team head", "rtm" = "research team member", "res" = "researcher", "rsp" = "respondent", "rst" = "respondent-appellant", "rse" = "respondent-appellee", "rpy" = "responsible party", "rsg" = "restager", "rsr" = "restorationist", "rev" = "reviewer", "rbr" = "rubricator", "sce" = "scenarist", "sad" = "scientific advisor", "aus" = "screenwriter", "scr" = "scribe", "scl" = "sculptor", "spy" = "second party", "sec" = "secretary", "sll" = "seller", "std" = "set designer", "stg" = "setting", "sgn" = "signer", "sng" = "singer", "sds" = "sound designer", "spk" = "speaker", "spn" = "sponsor", "sgd" = "stage director", "stm" = "stage manager", "stn" = "standards body", "str" = "stereotyper", "stl" = "storyteller", "sht" = "supporting host", "srv" = "surveyor", "tch" = "teacher", "tcd" = "technical director", "tld" = "television director", "tlp" = "television producer", "ths" = "thesis advisor", "trc" = "transcriber", "trl" = "translator", "tyd" = "type designer", "tyg" = "typographer", "uvp" = "university place", "vdg" = "videographer", "vac" = "voice actor", "wit" = "witness", "wde" = "wood engraver", "wdc" = "woodcutter", "wam" = "writer of accompanying material", "wac" = "writer of added commentary", "wal" = "writer of added lyrics", "wat" = "writer of added text", "win" = "writer of introduction", "wpr" = "writer of preface", "wst" = "writer of supplementary textual content" ) itemize <- function(header, x) { if (length(x) == 0) { return() } paste0( header, "\n", "\\itemize{\n", paste0(" \\item ", x, "\n", collapse = ""), "}\n" ) } package_url_parse <- function(x) { # -> \doi{XX.XXX} to avoid CRAN Notes, etc. # URL-decode %XX sequences because \doi{} is fully verbatim: # raw % starts an Rd comment and \% is not supported (#1321) x <- re_replace_all(x, "<(doi|DOI):(.*?)>", function(match) { match <- gsub("^<(doi|DOI):|>$", "", match) match <- utils::URLdecode(match) paste0("\\doi{", match, "}") }) # -> \url{http:XX.XXX} x <- re_replace_all(x, "<(http|https):\\/\\/(.*?)>", function(match) { match <- gsub("^<|>$", "", match) paste0("\\url{", escape(match), "}") }) # -> \href{https://arxiv.org/abs/XXX}{arXiv:XXX} # https://github.com/wch/r-source/blob/trunk/src/library/tools/R/Rd2pdf.R#L149-L151 patt_arxiv <- "<(arXiv:|arxiv:)([[:alnum:]/.-]+)([[:space:]]*\\[[^]]+\\])?>" x <- re_replace_all(x, patt_arxiv, function(match) { match <- gsub("^<(arXiv:|arxiv:)|>$", "", match) # Special cases has . # See https://CRAN.R-project.org/package=ciccr # Extract arxiv id, split by space arxiv_id <- re_split_half(match, " ")[[1]] paste0( "\\href{https://arxiv.org/abs/", escape(arxiv_id), "}{arXiv:", match, "}" ) }) x } roxygen2/R/rd-r6-methods.R0000644000176200001440000001030415165177023014753 0ustar liggesusersrd_r6_methods <- function(alias, self = list(), inherited = rd_r6_inherited()) { structure( list(alias = alias, self = self, inherited = inherited), class = "rd_r6_methods" ) } #' @export format.rd_r6_methods <- function(x, ...) { if (length(x$self) == 0) { return() } lines <- character() push <- function(...) lines <<- c(lines, ...) nms <- map_chr(x$self, \(m) m$name) classes <- map_chr(x$self, \(m) m$class) dest <- sprintf("method-%s-%s", classes, nms) code <- sprintf("\\code{%s()}", r6_method_name(classes, nms)) push("\\section{Methods}{") push( "\\subsection{Public methods}{", " \\itemize{", sprintf(" \\item \\href{#%s}{%s}", dest, code), " }", "}" ) push(format(x$inherited)) for (method in x$self) { push(format(method)) } push("}") lines } r6_extract_methods <- function(r6data, alias, block) { self <- r6data$self methods_df <- self[self$type == "method", ] methods_df <- methods_df[order(methods_df$file, methods_df$line), ] methods_df$tags <- replicate(nrow(methods_df), list(), simplify = FALSE) # Move method tags to methods data structure for (i in seq_along(block$tags)) { tag <- block$tags[[i]] if (r6_tag_type(tag, block) != "method") { next } # Tags from external blocks already have method name; ow need to find meth <- tag$r6method %||% find_method_for_tag(methods_df, tag) midx <- which(meth == methods_df$name) if (length(midx) == 0) { warn_roxy_tag(tag, "can't find matching R6 method") next } methods_df$tags[[midx]] <- c(methods_df$tags[[midx]], list(tag)) } # Flatten markdown sections for (i in seq_along(methods_df$tags)) { methods_df$tags[[i]] <- lapply(methods_df$tags[[i]], r6_flatten_sections) } methods_df <- add_default_methods(methods_df, block) nodoc <- map_int(methods_df$tags, length) == 0 if (any(nodoc)) { warn_roxy_block( block, "Undocumented R6 method{?s}: {methods_df$name[nodoc]}" ) } # Methods with @noRd are deliberately suppressed has_noRd <- map_lgl(methods_df$tags, function(tags) { any(map_lgl(tags, \(t) t$tag == "noRd")) }) methods_df <- methods_df[!has_noRd, ] self_methods <- lapply( seq_len(nrow(methods_df)), function(i) r6_method_from_row(methods_df[i, ], block) ) inherited <- r6_extract_inherited_methods(r6data) rd_r6_methods(alias, self = self_methods, inherited = inherited) } add_default_methods <- function(methods, block) { defaults <- list( clone = list( roxy_generated_tag( block, "description", "The objects of this class are cloneable with this method." ), roxy_generated_tag( block, "param", list(name = "deep", description = "Whether to make a deep clone.") ) ) ) for (mname in names(defaults)) { mline <- match(mname, methods$name) if (is.na(mline)) { next } if (length(methods$tags[[mline]]) > 0) { next } methods$tags[[mline]] <- defaults[[mname]] } methods } find_method_for_tag <- function(methods, tag) { if (nrow(methods) == 0) { return(NA_character_) } if (tag$file == "") { # for testing same_file <- TRUE } else { same_file <- basename(methods$file) == basename(tag$file) } w <- which(same_file & methods$line > tag$line)[1] methods$name[w] } r6_flatten_sections <- function(tag) { if (!tag$tag %in% c("description", "details")) { return(tag) } if (length(tag$val) <= 1) { return(tag) } titles <- names(tag$val) sections <- map_chr( seq_along(tag$val)[-1], \(i) paste0("\\subsection{", titles[[i]], "}{\n", tag$val[[i]], "\n}") ) parts <- if (nzchar(tag$val[[1]])) c(tag$val[[1]], sections) else sections tag$val <- paste(parts, collapse = "\n\n") tag } r6_all_examples <- function(methods) { unlist(lapply(methods$self, function(method) { if (length(method$examples) == 0) { return() } c( "\n## ------------------------------------------------", paste0("## Method `", r6_method_name(method$class, method$name), "()`"), "## ------------------------------------------------\n", paste(method$examples, collapse = "\n") ) })) } roxygen2/R/cpp11.R0000644000176200001440000000126615165177051013314 0ustar liggesusers# Generated by cpp11: do not edit by hand escapeExamples <- function(x) { .Call(`_roxygen2_escapeExamples`, x) } findEndOfTag <- function(string, is_code, start) { .Call(`_roxygen2_findEndOfTag`, string, is_code, start) } rdComplete <- function(string, is_code) { .Call(`_roxygen2_rdComplete`, string, is_code) } leadingSpaces <- function(lines) { .Call(`_roxygen2_leadingSpaces`, lines) } tokenise_block <- function(lines, file, offset) { .Call(`_roxygen2_tokenise_block`, lines, file, offset) } find_includes <- function(path) { .Call(`_roxygen2_find_includes`, path) } wrapUsage <- function(string, width, indent) { .Call(`_roxygen2_wrapUsage`, string, width, indent) } roxygen2/R/tag-metadata.R0000644000176200001440000000556515166171276014733 0ustar liggesusers#' Access metadata about built-in or available tags #' #' @export #' @family extending #' @param built_in Logical. Whether to restrict the result to built-in tags #' (`TRUE`) or to extend it to available tags (`FALSE`). tags_list <- function(built_in = TRUE) { if (isTRUE(built_in)) { methods <- attr(methods('roxy_tag_parse'), "info") methods <- methods[methods$from == "roxygen2", ] methods <- rownames(methods)[-1] } else { # Needed since the info attribute doesn't seem to exist # during R CMD check methods <- as.character(methods('roxy_tag_parse'))[-1] } sort(sub('.*\\.roxy_tag_', '', methods)) } #' @export #' @rdname tags_list tags_metadata <- function() { check_installed("yaml") meta <- yaml::read_yaml(yaml_path()) data.frame( tag = map_chr(meta, \(x) x[["name"]]), description = map_chr(meta, \(x) x[["description"]]), # \n not useful outside of RStudio template = sub("\n", "", map_chr(meta, \(x) x[["template"]] %||% "")), vignette = map_chr(meta, \(x) x[["vignette"]] %||% NA_character_), recommend = map_lgl(meta, \(x) x[["recommend"]] %||% FALSE) ) } yaml_path <- function() { system.file("roxygen2-tags.yml", package = "roxygen2") } tags_rd <- function(type, vignette = type) { tags <- tags_metadata() tags <- tags[tags$vignette == type & !is.na(tags$vignette), ] c( paste0("@name tags-", type), paste0("@aliases ", "@", tags$tag), "@description", paste0("Learn the full details in `vignette('", vignette, "')`."), "", if (any(tags$recommend)) { c( "Key tags:", tags_rd_desc(tags[tags$recommend, ]) ) }, if (any(!tags$recommend)) { c( "Other less frequently used tags:", tags_rd_desc(tags[!tags$recommend, ]) ) }, "@usage", paste0("#' @", tags$tag, tags$template) ) } tags_rd_desc <- function(tags, section) { paste0("* `@", tags$tag, tags$template, "`: ", tags$description) } #' Tags for documenting functions #' #' @eval tags_rd("rd-functions") #' @family documentation tags NULL #' Tags for documenting datasets #' #' @eval tags_rd("rd-datasets") #' @family documentation tags NULL #' Tags for documenting S3 #' #' @eval tags_rd("rd-S3") #' @family documentation tags NULL #' Tags for documenting S4 #' #' @eval tags_rd("rd-S4") #' @family documentation tags NULL #' Tags for documenting S7 #' #' @eval tags_rd("rd-S7") #' @family documentation tags NULL #' Tags for documenting R6 #' #' @eval tags_rd("rd-R6") #' @family documentation tags NULL #' Tags that help you reuse documentation #' #' @eval tags_rd("reuse") #' @family documentation tags NULL #' Tags for managing the `NAMESPACE` #' #' @eval tags_rd("namespace") NULL #' Tags related to markdown support #' #' @eval tags_rd("rd-formatting") NULL #' Tags for indexing and cross-references #' #' @eval tags_rd("index-crossref") #' @family documentation tags NULL roxygen2/R/rd-r6-super.R0000644000176200001440000000252315156520264014451 0ustar liggesusersrd_r6_super <- function( class, package = character(), classname = character(), has_topic = logical() ) { structure( list( class = class, package = package, classname = classname, has_topic = has_topic ), class = "rd_r6_super" ) } #' @export format.rd_r6_super <- function(x, ...) { if (length(x$classname) == 0) { return() } cls <- x$classname pkgs <- x$package ht <- x$has_topic title <- if (length(cls) > 1) "Super classes" else "Super class" self_pkg <- roxy_meta_get("current_package") %||% "" same_pkg <- pkgs == self_pkg label <- ifelse(same_pkg, cls, paste0(pkgs, "::", cls)) path <- map_chr(seq_along(cls), function(i) { if (ht[[i]]) { rd_link(pkgs[[i]], cls[[i]], label[[i]], code = TRUE) } else { paste0("\\code{", label[[i]], "}") } }) me <- sprintf("\\code{%s}", x$class) c( paste0("\\section{", title, "}{"), paste(c(rev(path), me), collapse = " -> "), "}" ) } r6_extract_superclasses <- function(r6data, env, class) { super <- r6data$super cls <- unique(super$classes$classname) if (length(cls) == 0) { return(rd_r6_super(class)) } pkgs <- super$classes$package[match(cls, super$classes$classname)] ht <- map2_lgl(cls, pkgs, has_topic) rd_r6_super(class, package = pkgs, classname = cls, has_topic = ht) } roxygen2/R/topo-sort.R0000644000176200001440000000271315157061144014330 0ustar liggesusersTopoSort <- R6::R6Class( "TopoSort", public = list( vertices = list(), add = function(name) { if (is.null(self$vertices[[name]])) { self$vertices[[name]] <- Vertex$new(name) } invisible(self$vertices[[name]]) }, add_ancestor = function(predecessor_name, ancestor_name) { predecessor <- self$add(predecessor_name) ancestor <- self$add(ancestor_name) predecessor$add_ancestor(ancestor) }, sort = function() { sorted <- list() visit <- function(predecessor) { predecessor$discovered <- TRUE for (ancestor in predecessor$ancestors) { if (!ancestor$discovered) { visit(ancestor) } } sorted <<- append(sorted, predecessor) } for (vertex in self$vertices) { if (!vertex$discovered) { visit(vertex) } } map_chr(sorted, \(x) x[["name"]]) } ) ) Vertex <- R6::R6Class( "Vertex", public = list( name = NA_character_, discovered = FALSE, ancestors = list(), initialize = function(name) { self$name <- name }, has_ancestor = function(ancestor) { for (vertex in self$ancestors) { if (identical(ancestor, vertex)) { return(TRUE) } } FALSE }, add_ancestor = function(ancestor) { if (!self$has_ancestor(ancestor)) { self$ancestors <- append(ancestor, self$ancestors) } } ) ) roxygen2/R/rd-simple.R0000644000176200001440000000202615154610530014247 0ustar liggesusers#' @export roxy_tag_parse.roxy_tag_concept <- function(x) tag_value(x) #' @export roxy_tag_rd.roxy_tag_concept <- function(x, base_path, env) { rd_section(x$tag, x$val) } #' @export format.rd_section_concept <- function(x, ...) { format_rd(x, ...) } #' @export roxy_tag_parse.roxy_tag_docType <- function(x) tag_name(x) #' @export roxy_tag_rd.roxy_tag_docType <- function(x, base_path, env) { rd_section("docType", x$val) } #' @export format.rd_section_docType <- function(x, ...) { format_first(x, ...) } #' @export roxy_tag_parse.roxy_tag_encoding <- function(x) tag_value(x) #' @export roxy_tag_rd.roxy_tag_encoding <- function(x, base_path, env) { rd_section(x$tag, x$val) } #' @export format.rd_section_encoding <- function(x, ...) { format_first(x, ...) } #' @export roxy_tag_parse.roxy_tag_keywords <- function(x) tag_words(x, min = 1) #' @export roxy_tag_rd.roxy_tag_keywords <- function(x, base_path, env) { rd_section("keyword", x$val) } #' @export format.rd_section_keyword <- function(x, ...) { format_rd(x, ...) } roxygen2/R/roxygenize.R0000644000176200001440000000643115157244126014571 0ustar liggesusers#' Document a package with roxygen2 #' #' This is the workhorse function that builds manual pages and metadata for a #' package. It is powered by [roclets][roclet], roxygen2's plugin system for #' producing different types of output. See the documentation of the #' individual components ([rd_roclet()], [namespace_roclet()], #' [update_collate()]) for more details, or learn how to make your own in #' `vignette("extending")`. #' #' Note that roxygen2 is a dynamic documentation system: it works by #' inspecting loaded objects in the package. This means that you must #' be able to load the package in order to document it: see [load] for #' details. #' #' @param package.dir Location of package top level directory. Default is #' working directory. #' @param roclets Character vector of [roclets][roclet] to use. #' #' The default, `NULL`, uses the roxygen `roclets` option, #' which defaults to `c("collate", "namespace", "rd")`. This will update #' (if needed) the `Collate` field with [update_collate()], #' produce the `NAMESPACE` file with [namespace_roclet()], and #' produce the Rd files with [rd_roclet()]. #' #' (Note that `update_collate()` is not technically a roclet but is still #' controlled with this argument for historical reasons.) #' @param load_code A function used to load all the R code in the package #' directory. The default, `NULL`, uses the strategy defined by #' the `load` roxygen option, which defaults to [load_pkgload()]. #' See [load] for more details. #' @param clean If `TRUE`, roxygen will delete all files previously #' created by roxygen before running each roclet. #' @return `NULL` #' @export roxygenize <- function( package.dir = ".", roclets = NULL, load_code = NULL, clean = FALSE ) { check_string(package.dir) check_character(roclets, allow_null = TRUE) load_code <- find_load_strategy(load_code) check_bool(clean) base_path <- normalizePath(package.dir) is_first <- roxygen_setup(base_path) find_package_cache_reset() roxy_meta_load(base_path) # Load required packages for method registration packages <- roxy_meta_get("packages") lapply(packages, loadNamespace) roclets <- roclets %||% roxy_meta_get("roclets") # To load code, we need an up-to-date Collate field and NAMESPACE if ("collate" %in% roclets) { update_collate(base_path) roclets <- setdiff(roclets, "collate") } if ("namespace" %in% roclets) { update_namespace_imports(base_path) } if (length(roclets) == 0) { return(invisible()) } roclets <- lapply(roclets, roclet_find) if (!is_interactive()) { withr::local_options(warn = 1) } # Now load code env <- load_code(base_path) local_roxy_meta_set("env", env) # Tokenise each file blocks <- parse_package(base_path, env = NULL) if (clean) { walk(roclets, roclet_clean, base_path = base_path) } roclets <- lapply( roclets, roclet_preprocess, blocks = blocks, base_path = base_path ) blocks <- lapply(blocks, block_set_env, env = env) results <- lapply( roclets, roclet_process, blocks = blocks, env = env, base_path = base_path ) out <- map2( roclets, results, roclet_output, base_path = base_path, is_first = is_first ) invisible(out) } #' @rdname roxygenize #' @export roxygenise <- roxygenize roxygen2/R/rd-template.R0000644000176200001440000000327315172410643014601 0ustar liggesusers#' @export roxy_tag_parse.roxy_tag_template <- function(x) { tag_value(x) } #' @export roxy_tag_parse.roxy_tag_templateVar <- function(x) { tag_two_part(x, "a variable name", "a value", multiline = TRUE) } process_templates <- function(block, base_path) { tags <- block_get_tags(block, "template") if (length(tags) == 0) { return(block) } templates <- map_chr(tags, \(x) x[["val"]]) paths <- map_chr(templates, template_find, base_path = base_path) var_tags <- block_get_tags(block, "templateVar") vars <- set_names( map(var_tags, \(x) x[["val"]][["description"]]), map_chr(var_tags, \(x) x[["val"]][["name"]]) ) vars <- lapply(vars, utils::type.convert, as.is = TRUE) results <- lapply(paths, template_eval, vars = list2env(vars)) tokens <- lapply(results, tokenise_block, file = "TEMPLATE", offset = 0L) tags <- lapply(tokens, parse_tags) # Insert templates back in the location where they came from block_replace_tags(block, "template", tags) } # Helpers ----------------------------------------------------------------- template_find <- function(base_path, template_name) { file_name <- paste0(template_name, ".", c("R", "r")) path <- c( file.path(base_path, "man-roxygen", file_name), file.path(base_path, "man", "roxygen", "templates", file_name) ) path_exists <- file.exists(path) if (!any(path_exists)) { # This should really use warn_roxy_tag() but it's not worth refactoring # for this rarely used feature cli::cli_abort("Can't find template {.str {template_name}}.", call = NULL) } path[path_exists][[1]] } template_eval <- function(template_path, vars) { utils::capture.output(brew::brew(template_path, envir = vars)) } roxygen2/R/zzz.R0000644000176200001440000000011214263172460013207 0ustar liggesusers.onLoad <- function(...) { as_character_rd <<- make_as_character_rd() } roxygen2/R/rd-eval.R0000644000176200001440000000135515154314256013717 0ustar liggesusersroxy_eval <- function(expr, env) { local_reproducible_output() eval(expr, env) } roxy_knit <- function(text, envir, options) { old_opts <- exec(opts_chunk$set, !!!options) withr::defer(exec(opts_chunk$set, !!!old_opts)) local_reproducible_output() knit(text = text, quiet = TRUE, envir = envir) } # Simplified from testthat::local_reproducible_output local_reproducible_output <- function(.envir = parent.frame()) { withr::local_options( crayon.enabled = FALSE, cli.unicode = FALSE, cli.dynamic = FALSE, cli.hyperlink = FALSE, rlang_interactive = FALSE, width = 80, .local_envir = .envir ) withr::local_envvar(RSTUDIO = NA, .local_envir = .envir) withr::local_collate("C", .local_envir = .envir) } roxygen2/R/rd-s7.R0000644000176200001440000000270315172410643013314 0ustar liggesusers#' @export roxy_tag_parse.roxy_tag_prop <- function(x) { x <- tag_two_part(x, "a property name", "a description", multiline = TRUE) if (is.null(x)) { return() } # Optionally specify a class if (grepl("@", x$val$name, fixed = TRUE)) { pieces <- strsplit(x$val$name, "@", fixed = TRUE)[[1]] if (length(pieces) != 2 || pieces[[1]] == "" || pieces[[2]] == "") { warn_roxy_tag(x, "must have form class@prop") return() } x$val$class <- pieces[[1]] x$val$name <- pieces[[2]] } x } #' @export roxy_tag_rd.roxy_tag_prop <- function(x, base_path, env) { rd_section( x$tag, data.frame( class = x$val$class %||% NA_character_, name = x$val$name, description = x$val$description ) ) } #' @export merge.rd_section_prop <- function(x, y, ...) { stopifnot(identical(class(x), class(y))) rd_section(x$type, rbind(x$value, y$value)) } #' @export format.rd_section_prop <- function(x, ...) { props <- x$value classes <- unique(props$class) if (identical(classes, NA_character_)) { return(rd_section_description( "Additional properties", paste0("@", props$name), props$description )) } sections <- map_chr(classes, function(cls) { rows <- props[props$class == cls, ] rd_subsection_description(cls, paste0("@", rows$name), rows$description) }) paste0( "\\section{Additional properties}{\n\n", paste0(sections, collapse = "\n"), "}\n" ) } roxygen2/R/package_files.R0000644000176200001440000000210315151411610015117 0ustar liggesuserspackage_files <- function(path = ".") { all <- normalizePath(r_files(path)) collate <- desc::desc_get_collate(file = file.path(path, "DESCRIPTION")) collate <- normalizePath(file.path(path, "R", collate)) rfiles <- c(collate, setdiff(all, collate)) ignore_files(rfiles, path) } r_files <- function(path) { sort_c(dir(file.path(path, "R"), "\\.[Rr]$", full.names = TRUE)) } ignore_files <- function(rfiles, path) { rbuildignore <- file.path(path, ".Rbuildignore") if (!file.exists(rbuildignore)) { return(rfiles) } # Strip leading directory and slashes rfiles_relative <- sub( normalizePath(path, winslash = "/"), "", normalizePath(rfiles, winslash = "/"), fixed = TRUE ) rfiles_relative <- sub("^[/]*", "", rfiles_relative) # Remove any files that match any perl-compatible regexp patterns <- read_lines(rbuildignore) patterns <- patterns[patterns != ""] if (length(patterns) == 0L) { return(rfiles) } matches <- lapply(patterns, grepl, rfiles_relative, perl = TRUE) matches <- Reduce("|", matches) rfiles[!matches] } roxygen2/R/markdown-state.R0000644000176200001440000000226715157043533015330 0ustar liggesusers#' @export roxy_tag_parse.roxy_tag_md <- function(x) tag_toggle(x) #' @export roxy_tag_parse.roxy_tag_noMd <- function(x) tag_toggle(x) ## Hacky global switch - this uses the fact that blocks are parsed ## one after the another, and that we set markdown on/off before each ## block markdown_env <- new.env(parent = emptyenv()) markdown_on <- function(value = NULL) { if (!is.null(value)) { assign("markdown-support", isTRUE(value), envir = markdown_env) } return(isTRUE(markdown_env$`markdown-support`)) } local_markdown <- function(env = parent.frame()) { old <- markdown_env$`markdown-support` markdown_on(TRUE) withr::defer(markdown_on(old), envir = env) } markdown_activate <- function(tags) { ## markdown on/off based on global flag and presence of @md & @nomd names <- map_chr(tags, \(x) x[["tag"]]) has_md <- "md" %in% names has_nomd <- "noMd" %in% names if (has_md && has_nomd) { md_tag <- tags[names == "md"][[1]] warn_roxy_tag(md_tag, "conflicts with @noMd; turning markdown parsing off") md <- FALSE } else { md <- roxy_meta_get("markdown", FALSE) if (has_md) { md <- TRUE } if (has_nomd) md <- FALSE } markdown_on(md) } roxygen2/R/roxygenize-setup.R0000644000176200001440000000520615166171276015733 0ustar liggesusersroxygen_setup <- function( path = ".", cur_version = NULL, frame = caller_env() ) { if (!file.exists(file.path(path, "DESCRIPTION"))) { cli::cli_abort( "{.arg package.dir} ({.path {path}}) does not contain a DESCRIPTION file.", call = frame ) } is_first <- first_time(path) if (is_first) { cli::cli_inform( "First time using {.pkg roxygen2}. Upgrading automatically..." ) } update_roxygen_version(path, cur_version = cur_version) encoding <- desc::desc_get("Encoding", path)[[1]] if (!identical(encoding, "UTF-8")) { cli::cli_inform(c( x = "{.pkg roxygen2} requires {.val Encoding: UTF-8}", i = "Current encoding is {.val {encoding}}" )) } man_path <- file.path(path, "man") dir.create(man_path, recursive = TRUE, showWarnings = FALSE) withr::local_envvar( ROXYGEN_PKG = desc::desc_get("Package", path), .local_envir = frame ) is_first } peek_roxygen_pkg <- function() { pkg <- Sys.getenv("ROXYGEN_PKG") if (nzchar(pkg)) { pkg } else { NULL } } update_roxygen_version <- function(path, cur_version = NULL) { cur <- cur_version %||% as.character(utils::packageVersion("roxygen2")) prev <- roxygen_version(path) if ( !is.na(cur) && !is.na(prev) && package_version(cur) < package_version(prev) ) { cli::cli_inform(c( x = "Installed {.pkg roxygen2} is older than the version used with this package", i = "You have {.val {cur}} but you need {.val {prev}}" )) } else if (!identical(cur, prev)) { if (!is.na(prev) && numeric_version(prev) <= "6.1.99") { cli::cli_rule() cli::cli_inform(c( "Changes in {.pkg roxygen2} 7.0.0:", "* `%` is now escaped automatically in Markdown mode.", "Please carefully check .Rd files for changes" )) cli::cli_rule() } cli::cli_inform(c( i = "Setting {.field Config/roxygen2/version} to {.val {cur}}" )) desc::desc_set("Config/roxygen2/version" = cur, file = path) desc::desc_del("RoxygenNote", file = path) } } first_time <- function(path) { if (!is.na(roxygen_version(path))) { return(FALSE) } generated <- dir(file.path(path, "man"), full.names = TRUE) generated <- generated[!file.info(generated)$isdir] namespace <- file.path(path, "NAMESPACE") if (file.exists(namespace)) { generated <- c(generated, namespace) } roxy <- map_lgl(generated, made_by_roxygen) all(!roxy) } roxygen_version <- function(path = ".") { version <- desc::desc_get("Config/roxygen2/version", file = path)[[1]] if (is.na(version)) { version <- desc::desc_get("RoxygenNote", file = path)[[1]] } trimws(version) } roxygen2/R/roxygen2-package.R0000644000176200001440000000123115154610530015516 0ustar liggesusers#' @useDynLib roxygen2, .registration=TRUE #' @keywords internal #' @import rlang "_PACKAGE" ## usethis namespace: start #' @importFrom knitr knit #' @importFrom knitr opts_chunk #' @importFrom lifecycle deprecated #' @importFrom R6 R6Class #' @importFrom stats setNames #' @importFrom utils head #' @importFrom utils tail #' @importFrom utils URLdecode #' @importFrom utils URLencode #' @importFrom xml2 xml_attr #' @importFrom xml2 xml_children #' @importFrom xml2 xml_contents #' @importFrom xml2 xml_find_all #' @importFrom xml2 xml_name #' @importFrom xml2 xml_ns_strip #' @importFrom xml2 xml_text #' @importFrom xml2 xml_type ## usethis namespace: end NULL roxygen2/R/object-import.R0000644000176200001440000000271415157043740015143 0ustar liggesusers# Re-export ---------------------------------------------------------------- rd_section_reexport <- function(pkg, fun) { check_character(pkg) check_character(fun) stopifnot(length(pkg) == length(fun)) rd_section("reexport", list(pkg = pkg, fun = fun)) } #' @export roxy_tag_rd.roxy_tag_.reexport <- function(x, base_path, env) { rd_section_reexport(x$val$pkg, x$val$fun) } #' @export merge.rd_section_reexport <- function(x, y, ...) { stopifnot(identical(class(x), class(y))) rd_section_reexport( c(x$value$pkg, y$value$pkg), c(x$value$fun, y$value$fun) ) } #' @export format.rd_section_reexport <- function(x, ...) { info <- data.frame( pkg = x$value$pkg, fun = x$value$fun ) pkgs <- split(info, x$value$pkg) pkg_links <- map(pkgs, function(pkg) { pkg <- pkg[order(pkg$fun), ] links <- paste0(reexport_link(pkg$pkg, pkg$fun), collapse = ", ") paste0("\\item{", pkg$pkg[[1]], "}{", links, "}") }) paste0( "\\description{\n", "These objects are imported from other packages. Follow the links\n", "below to see their documentation.\n", "\n", "\\describe{\n", paste0(" ", unlist(pkg_links), collapse = "\n\n"), "\n}}\n" ) } reexport_link <- function(pkg, fun) { env <- tryCatch(asNamespace(pkg), error = function(e) emptyenv()) map_chr(seq_along(fun), function(i) { rd_link( pkg[[i]], escape(fun[[i]]), escape(fun_suffix(fun[[i]], env)), code = TRUE ) }) } roxygen2/R/rd-name-alias.R0000644000176200001440000000164615166171276015010 0ustar liggesusers#' @export roxy_tag_parse.roxy_tag_aliases <- function(x) tag_words(x, min = 1) #' @export format.rd_section_alias <- function(x, ...) { x$value <- gsub("%", "\\%", x$value, fixed = TRUE) format_rd(x, ..., sort = FALSE) } #' @export roxy_tag_parse.roxy_tag_name <- function(x) tag_value(x) #' @export format.rd_section_name <- function(x, ...) { x$value <- gsub("%", "\\%", x$value, fixed = TRUE) format_first(x, ...) } topic_add_name_aliases <- function(topic, block, name) { tags <- block_get_tags(block, "aliases") if (length(tags) == 0) { aliases <- character() } else { aliases <- unlist(map(tags, \(x) x[["val"]])) } if (any(aliases == "NULL")) { # Don't add default aliases aliases <- setdiff(aliases, "NULL") } else { aliases <- c(name, block$object$alias, aliases) } aliases <- unique(aliases) topic$add(rd_section("name", name)) topic$add(rd_section("alias", aliases)) } roxygen2/R/object-r6.R0000644000176200001440000001115515156027200014147 0ustar liggesusers#' @export object_defaults.r6class <- function(x, block) { r6on <- roxy_meta_get("r6", TRUE) if (isTRUE(r6on)) { list( roxy_generated_tag(block, "docType", NULL), roxy_generated_tag(block, ".r6data", extract_r6_data(x$value)) ) } else { NextMethod() } } extract_r6_data <- function(x) { list( self = extract_r6_self_data(x), super = drop_clone_maybe(x, extract_r6_super_data(x)) ) } drop_clone_maybe <- function(x, data) { if (!"clone" %in% names(x$public_methods)) { cline <- which(data$members$name == "clone" & data$members$type == "method") if (length(cline)) data$members <- data$members[-cline, ] } data } extract_r6_self_data <- function(x) { rbind( extract_r6_methods(x), extract_r6_fields(x), extract_r6_bindings(x) ) } default_r6_methods <- function() { "clone" } extract_r6_methods <- function(x) { method_nms <- setdiff(names(x$public_methods), default_r6_methods()) method_loc <- map_int( x$public_methods[method_nms], function(m) { ref <- utils::getSrcref(m) if (is.null(ref)) { name <- x$classname %||% "unknown" cli::cli_abort( c( "R6 class {.cls {name}} lacks source references.", i = paste0( "If you are using the `installed` load method in {.file DESCRIPTION}, then ", "try re-installing the package with option '--with-keep.source', e.g. ", "{.code install.packages(..., INSTALL_OPTS = \"--with-keep.source\")}." ) ), call = NULL ) } utils::getSrcLocation(ref) } ) method_fnm <- map_chr( x$public_methods[method_nms], function(m) { utils::getSrcFilename(utils::getSrcref(m)) } ) method_formals <- map(x$public_methods[method_nms], formals) methods <- data.frame( type = if (length(method_loc)) "method" else character(), class = if (length(method_loc)) { x$classname %||% NA_character_ } else { character() }, name = unname(method_nms), file = unname(method_fnm), line = unname(method_loc), formals = I(unname(method_formals)) ) add_default_method_data(x, methods) } add_default_method_data <- function(obj, methods) { pubm <- obj$public_methods defaults <- list( clone = list( formals = if ("clone" %in% names(pubm)) I(list(formals(pubm$clone))) ) ) for (mname in names(defaults)) { if (mname %in% methods$name) { next } if (!mname %in% names(obj$public_methods)) { next } rec <- data.frame( type = defaults[[mname]]$type %||% "method", class = defaults[[mname]]$class %||% obj$classname %||% "unknown", name = defaults[[mname]]$name %||% mname, file = defaults[[mname]]$file %||% NA_character_, line = defaults[[mname]]$line %||% NA_integer_, formals = defaults[[mname]]$formals %||% NULL ) methods <- rbind(methods, rec) } methods } extract_r6_fields <- function(x) { field_nms <- names(x$public_fields) data.frame( type = rep("field", length(field_nms)), name = as.character(field_nms), class = rep(x$classname %||% NA_character_, length(field_nms)), file = rep(NA, length(field_nms)), line = rep(NA, length(field_nms)), formals = I(replicate(length(field_nms), NULL)) ) } extract_r6_bindings <- function(x) { bind_nms <- names(x$active) data.frame( type = if (length(bind_nms)) "active" else character(), name = as.character(bind_nms), class = rep(x$classname %||% NA_character_, length(bind_nms)), file = rep(NA, length(bind_nms)), line = rep(NA, length(bind_nms)), formals = I(replicate(length(bind_nms), NULL)) ) } extract_r6_super_data <- function(x) { if (is.null(x$inherit)) { return() } super <- x$get_inherit() super_data <- extract_r6_super_data(super) method_nms <- names(super$public_methods) field_nms <- names(super$public_fields) active_nms <- names(super$active) classname <- super$classname %||% NA_character_ pkg <- environmentName(topenv(super$parent_env)) cls <- rbind( data.frame( package = pkg, classname = classname ), super_data$classes ) types <- rep( c("method", "field", "active"), c(length(method_nms), length(field_nms), length(active_nms)) ) rsort <- function(x) sort_c(x, decreasing = TRUE) names <- c(rsort(method_nms), rsort(field_nms), rsort(active_nms)) mth <- rbind( data.frame( package = rep(pkg, length(names)), classname = rep(classname, length(names)), type = types, name = names ), super_data$members ) list(classes = cls, members = mth) } roxygen2/R/rd-inherit.R0000644000176200001440000003676215172224131014434 0ustar liggesusers# Tags -------------------------------------------------------------------- #' @export roxy_tag_parse.roxy_tag_inherit <- function(x) tag_inherit(x) #' @export roxy_tag_rd.roxy_tag_inherit <- function(x, base_path, env) { rd_section_inherit(x$val$source, list(x$val$fields)) } #' @export roxy_tag_parse.roxy_tag_inheritParams <- function(x) { tag_two_part( x, "a source", "an argument list", required = FALSE, markdown = FALSE ) } #' @export roxy_tag_rd.roxy_tag_inheritParams <- function(x, base_path, env) { list( rd_section_inherit(x$val$name, list("params")), rd_section_inherit_params_args(x$val$name, x$val$description) ) } #' @export roxy_tag_parse.roxy_tag_inheritDotParams <- function(x) { tag_two_part( x, "a source", "an argument list", required = FALSE, markdown = FALSE ) } #' @export roxy_tag_rd.roxy_tag_inheritDotParams <- function(x, base_path, env) { rd_section_inherit_dot_params(x$val$name, x$val$description) } #' @export roxy_tag_parse.roxy_tag_inheritSection <- function(x) { tag_two_part(x, "a topic name", "a section title") } #' @export roxy_tag_rd.roxy_tag_inheritSection <- function(x, base_path, env) { rd_section_inherit_section(x$val$name, x$val$description) } # Fields ------------------------------------------------------------------ # For each unique source, list which fields it inherits from rd_section_inherit <- function(source, fields) { check_character(source) stopifnot(is.list(fields)) stopifnot(!anyDuplicated(source)) stopifnot(length(source) == length(fields)) rd_section("inherit", list(source = source, fields = fields)) } #' @export merge.rd_section_inherit <- function(x, y, ...) { stopifnot(identical(class(x), class(y))) dedup <- collapse( c(x$value$source, y$value$source), c(x$value$fields, y$value$fields), \(x) Reduce(union, x) ) rd_section("inherit", list(source = dedup$key, fields = dedup$value)) } #' @export format.rd_section_inherit <- function(x, ...) NULL rd_section_inherit_section <- function(source, title) { check_character(source) check_character(title) stopifnot(length(source) == length(title)) rd_section("inherit_section", list(source = source, title = title)) } #' @export format.rd_section_inherit_section <- function(x, ...) NULL #' @export merge.rd_section_inherit_section <- function(x, y, ...) { stopifnot(identical(class(x), class(y))) rd_section_inherit_section( c(x$value$source, y$value$source), c(x$value$title, y$value$title) ) } rd_section_inherit_dot_params <- function(source, args) { check_character(source) check_character(args) stopifnot(length(source) == length(args)) rd_section("inherit_dot_params", list(source = source, args = args)) } #' @export format.rd_section_inherit_dot_params <- function(x, ...) NULL #' @export merge.rd_section_inherit_dot_params <- function(x, y, ...) { stopifnot(identical(class(x), class(y))) rd_section_inherit_dot_params( c(x$value$source, y$value$source), c(x$value$args, y$value$args) ) } rd_section_inherit_params_args <- function(source, args) { check_string(source) check_string(args) if (!nzchar(args)) { return(NULL) } rd_section("inherit_params_args", list(source = source, args = args)) } #' @export format.rd_section_inherit_params_args <- function(x, ...) NULL #' @export merge.rd_section_inherit_params_args <- function(x, y, ...) { stopifnot(identical(class(x), class(y))) rd_section_inherit_params_args( c(x$value$source, y$value$source), c(x$value$args, y$value$args) ) } # Process inheritance ----------------------------------------------------- topics_process_inherit <- function(topics, env) { inherits <- function(type) { \(x) x$inherits_from(type) } topics$topo_apply( inherits("return"), inherit_field, roxy_name = "return", rd_name = "value" ) topics$topo_apply(inherits("title"), inherit_field, "title") topics$topo_apply(inherits("description"), inherit_field, "description") topics$topo_apply(inherits("details"), inherit_field, "details") topics$topo_apply(inherits("seealso"), inherit_field, "seealso") topics$topo_apply(inherits("references"), inherit_field, "references") topics$topo_apply(inherits("examples"), inherit_field, "examples") topics$topo_apply(inherits("author"), inherit_field, "author") topics$topo_apply(inherits("source"), inherit_field, "source") topics$topo_apply(inherits("note"), inherit_field, "note") topics$topo_apply(inherits("note"), inherit_field, "format") # First inherit individual sections, then all sections. topics$topo_apply(function(x) x$inherits_section_from(), inherit_section) topics$topo_apply(inherits("sections"), inherit_sections) topics$topo_apply(inherits("params"), inherit_params) # Can't inherit ... into ... so can do in any order topics$apply(inherit_dot_params, env = env) invisible() } # Inherit parameters ----------------------------------------------------------- inherit_params <- function(topic, topics) { inheritors <- topic$inherits_from("params") if (length(inheritors) == 0) { return() } documented <- get_documented_params(topic) needed <- topic$get_value("formals") missing <- setdiff(needed, documented) if (length(missing) == 0) { warn_roxy_topic( topic$get_name(), c( x = "@inheritParams failed", i = "All parameters are already documented; none remain to be inherited." ) ) return() } # Work through inherited params seeing if any match the parameters # we're missing for (inheritor in inheritors) { source <- topic$get_name() inherited_params <- find_params( inheritor, topics, source = source, tag = "@inheritParams" ) # Apply argument filter if specified via @inheritParams foo args params_args <- topic$get_value("inherit_params_args") args_filter <- params_args$args[params_args$source == inheritor] if (length(args_filter) == 1 && args_filter != "") { doc_args <- map_chr(inherited_params, "[[", "name") selected <- select_args_text(doc_args, args_filter, topic_name = source) inherited_params <- Filter( function(p) any(p$name %in% selected), inherited_params ) } for (param in inherited_params) { match <- match_param(param$name, missing) if (!is.null(match)) { param_val <- setNames(param$value, paste(match, collapse = ",")) topic$add(rd_section("param", param_val)) missing <- setdiff(missing, match) } } if (length(missing) == 0) break } } # Ignore . prefix since it's sometimes necessary to add because a # function uses ... # Match parameters ignoring dots match_param <- function(from, to) { flip_dot <- function(x) { has_dot <- grepl("^\\.", x) ifelse(has_dot, gsub("^\\.", "", x), paste0(".", x)) } to_std <- c(to, flip_dot(to)) if (!all(from %in% to_std)) { return(NULL) } union( setdiff(to[match(from, to)], NA), setdiff(to[match(from, flip_dot(to))], NA) ) } inherit_dot_params <- function(topic, topics, env) { inheritors <- topic$get_value("inherit_dot_params") if (is.null(inheritors)) { return() } # Find documented params for each source — this determines which args are # available, matching how @inheritParams uses docs rather than formals. docs <- lapply( inheritors$source, find_params, topics = topics, source = topic$get_name(), tag = "@inheritDotParams" ) # Get unique documented param names for arg selection doc_args <- lapply(docs, function(d) { unlist(lapply(d, "[[", "name")) }) args <- map2( doc_args, inheritors$args, select_args_text, topic_name = topic$get_name() ) arg_matches <- function(args, docs) { matched_names <- lapply(docs, \(x) match_param(x$name, args)) match <- !map_lgl(matched_names, is.null) setNames( lapply(docs[match], "[[", "value"), map_chr(matched_names[match], paste, collapse = ",") ) } docs_selected <- unlist(map2(args, docs, arg_matches)) # Only document params under "..." that aren't otherwise documented documented <- get_documented_params(topic) non_documented_params <- setdiff(names(docs_selected), documented) docs_selected <- docs_selected[non_documented_params] if (length(docs_selected) == 0) { warn_roxy_topic( topic$get_name(), c( x = "@inheritDotParams failed", i = "No arguments inherited from {.fn {inheritors$source}}." ) ) return() } # Build the Rd # (1) Link to function(s) that was inherited from src <- inheritors$source from <- map_chr(src, function(x) { if (is_namespaced(x)) { parts <- re_split_half(x, "::") rd_link(parts[1], parts[2], x, code = TRUE) } else { rd_link(NA_character_, x, x, code = TRUE) } }) from <- paste0(from, collapse = ", ") # (2) Show each inherited argument arg_names <- paste0("\\code{", names(docs_selected), "}") args <- paste0( " \\item{", arg_names, "}{", docs_selected, "}", collapse = "\n" ) # NOTE: this rd structure is used by RStudio for parameter completions rd <- paste0( "\n", " Arguments passed on to ", from, "\n", " \\describe{\n", args, "\n", " }" ) topic$add(rd_section("param", c("..." = rd))) } get_documented_params <- function(topic, only_first = FALSE) { documented <- names(topic$get_value("param")) if (length(documented) > 0) { documented <- strsplit(documented, ",") if (only_first) { documented <- map_chr(documented, \(x) x[[1]]) } else { documented <- unlist(documented) } } documented[documented == "\\dots"] <- "..." documented } find_params <- function(name, topics, source, tag = "@inherits") { topic <- get_rd(name, topics, source = source, tag = tag) if (is.null(topic)) { return() } params <- topic_params(topic) if (is.null(params)) { return() } param_names <- trimws(names(params)) param_names <- strsplit(param_names, ",\\s*") param_names <- lapply(param_names, function(x) { x[x == "\\dots"] <- "..." x }) Map(list, name = param_names, value = unlist(params)) } topic_params <- function(x) { if (inherits(x, "Rd")) { arguments <- get_tags(x, "\\arguments") if (length(arguments) != 1) { return(list()) } items <- get_tags(arguments[[1]], "\\item") values <- map_chr(items, \(y) rd2text(y[[2]], attr(x, "package"))) params <- map_chr(items, \(y) rd2text(y[[1]], attr(x, "package"))) setNames(values, params) } else { x$get_value("param") } } # Inherit sections -------------------------------------------------------- inherit_sections <- function(topic, topics) { current_secs <- topic$get_value("section")$title for (inheritor in topic$inherits_from("sections")) { inheritor <- get_rd(inheritor, topics, source = topic$get_name()) if (is.null(inheritor)) { return() } sections <- find_sections(inheritor) needed <- !(sections$title %in% current_secs) if (!any(needed)) { next } topic$add( rd_section_section(sections$title[needed], sections$content[needed]) ) } } inherit_section <- function(topic, topics) { sections <- topic$get_value("inherit_section") sources <- sections$source titles <- sections$title for (i in seq_along(sources)) { inheritor <- get_rd(sources[[i]], topics, source = topic$get_name()) if (is.null(inheritor)) { return() } new_section <- find_sections(inheritor) selected <- new_section$title %in% titles[[i]] if (sum(selected) != 1) { warn_roxy_topic( topic$get_name(), "@inheritSection failed to find section {.str {titles[[i]]}} in topic {sources[[i]]}" ) return() } topic$add( rd_section_section( new_section$title[selected], new_section$content[selected] ) ) } } find_sections <- function(topic) { if (inherits(topic, "Rd")) { tag <- get_tags(topic, "\\section") titles <- map_chr( map(tag, \(x) x[[1]]), rd2text, package = attr(topic, "package") ) contents <- map_chr( map(tag, \(x) x[[2]]), rd2text, package = attr(topic, "package") ) list(title = titles, content = contents) } else { topic$get_value("section") } } # Inherit from single field ---------------------------------------------------- inherit_field <- function(topic, topics, rd_name, roxy_name = rd_name) { # Already has the field, so don't need to inherit if (topic$has_section(rd_name)) { return() } # Otherwise, try each try function listed in inherits for (inherit_from in topic$inherits_from(roxy_name)) { inherit_topic <- get_rd(inherit_from, topics, source = topic$get_name()) if (is.null(inherit_topic)) { next } inheritee <- find_field(inherit_topic, rd_name) if (is.null(inheritee)) { next } topic$add(rd_section(rd_name, inheritee)) return() } } find_field <- function(topic, field_name) { if (inherits(topic, "Rd")) { tag <- get_tags(topic, paste0("\\", field_name)) if (length(tag) == 0) { return() } value <- tag[[1]] attr(value, "Rd_tag") <- NULL trimws(rd2text(value, attr(topic, "package"))) } else { topic$get_value(field_name) } } rd2text <- function(x, package) { x <- tweak_links(x, package) chr <- as_character_rd(structure(x, class = "Rd"), deparse = TRUE) paste(chr, collapse = "") } # Convert relative to absolute links tweak_links <- function(x, package) { tag <- attr(x, "Rd_tag") if (is.list(x)) { if (!is.null(tag) && tag == "\\link") { opt <- attr(x, "Rd_option") if (is.null(opt)) { if (has_topic(x[[1]], package)) { attr(x, "Rd_option") <- structure(package, Rd_tag = "TEXT") } } else if (is_string(opt) && substr(opt, 1, 1) == "=") { topic <- substr(opt, 2, nchar(opt)) if (has_topic(topic, package)) { attr(x, "Rd_option") <- structure( paste0(package, ":", topic), Rd_tag = "TEXT" ) } } } else if (!is.null(tag) && tag == "\\linkS4class") { topic <- paste0(x[[1]], "-class") if (has_topic(topic, package)) { attr(x, "Rd_tag") <- "\\link" attr(x, "Rd_option") <- structure( paste0(package, ":", topic), Rd_tag = "TEXT" ) } } else if (length(x) > 0) { x[] <- map(x, tweak_links, package = package) } } x } # Find info in Rd or topic ------------------------------------------------ get_rd <- function(name, topics, source, tag = "@inherits") { if (is_namespaced(name)) { # External package parsed <- parse_expr(name) pkg <- as.character(parsed[[2]]) fun <- as.character(parsed[[3]]) get_rd_from_help(pkg, fun, source, tag = tag) } else { # Current package rd_name <- topics$find_filename(name) if (identical(rd_name, NA_character_)) { warn_roxy_topic( source, "{tag} failed to find topic {.str {name}} in current package" ) } topics$get(rd_name) } } get_rd_from_help <- function(package, alias, source, tag = "@inherits") { if (!is_installed(package)) { warn_roxy_topic( source, "{tag} failed because {.pkg {package}} is not installed" ) return() } help <- utils::help((alias), (package)) if (length(help) == 0) { warn_roxy_topic(source, "{tag} failed to find topic {package}::{alias}") return() } out <- internal_f("utils", ".getHelpFile")(help) attr(out, "package") <- package out } roxygen2/R/rd-usage.R0000644000176200001440000001367615166171276014113 0ustar liggesusers#' @export roxy_tag_parse.roxy_tag_usage <- function(x) { x <- tag_value(x, multiline = TRUE) x$val <- rd(x$val) x } #' @export roxy_tag_rd.roxy_tag_usage <- function(x, base_path, env) { if (identical(x$val, rd("NULL"))) { usage <- NULL } else { usage <- x$val } rd_section("usage", usage) } #' @export format.rd_section_usage <- function(x, ...) { rd_macro(x$type, build_rd(x$value, collapse = "\n\n"), space = TRUE) } # object_usage ------------------------------------------------------------ object_usage <- function(x) { UseMethod("object_usage") } #' @export object_usage.default <- function(x) { NULL } #' @export object_usage.data <- function(x) { if (roxy_meta_get("lazy_data", FALSE)) { rd(x$alias) } else { rd(paste0("data(", x$alias, ")")) } } #' @export object_usage.value <- function(x) { rd(x$alias) } #' @export object_usage.function <- function(x) { function_usage(x$alias, formals(x$value), identity) } object_usage.s3generic <- object_usage.function #' @export object_usage.s3method <- function(x) { method <- attr(x$value, "s3method") s3method <- function(name) { paste0("\\method{", name, "}{", auto_backtick(method[2]), "}") } function_usage(method[1], formals(x$value), s3method) } #' @export object_usage.s4generic <- function(x) { function_usage(x$value@generic, formals(x$value), identity) } #' @export object_usage.s4method <- function(x) { s4method <- function(name) { classes <- as.character(x$value@defined) paste0("\\S4method{", name, "}{", paste0(classes, collapse = ","), "}") } function_usage(x$value@generic, formals(x$value), s4method) } #' @export object_usage.s7class <- object_usage.function #' @export object_usage.s7generic <- object_usage.function #' @export object_usage.s7method <- function(x) { generic <- x$value$generic classes <- x$value$classes formatted <- map_chr(classes, \(nms) paste0("<", nms, ">", collapse = "/")) if (length(formatted) == 1) { comment <- paste0("## S7 method for class ", formatted) } else { comment <- paste0( "## S7 method for classes ", paste0(formatted, collapse = ", ") ) } usage <- function_usage(generic, formals(x$value$fn), identity) rd(paste0(comment, "\n", usage)) } # Function usage ---------------------------------------------------------- # Usage: # replacement, infix, regular # function, s3 method, s4 method, data function_usage <- function(name, formals, format_name = identity) { if (is_replacement_fun(name) && !is_infix_fun(name)) { name <- sub("<-", "", name, fixed = TRUE) if (identical(format_name, identity)) { name <- auto_backtick(name) } name <- gsub("%", "\\%", name, fixed = TRUE) formals$value <- NULL wrap_usage(name, format_name, formals, suffix = " <- value") } else if (is_infix_fun(name) && identical(format_name, identity)) { # If infix, and regular function, munge format arg_names <- names(formals) name <- format_name(name) if (is_padded_infix_fun(name)) { name <- paste0(" ", name, " ") } build_rd(arg_names[1], name, arg_names[2]) } else { if (identical(format_name, identity)) { name <- auto_backtick(name) } name <- gsub("%", "\\%", name, fixed = TRUE) wrap_usage(name, format_name, formals) } } is_replacement_fun <- function(name) { grepl("<-", name, fixed = TRUE) } is_infix_fun <- function(name) { ops <- c( "+", "-", "*", "^", "/", "==", ">", "<", "!=", "<=", ">=", "&", "|", "[[", "[", "$", ":", "::", ":::" ) grepl("^%.*%$", name) | name %in% ops } is_padded_infix_fun <- function(name) { ops <- c( "+", "-", "*", "/", "==", ">", "<", "!=", "<=", ">=", "&", "|" ) grepl("^%.*%$", name) || name %in% ops } usage_args <- function(args) { is.missing.arg <- function(arg) { is.symbol(arg) && deparse(arg) == "" } arg_to_text <- function(arg) { if (is.missing.arg(arg)) { return("") } text <- enc2utf8(deparse(arg, backtick = TRUE, width.cutoff = 500L)) text <- paste0(text, collapse = "\n") Encoding(text) <- "UTF-8" text } map_chr(as.list(args), arg_to_text) } args_string <- function(x, space = " ") { sep <- ifelse(names(x) != "" & x != "", paste0(space, "=", space), "") nms <- names2(x) arg_names <- ifelse(nms == "", "", escape(auto_backtick(nms))) paste0(arg_names, sep, escape(x)) } args_call <- function(call, args) { paste0(call, "(", paste0(args, collapse = ", "), ")") } #' @param name Function name #' @param format_name Single argument that returns formatted function name #' @param formals List of function formals #' @param suffix Optional suffix, used for replacement functions #' @noRd wrap_usage <- function(name, format_name, formals, suffix = NULL, width = 80L) { if (roxy_meta_get("old_usage", FALSE)) { # Use nbsp to keep argument name & default value on same line args <- args_string(usage_args(formals), "\u{A0}") x <- args_call(format_name(name), args) out <- wrapUsage(x, width = as.integer(width), indent = 2) out <- gsub("\u{A0}", " ", out, useBytes = TRUE) Encoding(out) <- "UTF-8" return(rd(paste0(out, suffix))) } args <- args_string(usage_args(formals)) bare <- args_call(name, args) if (!grepl("\n", bare, fixed = TRUE) && nchar(bare, type = "width") < width) { # Don't need to wrap out <- args_call(format_name(name), args) } else { # Wrap each argument and put on own line args <- paste0(" ", args) args <- map_chr(args, wrapUsage, width = 90, indent = 4) out <- paste0( format_name(name), "(\n", paste0(args, collapse = ",\n"), "\n)" ) } rd(paste0(out, suffix)) } # helpers ----------------------------------------------------------------- # used for testing call_to_usage <- function(code, env = pkg_env()) { obj <- call_to_object(!!enexpr(code), env) as.character(object_usage(obj)) } roxygen2/R/rd-s4.R0000644000176200001440000000143215172410643013307 0ustar liggesusers#' @export roxy_tag_parse.roxy_tag_field <- function(x) { tag_two_part(x, "a field name", "a description", multiline = TRUE) } #' @export roxy_tag_rd.roxy_tag_field <- function(x, base_path, env) { value <- setNames(x$val$description, x$val$name) rd_section(x$tag, value) } #' @export format.rd_section_field <- function(x, ...) { rd_section_description("Fields", names(x$value), x$value) } #' @export roxy_tag_parse.roxy_tag_slot <- function(x) { tag_two_part(x, "a slot name", "a description", multiline = TRUE) } #' @export roxy_tag_rd.roxy_tag_slot <- function(x, base_path, env) { value <- setNames(x$val$description, x$val$name) rd_section(x$tag, value) } #' @export format.rd_section_slot <- function(x, ...) { rd_section_description("Slots", names(x$value), x$value) } roxygen2/R/object-s3.R0000644000176200001440000000553115166171276014164 0ustar liggesusers#' Determine if a function is an S3 generic or S3 method #' #' @description #' `is_s3_generic` compares name to `.knownS3Generics` and #' `.S3PrimitiveGenerics`, then looks at the function body to see if it #' calls [UseMethod()]. #' #' `is_s3_method` builds names of all possible generics for that function #' and then checks if any of them actually is a generic. #' #' @param name Name of function. #' @param env Base environment in which to look for function definition. #' @keywords internal #' @export is_s3_generic <- function(name, env = parent.frame()) { if (name == "") { return(FALSE) } if (!exists(name, envir = env, mode = "function")) { return(FALSE) } f <- get(name, envir = env, mode = "function") if (inherits(f, "groupGenericFunction")) { return(TRUE) } ns_name <- tryCatch(getNamespaceName(environment(f)), error = function(e) "") if (identical(unname(.knownS3Generics[name]), ns_name)) { return(TRUE) } if (is.primitive(f)) { known_generics <- c( names(.knownS3Generics), internal_f("tools", ".get_internal_S3_generics")() ) return(name %in% known_generics) } calls_use_method(body(f)) } calls_use_method <- function(x) { # Base cases if (missing(x)) { return(FALSE) } if (!is.call(x)) { return(FALSE) } if (identical(x[[1]], quote(UseMethod))) { return(TRUE) } if (length(x) == 1) { return(FALSE) } # Recursive case: arguments to call for (arg in as.list(x[-1])) { if (calls_use_method(arg)) return(TRUE) } FALSE } #' @rdname is_s3_generic #' @export is_s3_method <- function(name, env = parent.frame()) { !is.null(find_generic(name, env)) } is.s3method <- function(x) inherits(x, "s3method") is.s3generic <- function(x) inherits(x, "s3generic") is.s3 <- function(x) inherits(x, c("s3method", "s3generic")) find_generic <- function(name, env = parent.frame()) { # If it's an S4 generic, it's definitely not an S3 method if (methods::isGeneric(name, where = env)) { return(NULL) } # special case all.equal() methods to avoid treating as all() methods (#1587) if (startsWith(name, "all.equal.") && is_s3_generic("all.equal", env)) { method <- substr(name, nchar("all.equal.") + 1, nchar(name)) return(c("all.equal", method)) } pieces <- strsplit(name, ".", fixed = TRUE)[[1]] n <- length(pieces) # No . in name, so can't be method if (n == 1) { return(NULL) } for (i in seq_len(n - 1)) { generic <- paste0(pieces[seq_len(i)], collapse = ".") class <- paste0(pieces[(i + 1):n], collapse = ".") if (is_s3_generic(generic, env)) return(c(generic, class)) } NULL } s3_method <- function(f, method) { check_function(f) check_character(method) class(f) <- c("s3method", "function") attr(f, "s3method") <- method f } s3_method_info <- function(x) { stopifnot(is.s3(x)) attr(x, "s3method") } roxygen2/R/roxygenize-needs.R0000644000176200001440000000425615154546447015700 0ustar liggesusers#' Check if documentation needs to be updated #' #' @description #' A lightweight check that compares modification times of `.Rd` files in #' `man/` with the source files listed in their backrefs. This is much faster #' than running [roxygenize()] but can suffer from both false negatives (e.g. #' if an inherited documentation topic has changed) and false positives (e.g. #' if a source file was modified but the change doesn't affect the #' documentation). #' #' @inheritParams roxygenize #' @param quiet If `TRUE`, suppresses the message listing out-of-date #' man pages. #' @return A logical value, invisibly. `TRUE` if any man pages appear #' to be out of date; `FALSE` otherwise. #' @export #' @examples #' \dontrun{ #' needs_roxygenize() #' } needs_roxygenize <- function(package.dir = ".", quiet = FALSE) { check_string(package.dir) check_bool(quiet) base_path <- normalizePath(package.dir) man_path <- file.path(base_path, "man") rd_files <- sort(dir(man_path, pattern = "\\.Rd$", full.names = TRUE)) outdated <- map_lgl(rd_files, rd_outdated, base_path = base_path) out_of_date <- basename(rd_files[outdated]) if (length(out_of_date) > 0 && !quiet) { cli::cli_inform(c( "!" = "{length(out_of_date)} man page{?s} may be out of date:", set_names(out_of_date, "*") )) } invisible(length(out_of_date) > 0) } rd_outdated <- function(rd_file, base_path) { lines <- read_lines(rd_file, n = 10) source_files <- rd_backref_sources(lines, base_path) if (length(source_files) == 0) { return(FALSE) } if (!all(file.exists(source_files))) { return(TRUE) } rd_mtime <- file.mtime(rd_file) source_mtimes <- file.mtime(source_files) any(source_mtimes > rd_mtime) } rd_backref_sources <- function(lines, base_path) { if (length(lines) == 0 || !check_made_by(lines[[1]])) { return(character()) } regexp <- "^%\\s*(Please edit documentation in| )" lines <- lines[grepl(regexp, lines)] if (length(lines) == 0) { return(character()) } lines <- gsub("^%(\\s*Please edit documentation in)?", "", lines) files <- unlist(strsplit(lines, ",")) files <- trimws(files) files <- files[nzchar(files)] file.path(base_path, files) } roxygen2/R/object-from-call.R0000644000176200001440000003003015174453534015502 0ustar liggesusersobject_from_call <- function(call, env, block, file) { if (is.character(call)) { if (identical(call, "_PACKAGE")) { parser_package(file) } else { parser_data(call, env, file) } } else if (is_set_call(call)) { parser_r6_set(call, env) } else if (is.call(call)) { if (is_s7_method_call(call)) { return(parser_s7_method(call, env, block)) } call <- call_match(call, eval(call[[1]], env)) name <- deparse(call[[1]]) switch( name, "=" = , "<-" = , "<<-" = parser_assignment(call, env, block), "delayedAssign" = parser_delayedAssign(call, env, block), "::" = parser_import(call, env, block), "methods::setClass" = , "setClass" = parser_setClass(call, env, block), "methods::setClassUnion" = , "setClassUnion" = parser_setClassUnion(call, env, block), "methods::setRefClass" = , "setRefClass" = parser_setRefClass(call, env, block), "methods::setGeneric" = , "setGeneric" = parser_setGeneric(call, env, block), "methods::setMethod" = , "setMethod" = parser_setMethod(call, env, block), "methods::setReplaceMethod" = , "setReplaceMethod" = parser_setReplaceMethod(call, env, block), "R.methodsS3::setMethodS3" = , "setMethodS3" = parser_setMethodS3(call, env, block), "R.oo::setConstructorS3" = , "setConstructorS3" = parser_setConstructorS3(call, env, block), NULL ) } else { # Patch @docType package to ensure that it gets a default alias # and other "_PACKAGE" features if (block_has_tags(block, "docType")) { docType <- block_get_tag_value(block, "docType") if (docType == "package") { warn_roxy_block( block, c( '`@docType "package"` is deprecated', i = 'Please document "_PACKAGE" instead.' ) ) return(parser_package(file)) } } NULL } } object_from_name <- function(name, env, block) { value <- get(name, env) if (inherits(value, "R6ClassGenerator")) { type <- "r6class" } else if (methods::is(value, "refObjectGenerator")) { value <- methods::getClass(as.character(value@className), where = env) type <- "rcclass" } else if (methods::is(value, "classGeneratorFunction")) { value <- methods::getClass(as.character(value@className), where = env) type <- "s4class" } else if (methods::is(value, "MethodDefinition")) { # S4 methods need munging to get real function def value@.Data <- extract_method_fun(value@.Data) type <- "s4method" } else if (methods::is(value, "standardGeneric")) { type <- "s4generic" } else if (inherits(value, "S7_class")) { type <- "s7class" } else if (inherits(value, "S7_generic")) { type <- "s7generic" } else if (is.function(value)) { # Potential S3 methods/generics need metadata added method <- block_get_tag_value(block, "method") value <- add_s3_metadata(value, name, env, block) if (inherits(value, "s3generic")) { type <- "s3generic" } else if (inherits(value, "s3method")) { type <- "s3method" } else { type <- "function" } } else { type <- "value" } object(value, name, type) } # Parsers for individual calls -------------------------------------------- is_set_call <- function(call) { is_call(call) && is_call(call[[1]], "$", n = 2) && is_symbol(call[[1]][[3]], "set") } parser_r6_set <- function(call, env) { lhs <- call[[1]] obj_name <- deparse(lhs[[2]]) obj <- tryCatch(get(obj_name, envir = env), error = function(e) NULL) if (!inherits(obj, "R6ClassGenerator")) { return(NULL) } class_name <- obj$classname method_name <- call[[3]] if (!is.character(method_name)) { return(NULL) } object(list(class = class_name, method = method_name), NULL, "r6method") } parser_data <- function(call, env, block) { if (isNamespace(env)) { value <- getExportedValue(call, ns = asNamespace(env)) } else { value <- get(call, envir = env) } object(value, call, type = "data") } parser_package <- function(file) { pkg_path <- dirname(dirname(file)) value <- list( desc = desc::desc(file = pkg_path), path = pkg_path ) object(value, NULL, type = "package") } parser_assignment <- function(call, env, block) { name <- as.character(call[[2]]) # If it's a compound assignment like x[[2]] <- ignore it if (length(name) > 1) { return() } # If it doesn't exist (any more), don't document it. if (!exists(name, env)) { return() } object_from_name(name, env, block) } parser_delayedAssign <- function(call, env, block) { name <- as.character(call$x) object_from_name(name, env, block) } parser_setClass <- function(call, env, block) { name <- as.character(call$Class) value <- methods::getClass(name, where = env) object(value, NULL, "s4class") } parser_setClassUnion <- function(call, env, block) { name <- as.character(call$name) value <- methods::getClass(name, where = env) object(value, NULL, "s4class") } parser_setRefClass <- function(call, env, block) { name <- as.character(call$Class) value <- methods::getClass(name, where = env) object(value, NULL, "rcclass") } parser_setGeneric <- function(call, env, block) { name <- as.character(call$name) value <- methods::getGeneric(name, where = env) object(value, NULL, "s4generic") } parser_setMethod <- function(call, env, block) { name <- as.character(call$f) value <- methods::getMethod(name, eval(call$signature), where = env) value@.Data <- extract_method_fun(value@.Data) object(value, NULL, "s4method") } parser_setReplaceMethod <- function(call, env, block) { name <- paste0(as.character(call$f), "<-") value <- methods::getMethod(name, eval(call[[3]]), where = env) value@.Data <- extract_method_fun(value@.Data) object(value, NULL, "s4method") } parser_import <- function(call, env, block) { pkg <- as.character(call[[2]]) fun <- as.character(call[[3]]) object(list(pkg = pkg, fun = fun), alias = fun, type = "import") } parser_setMethodS3 <- function(call, env, block) { # R.methodsS3::setMethodS3(name, class, ...) method <- as.character(call[[2]]) class <- as.character(call[[3]]) name <- paste(method, class, sep = ".") value <- add_s3_metadata(get(name, env), name, env, block) object(value, name, "s3method") } parser_setConstructorS3 <- function(call, env, block) { # R.oo::setConstructorS3(name, ...) name <- as.character(call[[2]]) object(get(name, env), name, "function") } # method(generic, class) <- fn # `<-`(method(generic, class), fn) is_s7_method_call <- function(call) { is_call(call, "<-", n = 2) && is_call(call[[2]], "method", ns = c("", "S7")) } parser_s7_method <- function(call, env, block) { generic_call <- call[[2]][[2]] class_call <- call[[2]][[3]] method_call <- call[[3]] generic <- eval(generic_call, env) if (inherits(generic, "S7_generic")) { generic_name <- generic@name } else { # S3 or S4 generic passed by name generic_name <- deparse(generic_call) } # Evaluate class spec: either a single class, a union, or list() for # multi-dispatch classes <- eval(class_call, env) if (!is_bare_list(classes)) { classes <- list(classes) } class_names <- lapply(classes, s7_class_name, block = block) fn <- eval(method_call, env) value <- list(fn = fn, generic = generic_name, classes = class_names) aliases <- s7_method_aliases(generic_name, class_names) object(value, aliases, "s7method") } s7_method_aliases <- function(generic, classes) { if (!any(lengths(classes) > 1)) { return(NULL) } combos <- expand.grid(classes, stringsAsFactors = FALSE) apply(combos, 1, function(row) { paste0(generic, ",", paste0(row, collapse = ","), "-method") }) } # https://github.com/RConsortium/S7/issues/594 s7_class_name <- function(cls, block) { name <- nameOfClass(cls) if (!is.null(name)) { # Regular S7 class + base wrappers name } else if (inherits(cls, "S7_union")) { # Unions return vector of member names, recursing for nested types unlist(lapply(cls$classes, s7_class_name, block = block)) } else if (inherits(cls, "S7_S3_class")) { cls$class } else if (inherits(cls, "S7_any")) { "any" } else if (inherits(cls, "S7_missing")) { "missing" } else { warn_roxy_block(block, "Unknown S7 class type") paste0(deparse(cls), collapse = " ") } } # helpers ----------------------------------------------------------------- add_s3_metadata <- function(val, name, env, block) { if (block_has_tags(block, "method")) { method <- block_get_tag_value(block, "method") return(s3_method(val, method)) } if (block_has_tags(block, "exportS3Method")) { method <- block_get_tag_value(block, "exportS3Method") if (length(method) == 1 && grepl("::", method, fixed = TRUE)) { generic <- re_split_half(method, "::")[[2]] class <- gsub(paste0("^", generic, "\\."), "", name) return(s3_method(val, c(generic, class))) } } if (is_s3_generic(name, env)) { class(val) <- c("s3generic", "function") return(val) } method <- find_generic(name, env) if (is.null(method)) { val } else { s3_method(val, method) } } # When a generic has ... and a method adds new arguments, the S4 method # wraps the definition inside another function which has the same arguments # as the generic. This function figures out if that's the case, and extracts # the original function if so. # # It's based on expression processing based on the structure of the # constructed method which looks like: # # function (x, ...) { # .local <- function (x, ..., y = 7) {} # .local(x, ...) # } extract_method_fun <- function(fun) { method_body <- body(fun) if (!is_call(method_body, "{")) { return(fun) } if (length(method_body) < 2) { return(fun) } first_line <- method_body[[2]] if (!is_call(first_line, name = "<-", n = 2)) { return(fun) } if (!identical(first_line[[2]], quote(`.local`))) { return(fun) } local_fun <- eval(first_line[[3]]) if (!is.function(local_fun)) { return(fun) } local_fun } #' Constructors for S3 object to represent R objects #' #' These objects are usually created by the parsers, but it is also #' useful to generate them by hand for testing. #' #' @param value The object itself. #' @param alias Alias for object being documented, in case you create a #' generator function with different name. #' @export #' @keywords internal #' @param type Type of the object, character. E.g. `"data"` or `"s4method"`. object <- function(value, alias, type) { structure( list( alias = alias, value = value, methods = if (type == "rcclass") rc_methods(value), topic = object_topic(value, alias, type) ), class = c(type, "object") ) } #' @export format.object <- function(x, ...) { c( paste0("<", class(x)[1], "> ", x$name), paste0(" $topic ", x$topic), if (!is.null(x$alias)) paste0(" $alias ", x$alias) ) } #' @export print.object <- function(x, ...) { cat_line(format(x, ...)) } object_topic <- function(value, alias, type) { switch( type, s4method = method_topic(value@generic, value@defined), s4class = paste0(value@className, "-class"), s4generic = value@generic, rcclass = paste0(value@className, "-class"), r6class = alias, r6method = alias, rcmethod = value@name, s7class = alias, s7generic = alias, s7method = method_topic(value$generic, value$classes), s3generic = alias, s3method = alias, import = alias, `function` = alias, package = alias, data = alias, value = alias, cli::cli_abort("Unsupported type {.str {type}}.", .internal = TRUE) ) } method_topic <- function(generic, classes) { class_strings <- vapply(classes, paste0, character(1), collapse = "/") paste0(generic, ",", paste0(class_strings, collapse = ","), "-method") } call_to_object <- function(code, env = pkg_env(), file = NULL) { code <- enexpr(code) eval(code, envir = env) if (is_call(code, "{")) { call <- code[[length(code)]] } else { call <- code } object_from_call(call, env, block = NULL, file = file) } roxygen2/R/utils.R0000644000176200001440000001172715166171276013537 0ustar liggesusersinternal_f <- function(p, f) { check_string(p) check_string(f) get(f, envir = asNamespace(p)) } "%||%" <- function(a, b) { if (length(a) > 0) a else b } subs <- c( # Common special function names "[<-" = "-subset-", "[" = "-sub-", "<-" = "-set-", "::" = "-", # Infix verbs "!" = "-not-", "&" = "-and-", "|" = "-or-", "*" = "-times-", "+" = "-plus-", "^" = "-pow-", # Others '"' = "-quote-", "#" = "-hash-", "$" = "-cash-", "%" = "-grapes-", "'" = "-single-quote-", "(" = "-open-paren-", ")" = "-close-paren-", ":" = "-colon-", ";" = "-semi-colon-", "<" = "-less-than-", "==" = "-equals-", "=" = "-equals-", ">" = "-greater-than-", "?" = "-help-", "@" = "-at-", "]" = "-close-brace-", "\\" = "-backslash-", "/" = "-slash-", "`" = "-tick-", "{" = "-open-curly-", "}" = "-close-", "~" = "-twiddle-" ) nice_name <- function(x) { for (i in seq_along(subs)) { x <- gsub(names(subs)[[i]], subs[[i]], x, fixed = TRUE) } # Clean up any remaining x <- gsub("[^A-Za-z0-9_.-]+", "-", x) x <- gsub("-+", "-", x) x <- gsub("^-|-$", "", x) x <- gsub("^\\.", "dot-", x) x } write_if_different <- function(path, contents, command = NULL, check = TRUE) { if (!file.exists(dirname(path))) { dir.create(dirname(path), showWarnings = FALSE) } name <- basename(path) if (check && !made_by_roxygen(path)) { cli::cli_inform(c( x = "Skipping {.href [{name}](file://{path})}", i = "It already exists and was not generated by {.pkg roxygen2}." )) return(FALSE) } line_ending <- detect_line_ending(path) contents <- paste0(paste0(contents, collapse = line_ending), line_ending) contents <- enc2utf8(gsub("\r?\n", line_ending, contents)) if (same_contents(path, contents)) { # Touch so mtime reflects last run, even though file wasn't changed Sys.setFileTime(path, Sys.time()) return(FALSE) } if (!grepl("^[a-zA-Z][a-zA-Z0-9_.-]*$", name)) { cli::cli_inform(c( x = "Skipping {.path {name}}", i = "Invalid file name" )) FALSE } else { if (!is.null(command)) { scheme <- "x-r-run" url <- paste0(scheme, ":", command) name <- cli::style_hyperlink(name, url) } cli::cli_inform("Writing {.path {name}}") writeBin(charToRaw(contents), path) TRUE } } same_contents <- function(path, contents) { if (length(contents) != 1) { cli::cli_abort("{.arg contents} must be a single string.", .internal = TRUE) } if (!file.exists(path)) { return(FALSE) } text_hash <- cli::hash_sha256(contents) path <- normalizePath(path, mustWork = TRUE) file_hash <- cli::hash_file_sha256(path) identical(text_hash, file_hash) } compact <- function(x) { x[!map_lgl(x, is.null)] } invert <- function(x) { if (length(x) == 0) { return() } stacked <- utils::stack(x) tapply(as.character(stacked$ind), stacked$values, list) } is_namespaced <- function(x) { tryCatch( { expr <- parse_expr(x) is_call(expr, "::", n = 2) }, error = function(err) FALSE ) } # Collapse the values associated with duplicated keys collapse <- function(key, value, fun, ...) { check_character(key) stopifnot(length(key) == length(value)) dedup <- tapply(value, key, fun, ..., simplify = FALSE) # tapply orders alphabetically, so reorder to match original order dedup <- dedup[unique(key)] list( key = names(dedup), value = unname(dedup) ) } cat_line <- function(...) { cat(paste0(..., "\n", collapse = "")) } tag_aliases <- function(f) { paste0("@aliases ", paste0("@", names(f()), collapse = " ")) } pkg_env <- function() { env <- new.env(parent = globalenv()) env$.packageName <- "roxygen2" env } uuid <- function(nchar = 8) { paste( sample(c(letters, LETTERS, 0:9), nchar, replace = TRUE), collapse = "" ) } paste_c <- function(...) { paste(c(...), collapse = "") } # quoting ----------------------------------------------------------------- auto_backtick <- function(x) { needs_backtick <- !has_quotes(x) & !is_syntactic(x) x[needs_backtick] <- encodeString(x[needs_backtick], quote = "`") x } auto_quote <- function(x) { needs_quotes <- !has_quotes(x) & !is_syntactic(x) x[needs_quotes] <- encodeString(x[needs_quotes], quote = '"') x } re_split_half <- function(x, pattern) { m <- regexpr(pattern, x) if (m > 0L) { left <- substr(x, 1, m - 1) right <- substr(x, m + attr(m, "match.length"), nchar(x)) } else { left <- x right <- "" } c(left, right) } re_count <- function(x, pattern, fixed = FALSE) { m <- gregexpr(pattern, x, fixed = fixed) vapply(m, \(i) sum(i > 0L), integer(1)) } re_replace_all <- function(x, pattern, fun) { m <- gregexpr(pattern, x, perl = TRUE) regmatches(x, m) <- lapply(regmatches(x, m), \(matches) { vapply(matches, fun, character(1)) }) x } is_syntactic <- function(x) make.names(x) == x has_quotes <- function(x) grepl(r"[^(`|'|").*\1$]", x) strip_quotes <- function(x) sub(r"[^(`|'|")(.*)\1$]", r"(\2)", x) roxygen2/R/topic.R0000644000176200001440000001252315154546446013512 0ustar liggesusers#' A `RoxyTopic` is an ordered collection of unique rd_sections #' #' @description #' A `RoxyTopic` object corresponds to a generated `.Rd` file. #' #' @param type Section type, a character scalar. #' @param overwrite Whether to overwrite an existing section. If `FALSE` #' then the two sections will be merged. #' #' @keywords internal RoxyTopic <- R6::R6Class( "RoxyTopic", public = list( #' @field sections Named list of sections. Each item must be an #' [rd_section()] object. sections = list(), #' @field filename Path to the `.Rd` file to generate. filename = "", #' @description Format the `.Rd` file. It considers the sections in #' particular order, even though Rd tools will reorder them again. #' #' @param ... Passed to the `format()` methods of the [rd_section()] #' objects, the sections. #' @return Character string. format = function(...) { # This has to happen here to get a better order when combining topics order <- c( "backref", "docType", "encoding", "name", "alias", "title", "format", "source", "usage", "param", "value", "description", "details", "minidesc", "field", "slot", "rcmethods", "note", "section", "examples", "references", "seealso", "author", "concept", "keyword", "rawRd" ) sections <- move_names_to_front(self$sections, order) formatted <- lapply(sections, format, ...) paste0( made_by("%"), paste0(unlist(formatted), collapse = "\n") ) }, #' @description Check if an `.Rd` file is valid #' @return Logical flag, `TRUE` for valid `.Rd` files is_valid = function() { # Needs both title and name sections to generate valid Rd all(self$has_section(c("title", "name"))) }, #' @description Check if an `.Rd` file has a certain section. #' @return Logical flag. has_section = function(type) { type %in% names(self$sections) }, #' @description Query a section. #' @return The [rd_section] object representing the section, or `NULL` #' if the topic has no such section. get_section = function(type) { self$sections[[type]] }, #' @description Query the value of a section. This is the value of #' the [rd_section] object. #' @return Value. get_value = function(type) { self$get_section(type)$value }, #' @description Get the Rd code of a section. #' @return Character vector, one element per line. get_rd = function(type) { format(self$get_section(type)) }, #' @description Get the value of the `name` section. This is the name #' of the Rd topic. #' @return Character scalar. get_name = function() { self$get_value("name")[[1]] }, #' @description Query the topics this topic inherits `type` from. #' @return A character vector of topic names. inherits_from = function(type) { if (!self$has_section("inherit")) { return(character()) } inherit <- self$get_value("inherit") inherits_field <- map_lgl(inherit$fields, \(x) type %in% x) sources <- inherit$source[inherits_field] if ("NULL" %in% sources) { return(character()) } sources }, #' @description Query the topics this topic inherits sections from. #' @return A character vector of topic names. inherits_section_from = function() { if (!self$has_section("inherit_section")) { return(character()) } self$get_value("inherit_section")$source }, #' @description Add one or more sections to the topic. #' @param x Section(s) to add. It may be #' another `RoxyTopic` object, all of its sections will be added; #' or an [rd_section] object; #' or a list of [rd_section] objects to add. #' @param block Name of block to use in error messages. add = function(x, block = "???", overwrite = FALSE) { if (inherits(x, "RoxyTopic")) { self$add(x$sections, block, overwrite = overwrite) } else if (inherits(x, "rd_section")) { self$add_section(x, block, overwrite = overwrite) } else if (is.list(x)) { for (section in x) { self$add_section(section, block, overwrite = overwrite) } } else if (is.null(x)) { # skip } else { cli::cli_abort( "Don't know how to add object of type {.cls {class(x)[1]}}.", .internal = TRUE ) } invisible() }, #' @description Add a section. #' @details #' Ensures that each type of name (as given by its name), only appears #' once in `self$sections`. This method if for internal use only. #' @param section [rd_section] object to add. #' @param block Name of block to use in error messages. add_section = function(section, block = "???", overwrite = FALSE) { if (is.null(section)) { return() } type <- section$type if (self$has_section(type) && !overwrite) { section <- merge(self$get_section(type), section, block = block) } self$sections[[type]] <- section invisible() } ) ) move_names_to_front <- function(x, to_front) { nms <- names(x) x[union(intersect(to_front, nms), nms)] } roxygen2/R/rd-r6-field.R0000644000176200001440000000471615170121342014372 0ustar liggesusersr6_extract_field_tags <- function(block, r6data, type = c("field", "active")) { type <- match.arg(type) other_type <- if (type == "field") "active" else "field" label <- if (type == "field") "field" else "active binding" self <- r6data$self expected <- self$name[self$type == type] other <- self$name[self$type == other_type] tags <- keep(block$tags, \(t) tag_is(t, "field") && !tag_has_name(t, other)) docd <- unlist(lapply(tags, tag_names)) dup <- unique(docd[duplicated(docd)]) if (length(dup) > 0) { warn_roxy_block(block, "R6 {label}{?s} documented multiple times: {dup}") } if (type == "field") { xtra <- setdiff(docd, expected) if (length(xtra) > 0) { warn_roxy_block(block, "Unknown R6 {label}{?s}: {xtra}") } } # @field name NULL suppresses documentation for that field/binding tags <- discard(tags, function(t) toupper(t$val$description) == "NULL") items <- lapply(tags, function(t) { rd_r6_field( name = gsub(",", ", ", t$val$name), description = t$val$description ) }) rd_r6_fields(items, type = type, expected = expected) } r6_field_names <- function(rd_fields) { if (length(rd_fields) == 0) { return(character()) } labels <- map_chr(rd_fields, \(x) x$name) trimws(unlist(strsplit(labels, ","))) } # Rd --------------------------------------------------------------------------- rd_r6_fields <- function( fields = list(), type = c("field", "active"), expected = character() ) { type <- match.arg(type) structure( list(fields = fields, type = type, expected = expected), class = "rd_r6_fields" ) } rd_r6_field <- function(name, description) { structure( list(name = name, description = description), class = "rd_r6_field" ) } #' @export format.rd_r6_field <- function(x, ...) { paste0("\\item{\\code{", x$name, "}}{", x$description, "}") } #' @export format.rd_r6_fields <- function(x, ...) { if (x$type == "field") { format_r6_field_section(x$fields, "Public fields", "r6-fields") } else { format_r6_field_section(x$fields, "Active bindings", "r6-active-bindings") } } format_r6_field_section <- function(fields, title, css_class) { if (length(fields) == 0) { return() } c( paste0("\\section{", title, "}{"), paste0(" ", rd_if_html(paste0('
    '))), " \\describe{", paste0(" ", map_chr(fields, format), collapse = "\n\n"), " }", paste0(" ", rd_if_html("
    ")), "}" ) } roxygen2/R/markdown-link.R0000644000176200001440000001616315166171276015153 0ustar liggesusers#' Add link reference definitions for functions to a markdown text #' #' We find the `[text][ref]` and the `[ref]` forms. There must be no #' spaces between the closing and opening bracket in the `[text][ref]` #' form. #' #' These are the link references we add for local links: #' #' ``` #' MARKDOWN LINK TEXT CODE RD #' -------- --------- ---- -- #' [fun()] fun() T \\link[=fun]{fun()} #' [obj] obj F \\link{obj} #' [`obj`] obj T \\link{obj} #' [text][fun()] text F \\link[=fun]{text} #' [text][obj] text F \\link[=obj]{text} #' [s4-class] s4 F \\link[=s4-class]{s4} #' ``` #' #' And for cross-package links: #' #' ``` #' MARKDOWN LINK TEXT CODE RD #' -------- --------- ---- -- #' [fun()] fun() T \\link[pkg:fun]{pkg::fun()} #' [obj] obj F \\link[pkg:obj]{pkg::obj} #' [`obj`] obj T \\link[pkg:obj]{pkg::obj} #' [pkg::fun()] pkg::fun() T \\link[pkg:fun]{pkg::fun()} #' [pkg::obj] pkg::obj F \\link[pkg:obj]{pkg::obj} #' [text][fun()] text F \\link[pkg:fun]{text} #' [text][obj] text F \\link[pkg:obj]{text} #' [text][pkg::fun()] text F \\link[pkg:fun]{text} #' [text][pkg::obj] text F \\link[pkg:obj]{text} #' [s4-class] s4 F \\link[pkg:s4-class]{s4} #' [pkg::s4-class] pkg::s4 F \\link[pkg:s4-class]{pkg::s4} #' ``` #' #' The reference links will always look like `R:ref` for `[ref]` and #' `[text][ref]`. We add in a special `R:` marker to the URL to avoid #' picking up other links, that were specified via `` or #' `[text](link)`. In the parsed XML tree these look the same as #' our `[link]` and `[text][link]` links. #' #' In the link references, we need to URL encode the reference, #' otherwise commonmark does not use it (see issue #518). #' #' @param text Input markdown text. #' @return The input text and all dummy reference link definitions #' appended. #' #' @noRd NULL # Append link references for R functions --------------------------------------- add_linkrefs_to_md <- function(text) { ref_lines <- get_md_linkrefs(text) if (length(ref_lines) == 0) { return(text) } ref_text <- paste0(ref_lines, collapse = "\n") paste0(text, "\n\n", ref_text, "\n") } get_md_linkrefs <- function(text) { refs <- regmatches( text, gregexec( paste0( "(?x)", "(?<=[^\\]\\\\]|^)", # must not be preceded by ] or \ "\\[([^\\]\\[]+)\\]", # match anything inside of [] "(?:\\[([^\\]\\[]+)\\])?", # match optional second pair of [] "(?=[^\\[{]|$)" # must not be followed by [ or { ), text, perl = TRUE ) )[[1]] if (length(refs) > 0) { refs <- t(refs) } if (length(refs) == 0) { return(character()) } ## For the [fun] form the link text is the same as the destination. refs[, 3] <- ifelse(refs[, 3] == "", refs[, 2], refs[, 3]) refs3encoded <- map_chr(refs[, 3], URLencode) paste0("[", refs[, 3], "]: ", "R:", refs3encoded) } # Link parsing ----------------------------------------------------------------- parse_link <- function(destination, contents, state) { ## Not a [] or [][] type link, remove prefix if it is if (!grepl("^R:", destination)) { return(NULL) } destination <- sub("^R:", "", URLdecode(destination)) ## if contents is a `code tag`, then we need to move this outside is_code <- FALSE if (length(contents) == 1 && xml_name(contents) == "code") { is_code <- TRUE contents <- xml_contents(contents) destination <- sub("`$", "", sub("^`", "", destination)) local_bindings(.env = state, in_link_code = TRUE) } ## If the supplied link text is the same as the reference text, ## then we assume that the link text was automatically generated and ## it was not specified explicitly. In this case `()` links are ## turned to `\\code{}`. ## We also assume link text if we see a non-text XML tag in contents. has_link_text <- paste(xml_text(contents), collapse = "") != destination || any(xml_name(contents) != "text") ## if (is_code) then we'll need \\code ## `pkg` is package or NA ## `fun` is fun() or topic (fun is with parens) ## `topic` is fun or topic (fun is without parens) ## `s4` is TRUE if we link to an S4 class (i.e. have -class suffix) ## `noclass` is fun with -class removed is_code <- is_code || (grepl("[(][)]$", destination) && !has_link_text) pkg <- regmatches(destination, regexec("^(.*)::", destination))[[1]][2] explicit_pkg <- !is.na(pkg) fun <- utils::tail(strsplit(destination, "::", fixed = TRUE)[[1]], 1) topic <- sub("[(][)]$", "", fun) if (!has_link_text && grepl("-class$", destination)) { fun <- regmatches(fun, regexec("^(.*)-class$", fun))[[1]][2] } # Standardise links: cross-packagae always get prefix; # within package never gets prefix if (is.na(pkg)) { pkg <- find_package(topic, tag = state$tag) } else if (identical(pkg, roxy_meta_get("current_package"))) { pkg <- NA_character_ explicit_pkg <- FALSE } check_topic(pkg, topic, state$tag) ## To understand this, look at the RD column of the table above if (!has_link_text) { text <- fun if (explicit_pkg) { text <- paste0(pkg, "::", text) } text <- escape(text) } else { text <- mdxml_link_text(contents, state) } rd_link(pkg, escape(topic), text, code = is_code) } check_topic <- function(pkg, topic, tag = NULL) { if (is.na(pkg) || identical(roxy_meta_get("current_package"), pkg)) { return(invisible()) } if (!is_installed(pkg)) { warn_roxy_tag(tag, "refers to un-installed package {pkg}") return(invisible()) } help_path <- utils::help((topic), (pkg))[1] if (is.na(basename(help_path))) { warn_roxy_tag(tag, "refers to unavailable topic {pkg}::{topic}") } invisible() } fun_suffix <- function(name, env) { if (is_infix_fun(name)) { name } else if (!env_has(env, name)) { name } else { obj <- env_get(env, name) if (!is.function(obj)) { name } else { paste0(name, "()") } } } rd_link <- function(pkg, topic, text, code = FALSE) { if (is.na(pkg) && topic == text) { out <- paste0("\\link{", text, "}") } else { anchor <- if (is.na(pkg)) paste0("=", topic) else paste0(pkg, ":", topic) out <- paste0("\\link[", anchor, "]{", text, "}") } if (code) { out <- paste0("\\code{", out, "}") } out } #' Dummy page to test roxygen's markdown formatting #' #' Links are very tricky, so I'll put in some links here: #' Link to a function: [roxygenize()]. #' Link to an object: [roxygenize] (we just treat it like an object here. #' #' Link to another package, function: [desc::desc()]. #' Link to another package, non-function: [desc::desc]. #' #' Link with link text: [this great function][roxygenize()], #' [`roxygenize`][roxygenize()], or [that great function][roxygenize]. #' #' In another package: [and this one][desc::desc]. #' #' This is a table: #' #' | __foo__ | __bar__ | #' | :-- | --: | #' | 1 | 2 | #' | 100 | 200 | #' #' @name markdown-test #' @keywords internal NULL roxygen2/R/rd-section.R0000644000176200001440000000227515166171276014444 0ustar liggesusers#' @export roxy_tag_parse.roxy_tag_section <- function(x) { tag_markdown(x) } #' @export roxy_tag_rd.roxy_tag_section <- function(x, base_path, env) { pieces <- re_split_half(x$val, ":") title <- strsplit(pieces[1], "\n", fixed = TRUE)[[1]] if (length(title) > 1) { warn_roxy_tag( x, c( "title spans multiple lines.", i = "Did you forget a colon (:) at the end of the title?" ) ) return() } rd_section_section(pieces[1], pieces[2]) } rd_section_section <- function(title, content) { check_character(title) check_character(content) stopifnot(length(title) == length(content)) rd_section("section", list(title = title, content = content)) } #' @export format.rd_section_section <- function(x, ...) { paste0( "\\section{", x$value$title, "}{\n", x$value$content, "\n}\n", collapse = "\n" ) } #' @export merge.rd_section_section <- function(x, y, ...) { stopifnot(identical(class(x), class(y))) dedup <- collapse( c(x$value$title, y$value$title), c(x$value$content, y$value$content), paste, collapse = "\n\n" ) rd_section("section", list(title = dedup$key, content = unlist(dedup$value))) } roxygen2/R/markdown-code.R0000644000176200001440000001261215166171276015123 0ustar liggesusers#' Expand embedded inline code #' #' @description #' For example this becomes two: `r 1+1`. #' Variables can be set and then reused, within the same #' tag: `r x <- 100; NULL` #' The value of `x` is `r x`. #' #' We have access to the internal functions of the package, e.g. #' since this is _roxygen2_, we can refer to the internal `markdown` #' function, and this is `TRUE`: `r is.function(markdown)`. #' #' To insert the name of the current package: `r packageName()`. #' #' The `iris` data set has `r ncol(iris)` columns: #' `r paste0("\x60\x60", colnames(iris), "\x60\x60", collapse = ", ")`. #' #' ```{r} #' # Code block demo #' x + 1 #' ``` #' #' Chunk options: #' #' ```{r results = "hold"} #' names(mtcars) #' nrow(mtcars) #' ``` #' #' Plots: #' #' ```{r test-figure} #' plot(1:10) #' ``` #' #' Alternative knitr engines: #' #' ```{verbatim} #' #| file = "tests/testthat/example.Rmd" #' ``` #' #' Also see `vignette("rd-formatting")`. #' #' @param text Input text. #' @return #' Text with R code expanded. #' A character vector of the same length as the input `text`. #' #' @keywords internal markdown_evaluate <- function(text) { text <- paste(text, collapse = "\n") mdxml <- xml_ns_strip(md_to_mdxml(text, sourcepos = TRUE)) code_nodes <- xml_find_all(mdxml, ".//code | .//code_block") rcode_nodes <- keep(code_nodes, is_markdown_code_node) if (length(rcode_nodes) == 0) { return(text) } rcode_pos <- parse_md_pos(map_chr(rcode_nodes, xml_attr, "sourcepos")) rcode_pos <- work_around_cmark_sourcepos_bug(text, rcode_pos) out <- eval_code_nodes(rcode_nodes) re_set_all_pos(text, rcode_pos, out, rcode_nodes) } # Work around commonmark sourcepos bug for inline R code # https://github.com/r-lib/roxygen2/issues/1353 work_around_cmark_sourcepos_bug <- function(text, rcode_pos) { if (Sys.getenv("ROXYGEN2_NO_SOURCEPOS_WORKAROUND", "") != "") { return(rcode_pos) } lines <- strsplit(text, "\n", fixed = TRUE)[[1]] for (l in seq_len(nrow(rcode_pos))) { # Do not try to fix multi-line code, we error for that (below) if (rcode_pos$start_line[l] != rcode_pos$end_line[l]) { next } line <- lines[rcode_pos$start_line[l]] start <- rcode_pos$start_column[l] # Maybe correct? At some point this will be fixed upstream, hopefully. if (substr(line, start - 1, start + 1) == "`r ") { next } # Maybe indented and we can shift it? # It is possible that the shift that we try accidentally matches # "`r ", but it seems to be extremely unlikely. An example is this: # #' ``1`r `` `r 22*10` # (seven spaces after the #', so an indent of six spaces. If we shift # the real "`r " left by six characters, there happens to be another # "`r " there. m <- regexpr("^[ ]+", line) indent <- attr(m, "match.length") if ( m > 0L && substr(line, start - 1 + indent, start + 1 + indent) == "`r " ) { rcode_pos$start_column[l] <- rcode_pos$start_column[l] + indent rcode_pos$end_column[l] <- rcode_pos$end_column[l] + indent } } rcode_pos } is_markdown_code_node <- function(x) { info <- xml_attr(x, "info") substr(xml_text(x), 1, 2) == "r " || (!is.na(info) && grepl("^[{][a-zA-z]+[}, ]", info)) } parse_md_pos <- function(text) { nums <- map(strsplit(text, "[:-]"), as.integer) data.frame( start_line = map_int(nums, \(x) x[[1]]), start_column = map_int(nums, \(x) x[[2]]), end_line = map_int(nums, \(x) x[[3]]), end_column = map_int(nums, \(x) x[[4]]) ) } eval_code_nodes <- function(nodes) { evalenv <- roxy_meta_get("evalenv") # This should only happen in our test cases if (is.null(evalenv)) { evalenv <- new.env(parent = baseenv()) } map_chr(nodes, eval_code_node, env = evalenv) } eval_code_node <- function(node, env) { if (xml_name(node) == "code") { # write knitr markup for inline code text <- paste0("`", xml_text(node), "`") } else { lang <- xml_attr(node, "info") # write knitr markup for fenced code text <- paste0("```", if (!is.na(lang)) lang, "\n", xml_text(node), "```\n") } chunk_opts <- utils::modifyList( knitr_chunk_defaults(), as.list(roxy_meta_get("knitr_chunk_options", NULL)) ) roxy_knit(text, env, chunk_opts) } knitr_chunk_defaults <- function() { list( error = FALSE, fig.path = "man/figures/", fig.process = basename, comment = "#>", collapse = TRUE ) } re_set_all_pos <- function(text, pos, value, nodes) { # Cmark has a bug when reporting source positions for multi-line # code tags, and it does not count the indenting space in the # continuation lines: https://github.com/commonmark/cmark/issues/296 types <- xml_name(nodes) if (any(types == "code" & pos$start_line != pos$end_line)) { cli::cli_abort("multi-line `r ` markup is not supported", call = NULL) } # Need to split the string, because of the potential multi-line # code tags, and then also recode the positions lens <- nchar(strsplit(text, "\n", fixed = TRUE)[[1]]) shifts <- c(0, cumsum(lens + 1L)) shifts <- shifts[-length(shifts)] start <- shifts[pos$start_line] + pos$start_column end <- shifts[pos$end_line] + pos$end_column # Create intervals for the parts we keep keep_start <- c(1, end + 2L) keep_end <- c(start - 2L, nchar(text)) # Now piece them together out <- paste0( substring(text, keep_start, keep_end), c(value, ""), collapse = "" ) attributes(out) <- attributes(text) out } roxygen2/R/collate.R0000644000176200001440000000634115166171276014016 0ustar liggesusers#' Update Collate field in DESCRIPTION #' #' @description #' By default, R loads files in alphabetical order. This is fine if your #' package doesn't have any cross-file dependencies, but if you're using a tool #' like S4, you'll need to make sure that classes are loaded before subclasses #' and generics are defined before methods. You can do this by hand by setting #' the `Collate` field in the `DESCRIPTION` or automate it by using `@include` #' tags to specify the cross-file dependencies: #' #' ```R #' #' @include before.R #' NULL #' ``` #' #' If there are no `@include` tags, roxygen2 will leave the `Collate` field as #' is. This makes it easier to use roxygen2 with an existing collate directive, #' but if you remove all your `@include` tags, you'll need to also #' manually delete the collate field. #' #' Generally, you should not need to run this function yourself; it will be #' run automatically by any package that needs to load your R files in #' collation order. #' #' `update_collate()` is not not technically a [roclet], like [rd_roclet()] #' and [namespace_roclet()], because you have to be able to load the pacakge #' before you can process it with roclets. However, because it was historical #' implemented as a roclet, it's still controlled by the `roclets` argument of #' [roxygenize()]. #' #' @param base_path Path to package directory. #' @examples #' #' If `example-a.R`, `example-b.R` and `example-c.R` live in `R/` #' #' and we're in `example-a.R`, then the following @include tag #' #' ensures that example-b and example-c are sourced before example-a. #' #' @include example-b.R example-c.R #' NULL #' @export #' @aliases @include update_collate <- function(base_path) { check_string(base_path) if (!file.exists(base_path)) { cli::cli_abort("{.path {base_path}} doesn't exist.") } new <- generate_collate(file.path(base_path, "R")) if (is.null(new)) { return(invisible()) } desc_path <- file.path(base_path, "DESCRIPTION") old <- desc::desc_get_collate(file = desc_path) if (!identical(old, new)) { cli::cli_inform("Updating collate directive in {.path {desc_path}}") desc::desc_set_collate(new, file = desc_path) } invisible() } generate_collate <- function(base_path) { paths <- sort_c(dir(base_path, pattern = "[.][Rr]$", full.names = TRUE)) includes <- lapply(paths, find_and_filter_includes, paths = basename(paths)) names(includes) <- paths n <- sum(map_int(includes, length)) if (n == 0) { return() } topo <- TopoSort$new() for (path in paths) { file <- base_path(path, base_path) topo$add(file) for (include in includes[[path]]) { topo$add_ancestor(file, include) } } topo$sort() } find_and_filter_includes <- function(path, paths) { includes <- find_includes(path) invalid <- setdiff(includes, paths) if (length(invalid) > 0) { for (include in invalid) { path <- cli::style_hyperlink(basename(path), paste0("file://", path)) cli::cli_inform(c( x = "{path}: unknown path in `@include {invalid}`." )) } } intersect(includes, paths) } base_path <- function(path, base) { path <- normalizePath(path, winslash = "/") base <- normalizePath(base, winslash = "/") sub(paste0(base, "/"), "", path, fixed = TRUE) } roxygen2/R/rd-family.R0000644000176200001440000000374315156340121014244 0ustar liggesusers#' @export roxy_tag_parse.roxy_tag_family <- function(x) tag_markdown(x) #' @export roxy_tag_rd.roxy_tag_family <- function(x, base_path, env) { rd_section(x$tag, x$val) } #' @export format.rd_section_family <- function(x, ...) { NULL } # ------------------------------------------------------------------------- topics_process_family_prefix <- function(family) { default <- paste0("Other ", family, ":") # check for meta (use default prefix when unset) meta <- roxy_meta_get("rd_family_title") if (is.null(meta)) { return(default) } # validate meta structure valid <- is.character(meta) || is.list(meta) if (!valid) { cli::cli_abort( "{.code rd_family_title} is set, but is not a named list / vector." ) } # extract element prefix <- meta[[family]] if (is.null(prefix)) { return(default) } prefix <- markdown(prefix, tag = "family") # Ensure prefix ends with a colon (#1656) if (!grepl(":$", prefix)) { prefix <- paste0(prefix, ":") } prefix } topics_process_family <- function(topics, env) { family_index <- invert(topics$simple_values("family")) aliases <- topics$simple_values("alias") for (topic_name in names(topics$topics)) { topic <- topics$get(topic_name) families <- topic$get_value("family") for (family in families) { related <- family_index[[family]] topic$add(rd_section("concept", family)) others <- setdiff(related, topic_name) if (length(others) < 1) { next } other_aliases <- aliases[others] other_aliases_order <- map_chr(other_aliases, \(x) escape(x[1])) by_file <- map_chr( other_aliases[order_c(other_aliases_order)], function(x) { rd_link(NA, escape(x[1]), escape(fun_suffix(x[1], env)), code = TRUE) } ) links <- paste(by_file, collapse = ",\n") seealso <- topics_process_family_prefix(family) topic$add(rd_section("seealso", paste0(seealso, "\n", links))) } } invisible() } roxygen2/R/roclet.R0000644000176200001440000001002315156561373013653 0ustar liggesusers#' Build a new roclet #' #' @description #' Roclets are roxygen2's plugin system for producing different types of output, #' like Rd files ([rd_roclet()]) or the `NAMESPACE` file ([namespace_roclet()]). #' #' To create a new roclet, you will need to create a constructor function that #' calls `roclet()`, and then implement the methods described below. #' #' See `vignette("extending")` for more details. #' #' @section Methods: #' #' * `roclet_preprocess()` is called after blocks have been parsed but before #' code has been evaluated. This should only be needed if your roclet affects #' how code will be evaluated. Should return a roclet. #' #' * `roclet_process()` called after blocks have been evaluated; i.e. the #' `@eval` tag has been processed, and the object associated with each block #' has been determined. #' #' * `roclet_output()` is given the output from `roclet_process()` and should #' produce files on disk. #' #' * `roclet_clean()` called when `roxygenise(clean = TRUE)`. Should remove #' any files created by the roclet. #' #' ### Deprecated methods #' #' `roclet_tags()` is no longer used; instead provide a [roxy_tag_parse()] #' method for each tag. #' #' @param x A `roclet` object. #' @param blocks A list of [roxy_block] objects. #' @param results Value returned from your `roclet_process()` method. #' @param base_path Path to root of source package. #' @param env Package environment. #' @keywords internal #' @name roclet NULL #' @export #' @rdname roclet #' @param subclass Class of the roclet, character vector. #' @examples #' # Custom roclet #' custom_roclet <- roclet("custom") #' # Roclet that extends the existing Rd roclet. #' supercharged_rd_roclet <- roclet(c("cool", "rd")) roclet <- function(subclass, ...) { structure(list(...), class = c(paste0("roclet_", subclass), "roclet")) } #' @export #' @rdname roclet roclet_preprocess <- function(x, blocks, base_path) { UseMethod("roclet_preprocess") } #' @export roclet_preprocess.default <- function(x, blocks, base_path) { x } #' @export #' @rdname roclet roclet_process <- function(x, blocks, env, base_path) { UseMethod("roclet_process") } #' @export #' @rdname roclet roclet_output <- function(x, results, base_path, ...) { UseMethod("roclet_output", x) } #' @export #' @rdname roclet roclet_clean <- function(x, base_path) { UseMethod("roclet_clean") } #' @export #' @rdname roclet roclet_tags <- function(x) { UseMethod("roclet_tags") } #' Create a roclet from a string #' #' This provides a flexible way of specifying a roclet in a string. #' #' @param x Arbitrary R code evaluated in roxygen2 package. #' @family extending #' @export #' @examples #' # rd, namespace, and vignette work for backward compatibility #' roclet_find("rd") #' #' # But generally you should specify the name of a function that #' # returns a roclet #' roclet_find("rd_roclet") #' #' # If it lives in another package, you'll need to use :: #' roclet_find("roxygen2::rd_roclet") #' #' # If it takes parameters (which no roclet does currently), you'll need #' # to call the function #' roclet_find("roxygen2::rd_roclet()") roclet_find <- function(x) { check_string(x) env <- new.env(parent = getNamespace("roxygen2")) env$rd <- rd_roclet env$namespace <- namespace_roclet env$vignette <- vignette_roclet expr <- parse(text = x) res <- eval(expr, env) if (is.function(res)) { res <- res() } if (!is.roclet(res)) { cli::cli_abort("Must return a roclet.") } res } is.roclet <- function(x) inherits(x, "roclet") #' Process roclet on string and capture results #' #' Useful for testing. #' #' @param roclet Name of roclet to use for processing. #' @param input Source string #' @param wd Working directory #' @export #' @family extending roc_proc_text <- function(roclet, input, wd = NULL) { stopifnot(is.roclet(roclet)) file <- tempfile() write_lines(input, file) on.exit(unlink(file)) if (!is.null(wd)) { withr::local_dir(wd) } env <- env_file(file) blocks <- parse_text(input, env = env) roclet_process(roclet, blocks, env = env, base_path = ".") } roxygen2/R/rd-describe-in.R0000644000176200001440000001557315172410643015160 0ustar liggesusers#' @export roxy_tag_parse.roxy_tag_describeIn <- function(x) { if (!is.na(x$raw) && !grepl("[[:space:]]+", x$raw)) { warn_roxy_tag( x, c( "requires a name and description", i = "Did you want @rdname instead?" ) ) NULL } else { tag_two_part(x, "a topic name", "a description", multiline = TRUE) } } topic_add_describe_in <- function(topic, block, env) { tag <- block_get_tag(block, "describeIn") if (is.null(tag)) { return() } if (is.null(block$object)) { warn_roxy_tag(tag, "must be used with an object") return() } if (block_has_tags(block, "name")) { warn_roxy_tag(tag, "can not be used with @name") return() } if (block_has_tags(block, "rdname")) { warn_roxy_tag(tag, "can not be used with @rdname") return() } if (is.null(object_name(block$object))) { warn_roxy_tag(tag, "not supported with this object type") return() } dest <- find_object(tag$val$name, env) metadata <- build_minidesc_metadata(block$object, dest) topic$add(rd_section_minidesc( name = object_name(block$object), desc = tag$val$description, extends = metadata$extends, generic = metadata$generic, class = metadata$class )) dest$topic } # Field ------------------------------------------------------------------- #' Record data for minidescription sections from `@describeIn` #' #' @param name name of the source function. #' @param desc description passed to `@describeIn`. #' @param extends how the source function extends the destination function: #' - `"generic"` if the source extends a (S3 or S4) generic in the destination, #' - `"class"` if the source extends an informal S3 or formal S4 constructor #' in the destination. #' For S3, there is always only *one* class. #' For S4, the methods' signature is used instead, to cover multiple dispatch. #' - `""` (default) otherwise. #' @param generic,class name of the generic and class that is being extended by #' the method, otherwise empty string (`""`). #' @return a dataframe with one row for each `@describeIn`, wrapped inside #' `rd_section()` #' @noRd rd_section_minidesc <- function( name, desc, extends = c("", "generic", "class"), generic = "", class = "" ) { check_string(name) check_character(desc) rlang::arg_match(extends) check_string(generic) check_string(class) data <- data.frame( name = name, desc = desc, extends = extends, generic = generic, class = class ) rd_section("minidesc", data) } #' @export merge.rd_section_minidesc <- function(x, y, ..., block) { stopifnot(identical(class(x), class(y))) rd_section("minidesc", rbind(x$value, y$value)) } # Rd Output ------------------------------------------------------------------- #' @export format.rd_section_minidesc <- function(x, ...) { order <- intersect(c("generic", "class", ""), unique(x$value$extends)) by <- factor(x$value$extends, levels = order) subsections <- split(x$value, by) body <- map2_chr(subsections, names(subsections), format_section) paste0(body, collapse = "\n") } format_section <- function(df, type) { title <- switch( type, class = "Methods (by generic)", generic = "Methods (by class)", "Functions" ) bullets <- paste0("\\code{", df$name, "}: ", df$desc, "\n") body <- paste0( "\\itemize{\n", paste0("\\item ", bullets, "\n", collapse = ""), "}" ) paste0("\\section{", title, "}{\n", body, "}") } # Helpers ----------------------------------------------------------------- # Imperfect: # * will fail with S3 methods that need manual disambiguation (rare) # * can't use if @name overridden, but then you could just the use alias find_object <- function(name, env) { if (methods::isClass(name, where = env)) { object(methods::getClass(name, where = env), NULL, "s4class") } else if (exists(name, envir = env)) { object_from_name(name, env, NULL) } else { object(NULL, name, "data") } } #' Build metadata for how to present `@describeIn` tag #' @return list of character scalars named `extends`, `generic`, `class`. #' See rd_section_minidesc() for details. #' @noRd build_minidesc_metadata <- function(src, dest) { src_type <- class(src)[1] dest_type <- class(dest)[1] dest_name <- as.character(dest$topic) if (src_type == "s3method") { generic <- attr(src$value, "s3method")[1] class <- attr(src$value, "s3method")[2] if (dest_type == "s3generic" && generic == dest_name) { # src method fits dest generic extends <- "generic" } else if (fits_constructor(dest_name, src)) { # src method fits informal dest constructor (heuristically) extends <- "class" } else { extends <- "" } } else if (src_type == "s4method") { generic <- as.character(src$value@generic) class <- sig2class(src$value@defined) if (dest_type == "s4generic") { # TODO must test whether src method fits dest generic extends <- "generic" } else if (dest_type == "s4class") { extends <- "class" # TODO must test whether src method fits dest constructor } else { extends <- "" } } else { generic <- "" class <- "" extends <- "" } list(extends = extends, generic = generic, class = class) } # Turn S4 signature into a string sig2class <- function(sig) { if (length(sig) == 1) { as.character(sig) } else { paste0(names(sig), " = ", sig, collapse = ",") } } # Is destination is probably constructor for src? fits_constructor <- function(dest_name, src) { src_class <- attr(src$value, "s3method")[2] # simple case where class name is the same as the constructor name if (src_class == dest_name) { return(TRUE) } # more complex case where class name = package name + constructor name evalenv <- roxy_meta_get("env") %||% parent.frame() # needed for tests pkg_name <- utils::packageName(evalenv) %||% "" src_class == paste0(pkg_name, "_", dest_name) } object_name <- function(x) { UseMethod("object_name") } #' @export object_name.default <- function(x) { x$alias } #' @export object_name.function <- function(x) { object_name_fun(x$alias, x) } #' @export object_name.s3generic <- object_name.function #' @export object_name.s3method <- function(x) { method <- attr(x$value, "s3method") as.character(function_usage(method[[1]], list(as.name(method[[2]])))) # # name <- paste(, collapse = ".") # object_name_fun(name, x) } #' @export object_name.s4generic <- function(x) { object_name_fun(x$value@generic, x) } #' @export object_name.s4method <- function(x) { classes <- lapply(x$value@defined, as.name) if (length(classes) == 1) { names(classes) <- NULL } as.character(function_usage(x$value@generic, classes)) } object_name_fun <- function(name, x, format_name = identity) { if (is_replacement_fun(name) || is_infix_fun(name)) { args <- formals(x$value) } else { args <- NULL } as.character(function_usage(name, args, format_name)) } roxygen2/R/safety.R0000644000176200001440000000075015151411610013643 0ustar liggesusersmade_by_roxygen <- function(path) { if (!file.exists(path)) { return(TRUE) } first <- read_lines(path, n = 1) check_made_by(first) } check_made_by <- function(first) { if (length(first) == 0L) { return(FALSE) } grepl("^. Generated by roxygen2", first) } made_by <- function(comment) { # This text is used by IDE to display a special warning. DO NOT CHANGE # without consulting the IDE team paste0(comment, " Generated by roxygen2: do not edit by hand\n") } roxygen2/R/field.R0000644000176200001440000000472015166171276013455 0ustar liggesusers#' Construct an `rd_section` object #' #' An `rd_section` represents an Rd command that can appear at the top-level #' of an Rd document, like `\name{}`, `\title{}`, `\description{}`, or #' `\section{}`. #' #' @section Methods: #' If provide your own `rd_section` type, you'll also need to define a #' `format.rd_section_{type}` method that returns formatted Rd output. You #' may also need to provide a `merge.rd_section_{type}` method if two #' sections can not be combined with `rd_section(x$type, c(x$value, y$value))`. #' See `vignette("extending")` for more details. #' #' @param type Section type. Stored in `type` field, and in class #' `rd_section_{type}`. To avoid namespace clashes between different #' extensions, this should include the package name. #' @param value Section data. Only used by `format()` and `merge()` methods. #' @export #' @family extending rd_section <- function(type, value) { if (is.null(value) || identical(value, "NULL")) { # NULL is special sentinel value that suppresses output of that field return() } structure( list( type = type, value = value ), class = c(paste0("rd_section_", type), "rd_section") ) } #' @export print.rd_section <- function(x, ...) { cat(format(x), "\n") } #' @export format.rd_section <- function(x, ...) { cli::cli_abort("{.fn format.{class(x)[[1]]}} method not found.") } #' @export merge.rd_section <- function(x, y, ...) { stopifnot(identical(class(x), class(y))) rd_section(x$type, c(x$value, y$value)) } format_rd <- function(x, ..., sort = TRUE) { # One rd macro for each value x$value <- unique(x$value) if (sort) { x$value <- sort_c(x$value) } map_chr(x$value, rd_macro, field = x$type) } format_first <- function(x, ...) { # Only use the first value rd_macro(x$type, x$value[1]) } format_collapse <- function(x, ..., indent = 0, exdent = 0) { # Collapse all into a single string value <- paste0(x$value, collapse = "\n\n") rd_macro(x$type, value, space = TRUE) } rd_section_description <- function(name, dt, dd) { if (length(dt) == 0) { return("") } paste0("\\section{", name, "}{\n\n", rd_enumerate(dt, dd), "}\n") } rd_subsection_description <- function(name, dt, dd) { if (length(dt) == 0) { return("") } paste0("\\subsection{", name, "}{\n\n", rd_enumerate(dt, dd), "\n}\n") } rd_enumerate <- function(dt, dd) { items <- paste0("\\item{\\code{", dt, "}}{", dd, "}", collapse = "\n\n") paste0("\\describe{\n", items, "\n}") } roxygen2/R/load.R0000644000176200001440000000544115161504570013302 0ustar liggesusers#' Load package code #' #' @description #' roxygen2 is a dynamic documentation system, which means it works with the #' objects inside your package, not just the source code used to create them. #' These functions offer various ways of loading your package to suit various #' constraints: #' #' * `load_pkgload()` uses `pkgload::load_all()` to simulate package loading #' as closely as we know how. It offers high fidelity handling of code that #' uses S4, but requires that the package be compiled. #' #' * `load_source()` simulates package loading by attaching packages listed in #' `Depends` and `Imports`, then sources all files in the `R/` directory. #' This was the default strategy used in roxygen2 6.0.0 and earlier; #' it's primary advantage is that it does not need compilation. #' #' * `load_installed()` uses the installed version of the package. Use this #' strategy if you have installed a development version of the package #' already. This is the highest fidelity strategy, but requires work #' outside of roxygen2. #' #' You can change the default strategy for your function with roxygen2 `load` #' option. Override the default off `pkgload` to use the `source` or #' `installed` strategies: #' #' ``` #' Roxygen: list(load = "source") #' ``` #' @name load #' @param path Path to source package NULL #' @rdname load #' @export load_pkgload <- function(path) { pkgload::load_all(path, helpers = FALSE, attach_testthat = FALSE)$env } #' @rdname load #' @export load_installed <- function(path) { package <- desc::desc_get_field("Package", file = path) asNamespace(package) } #' @rdname load #' @export load_source <- function(path) { # Create environment env <- new.env(parent = globalenv()) methods::setPackageName("roxygen_devtest", env) # Attach dependencies deps <- desc::desc_get_deps(path) pkgs <- deps$package[ deps$type %in% c("Depends", "Imports") & deps$package != "R" ] lapply(pkgs, require, character.only = TRUE) # Source files lapply(package_files(path), sys_source, envir = env) env } sys_source <- function(file, envir = baseenv()) { exprs <- parse(text = read_lines(file)) for (expr in exprs) { eval(expr, envir = envir) } invisible() } # Helpers ----------------------------------------------------------------- find_load_strategy <- function( x, option = roxy_meta_get("load", "pkgload"), call = caller_env() ) { if (is.function(x)) { return(x) } if (is.null(x)) { x <- option check_string(x, arg = I("roxygen2 `load` option"), call = call) } else { check_string(x, arg = "load_code", call = call) } switch( x, pkgload = load_pkgload, source = load_source, installed = load_installed, cli::cli_abort( "Unknown value of {.code load} option: {.str {x}}.", call = call ) ) } roxygen2/R/utils-rd.R0000644000176200001440000000544715166171276014144 0ustar liggesusers# Output ------------------------------------------------------------------ # A simple object to represent rd escaped text. rd <- function(x) { structure(x, class = "rd") } #' @export c.rd <- function(...) { rd(NextMethod()) } #' @export print.rd <- function(x, ...) { out <- paste0(" ", x, collapse = "\n") cat(out) } escape <- function(x) UseMethod("escape") #' @export escape.NULL <- function(x) NULL #' @export escape.rd <- function(x) x #' @export escape.character <- function(x) { # wrap_usage uses \u{A0}, the unicode non-breaking space, which # is not necessarily valid in windows locales. useBytes is a quick # hack to fix the problem. x1 <- gsub(r"(\)", r"(\\)", x, fixed = TRUE, useBytes = TRUE) x2 <- gsub("%", r"(\%)", x1, fixed = TRUE, useBytes = TRUE) rd(x2) } # Works like paste, but automatically escapes all input variables, # but not literal strings build_rd <- function(..., collapse = NULL, sep = "") { args <- dots(...) env <- parent.frame() escaped <- lapply(args, function(arg) { if (is.character(arg)) { return(arg) } escape(eval(arg, env)) }) string <- do.call("paste", c(escaped, list(collapse = collapse, sep = sep))) rd(string) } dots <- function(...) { eval(substitute(alist(...))) } # Translate a field and values into an Rd macro. # Multiple values get their own braces. rd_macro <- function(field, ..., space = FALSE) { if (space) { values <- paste0("\n", paste0(..., collapse = "\n"), "\n") } else { values <- trimws(c(...)) } paste0("\\", field, paste0("{", values, "}", collapse = "")) } # Input ------------------------------------------------------------------- get_tags <- function(rd, tag) { Filter(\(x) identical(attr(x, "Rd_tag"), tag), rd) } # helpers ----------------------------------------------------------------- parse_rd <- function(x) { con <- textConnection(x) on.exit(close(con), add = TRUE) tryCatch( tools::parse_Rd(con, fragment = TRUE, encoding = "UTF-8"), warning = function(cnd) NULL ) } # Generated in .onLoad() as_character_rd <- NULL make_as_character_rd <- function() { # "as.character.Rd" appears to a few commands in TWOARGS # this code hacks the body of the function to add it fn <- internal_f("tools", "as.character.Rd") body <- body(fn) idx <- detect_index( body, \(x) is_call(x, "<-", 2) && is_symbol(x[[2]], "TWOARG") ) if (idx == 0) { return(fn) } body[[idx]][[3]] <- call_modify( body[[idx]][[3]], "\\href", "\\ifelse", "\\if" ) body(fn) <- body fn } has_topic <- function(topic, package) { tryCatch( { out <- exec("help", topic, package, .env = global_env()) inherits(out, "dev_topic") || (inherits(out, "help_files_with_topic") && length(out) == 1) }, error = function(c) FALSE ) } roxygen2/R/utils-warn.R0000644000176200001440000000335515154537013014472 0ustar liggesusers#' @export #' @rdname roxy_tag #' @param message Warning message #' @inheritParams rlang::abort #' @param envir passed to `rlang::warn()` as `.envir`. warn_roxy_tag <- function(tag, message, parent = NULL, envir = parent.frame()) { if (is.null(tag)) { names(message)[[1]] <- "x" cli::cli_inform(message, parent = parent, .envir = envir) return(invisible()) } tag_name <- cli::format_inline("{.strong @{tag$tag}} ") if (is.null(tag$raw)) { tag_name <- paste(tag_name, "(automatically generated) ") } message[[1]] <- paste0(tag_name, message[[1]]) warn_roxy(tag$file, tag$line, message, parent = parent, envir = envir) } warn_roxy_block <- function( block, message, parent = NULL, envir = parent.frame() ) { warn_roxy(block$file, block$line, message, parent = parent, envir = envir) } warn_roxy_function <- function( fun, message, parent = NULL, envir = parent.frame() ) { srcref <- attr(fun, "srcref") file <- attr(srcref, "srcfile")$filename %||% '' line <- as.vector(srcref)[[1]] %||% 0 warn_roxy(file, line, message, parent = parent, envir = envir) } warn_roxy <- function( file, line, message, parent = NULL, envir = parent.frame() ) { link <- cli::style_hyperlink( paste0(basename(file), ":", line), paste0("file://", file), params = c(line = line, col = 1) ) message[[1]] <- paste0(link, ": ", message[[1]], ".") names(message)[[1]] <- "x" cli::cli_inform(message, parent = parent, .envir = envir) } warn_roxy_topic <- function( topic, message, parent = NULL, envir = parent.frame() ) { message[[1]] <- paste0("In topic '", topic, "': ", message[[1]], ".") names(message)[[1]] <- "x" cli::cli_inform(message, parent = parent, .envir = envir) } roxygen2/R/markdown-escaping.R0000644000176200001440000001625015166171276016004 0ustar liggesusers#' Escape fragile Rd tags #' #' @description #' `escape_rd_for_md()` replaces fragile Rd tags with placeholders, to avoid #' interpreting them as markdown. `unescape_rd_for_md()` puts the original #' text back in place of the placeholders after the markdown parsing is done. #' The fragile tags are listed in `escaped_for_md`. #' #' Some Rd macros are treated specially: #' #' * For `if`, markdown is only allowed in the second argument. #' * For `ifelse` markdown is allowed in the second and third arguments. #' #' @param text Input text. Potentially contains Rd and/or #' markdown markup. #' @returns #' * `escape_rd_for_md`: a "safe" version of the input text, where #' each fragile Rd tag is replaced by a placeholder. The #' original text is added as an attribute for each placeholder. #' * `unescape_rd_for_md`: the original Rd text. #' @rdname markdown-internals #' @keywords internal escape_rd_for_md <- function(text) { rd_tags <- find_fragile_rd_tags(text, escaped_for_md) protected <- protect_rd_tags(text, rd_tags) double_escape_md(protected) } escaped_for_md <- paste0( "\\", c( "acronym", "code", "command", "CRANpkg", "deqn", "doi", "dontrun", "dontshow", "donttest", "email", "env", "eqn", "figure", "file", "if", "ifelse", "kbd", "link", "linkS4class", "method", "mjeqn", "mjdeqn", "mjseqn", "mjsdeqn", "mjteqn", "mjtdeqn", "newcommand", "option", "out", "packageAuthor", "packageDescription", "packageDESCRIPTION", "packageIndices", "packageMaintainer", "packageTitle", "pkg", "PR", "preformatted", "renewcommand", "S3method", "S4method", "samp", "special", "testonly", "url", "var", "verb" ) ) #' @param rd_text The markdown parsed and interpreted text. #' @param esc_text The original escaped text from #' `escape_rd_for_md()`. #' @rdname markdown-internals unescape_rd_for_md <- function(rd_text, esc_text) { id <- attr(esc_text, "roxygen-markdown-subst")$id tags <- attr(esc_text, "roxygen-markdown-subst")$tags for (i in seq_len(nrow(tags))) { ph <- paste0(id, "-", i, "-") rd_text <- sub(ph, tags$text[i], rd_text, fixed = TRUE) } rd_text } #' Find all fragile tags (int the supplied list) in the text #' #' Ignore the tags that are embedded into a fragile tag. #' #' @param text Input text, character scalar. #' @param fragile Character vector of fragile tags to find. #' @return Data frame of fragile tags, with columns: #' `tag`, `start`, `end`, `argend`, #' `text`. #' #' @noRd find_fragile_rd_tags <- function(text, fragile) { tags <- find_all_rd_tags(text) ftags <- tags[tags$tag %in% fragile, ] ## Remove embedded ones keep <- map_lgl(seq_len(nrow(ftags)), function(i) { sum(ftags$start <= ftags$start[i] & ftags$argend >= ftags$argend[i]) == 1 }) ftags <- ftags[keep, ] if (nrow(ftags)) { ftags$text <- substring(text, ftags$start, ftags$argend) } ftags } #' Find all (complete) Rd tags in a string #' #' Complete means that we include the argument(s) as well. #' #' @param text Input text, character scalar. #' #' @noRd find_all_rd_tags <- function(text) { text_len <- nchar(text) ## Find the tag names tags <- find_all_tag_names(text) ## Find the end of the argument list for each tag. Note that ## tags might be embedded into the arguments of other tags. tags$argend <- map_int(seq_len(nrow(tags)), function(i) { tag_plus <- substr(text, tags$end[i], text_len) findEndOfTag(tag_plus, is_code = FALSE, start = 0L) + tags$end[i] }) tags } #' Find all tag names in a string #' #' Note that we also protect these tags within code, strings #' and comments, for now. We'll see if this causes any #' problems. #' #' @param text Input text, scalar. #' @return Data frame, with columns: `tag`, `start`, #' `end`. #' #' @noRd find_all_tag_names <- function(text) { ## Find the tags without arguments first m <- gregexpr(r"(\\[a-zA-Z][a-zA-Z0-9]*)", text)[[1]] if (m[[1]] == -1L) { tag_pos <- matrix( integer(), ncol = 2, dimnames = list(NULL, c("start", "end")) ) } else { tag_pos <- cbind( start = as.integer(m), end = as.integer(m) + attr(m, "match.length") - 1L ) } if (nrow(tag_pos) == 0) { data.frame(tag = character(), start = integer(), end = integer()) } else { data.frame( tag = substring(text, tag_pos[, "start"], tag_pos[, "end"]), as.data.frame(tag_pos) ) } } #' Replace fragile Rd tags with placeholders #' #' @param text The text, character scalar. #' @param rd_tags Fragile Rd tags, in a data frame, #' as returned by `find_fragile_rd_tags`. #' @return Text, after the substitution. The original #' text is added as an attribute. #' #' @noRd protect_rd_tags <- function(text, rd_tags) { id <- make_random_string() text <- re_sub_same(text, rd_tags, id) attr(text, "roxygen-markdown-subst") <- list(tags = rd_tags, id = id) text } #' Replace parts of the same string #' #' It assumes that the intervals to be replaced do not #' overlap. Gives an error otherwise. #' #' @param str String scalar. #' @param repl Data frame with columns: `start`, `end`, #' `argend`, `text`. #' @param id Placeholder string. #' @return Input string with the replacements performed. #' Note that all replacements are performed in parallel, #' at least conceptually. #' #' @noRd re_sub_same <- function(str, repl, id) { repl <- repl[order(repl$start), ] if (is.unsorted(repl$end) || is.unsorted(repl$argend)) { cli::cli_abort("Replacement intervals must not overlap.", .internal = TRUE) } for (i in seq_len(nrow(repl))) { ## The trailing - is needed, to distinguish between -1 and -10 new_text <- paste0(id, "-", i, "-") str <- paste0( substr(str, 1, repl$start[i] - 1), new_text, substr(str, repl$argend[i] + 1, nchar(str)) ) ## Need to shift other coordinates (we shift everything, ## it is just simpler). inc <- nchar(new_text) - (repl$argend[i] - repl$start[i] + 1) repl$start <- repl$start + inc repl$end <- repl$end + inc repl$argend <- repl$argend + inc } str } #' Make a random string #' #' We use this as the placeholder, to make sure that the #' placeholder does not appear in the text. #' #' @return String scalar #' #' @noRd make_random_string <- function(length = 32) { paste( sample(c(LETTERS, letters, 0:9), length, replace = TRUE), collapse = "" ) } #' Check markdown escaping #' #' This is a regression test for Markdown escaping. #' #' @details #' Each of the following bullets should look the same when rendered: #' #' * Backticks: `\`, `\%`, `\$`, `\_` #' * `\verb{}`: \verb{\\}, \verb{\\%}, \verb{\$}, \verb{\_} #' #' \[ this isn't a link \] #' \\[ neither is this \\] #' #' @param text Input text. #' @return Double-escaped text. #' @keywords internal #' @examples #' "%" # percent #' "\"" # double quote #' '\'' # single quote double_escape_md <- function(text) { text <- gsub(r"(\)", r"(\\)", text, fixed = TRUE) # De-dup escaping used to avoid [] creating a link text <- gsub(r"(\\[)", r"(\[)", text, fixed = TRUE) text <- gsub(r"(\\])", r"(\])", text, fixed = TRUE) text } roxygen2/R/utils-io.R0000644000176200001440000000142415151411610014114 0ustar liggesusersreadLines <- function(...) cli::cli_abort("Use read_lines!", .internal = TRUE) writeLines <- function(...) cli::cli_abort("Use write_lines!", .internal = TRUE) read_lines <- function(path, n = -1L) { base::readLines(path, n = n, encoding = "UTF-8", warn = FALSE) } write_lines <- function(text, path, line_ending = detect_line_ending(path)) { # we need to convert any embedded newlines as well text <- gsub("\r?\n", line_ending, text) path <- file(path, open = "wb") base::writeLines(enc2utf8(text), path, sep = line_ending, useBytes = TRUE) close(path) } detect_line_ending <- function(path) { tryCatch( { samp <- suppressWarnings(readChar(path, nchars = 500)) if (isTRUE(grepl("\r\n", samp))) "\r\n" else "\n" }, error = function(e) "\n" ) } roxygen2/R/import-standalone-purrr.R0000644000176200001440000001316715154314256017201 0ustar liggesusers# Standalone file: do not edit by hand # Source: https://github.com/r-lib/rlang/blob/HEAD/R/standalone-purrr.R # Generated by: usethis::use_standalone("r-lib/rlang", "purrr") # ---------------------------------------------------------------------- # # --- # repo: r-lib/rlang # file: standalone-purrr.R # last-updated: 2023-02-23 # license: https://unlicense.org # imports: rlang # --- # # This file provides a minimal shim to provide a purrr-like API on top of # base R functions. They are not drop-in replacements but allow a similar style # of programming. # # ## Changelog # # 2023-02-23: # * Added `list_c()` # # 2022-06-07: # * `transpose()` is now more consistent with purrr when inner names # are not congruent (#1346). # # 2021-12-15: # * `transpose()` now supports empty lists. # # 2021-05-21: # * Fixed "object `x` not found" error in `imap()` (@mgirlich) # # 2020-04-14: # * Removed `pluck*()` functions # * Removed `*_cpl()` functions # * Used `as_function()` to allow use of `~` # * Used `.` prefix for helpers # # nocov start map <- function(.x, .f, ...) { .f <- as_function(.f, env = global_env()) lapply(.x, .f, ...) } walk <- function(.x, .f, ...) { map(.x, .f, ...) invisible(.x) } map_lgl <- function(.x, .f, ...) { .rlang_purrr_map_mold(.x, .f, logical(1), ...) } map_int <- function(.x, .f, ...) { .rlang_purrr_map_mold(.x, .f, integer(1), ...) } map_dbl <- function(.x, .f, ...) { .rlang_purrr_map_mold(.x, .f, double(1), ...) } map_chr <- function(.x, .f, ...) { .rlang_purrr_map_mold(.x, .f, character(1), ...) } .rlang_purrr_map_mold <- function(.x, .f, .mold, ...) { .f <- as_function(.f, env = global_env()) out <- vapply(.x, .f, .mold, ..., USE.NAMES = FALSE) names(out) <- names(.x) out } map2 <- function(.x, .y, .f, ...) { .f <- as_function(.f, env = global_env()) out <- mapply(.f, .x, .y, MoreArgs = list(...), SIMPLIFY = FALSE) if (length(out) == length(.x)) { set_names(out, names(.x)) } else { set_names(out, NULL) } } map2_lgl <- function(.x, .y, .f, ...) { as.vector(map2(.x, .y, .f, ...), "logical") } map2_int <- function(.x, .y, .f, ...) { as.vector(map2(.x, .y, .f, ...), "integer") } map2_dbl <- function(.x, .y, .f, ...) { as.vector(map2(.x, .y, .f, ...), "double") } map2_chr <- function(.x, .y, .f, ...) { as.vector(map2(.x, .y, .f, ...), "character") } imap <- function(.x, .f, ...) { map2(.x, names(.x) %||% seq_along(.x), .f, ...) } pmap <- function(.l, .f, ...) { .f <- as.function(.f) args <- .rlang_purrr_args_recycle(.l) do.call( "mapply", c( FUN = list(quote(.f)), args, MoreArgs = quote(list(...)), SIMPLIFY = FALSE, USE.NAMES = FALSE ) ) } .rlang_purrr_args_recycle <- function(args) { lengths <- map_int(args, length) n <- max(lengths) stopifnot(all(lengths == 1L | lengths == n)) to_recycle <- lengths == 1L args[to_recycle] <- map(args[to_recycle], function(x) rep.int(x, n)) args } keep <- function(.x, .f, ...) { .x[.rlang_purrr_probe(.x, .f, ...)] } discard <- function(.x, .p, ...) { sel <- .rlang_purrr_probe(.x, .p, ...) .x[is.na(sel) | !sel] } map_if <- function(.x, .p, .f, ...) { matches <- .rlang_purrr_probe(.x, .p) .x[matches] <- map(.x[matches], .f, ...) .x } .rlang_purrr_probe <- function(.x, .p, ...) { if (is_logical(.p)) { stopifnot(length(.p) == length(.x)) .p } else { .p <- as_function(.p, env = global_env()) map_lgl(.x, .p, ...) } } compact <- function(.x) { .x[as.logical(lengths(.x))] } transpose <- function(.l) { if (!length(.l)) { return(.l) } inner_names <- names(.l[[1]]) if (is.null(inner_names)) { fields <- seq_along(.l[[1]]) } else { fields <- set_names(inner_names) .l <- map(.l, function(x) { if (is.null(names(x))) { set_names(x, inner_names) } else { x } }) } # This way missing fields are subsetted as `NULL` instead of causing # an error .l <- map(.l, as.list) map(fields, function(i) { map(.l, .subset2, i) }) } every <- function(.x, .p, ...) { .p <- as_function(.p, env = global_env()) for (i in seq_along(.x)) { if (!rlang::is_true(.p(.x[[i]], ...))) return(FALSE) } TRUE } some <- function(.x, .p, ...) { .p <- as_function(.p, env = global_env()) for (i in seq_along(.x)) { if (rlang::is_true(.p(.x[[i]], ...))) return(TRUE) } FALSE } negate <- function(.p) { .p <- as_function(.p, env = global_env()) function(...) !.p(...) } reduce <- function(.x, .f, ..., .init) { f <- function(x, y) .f(x, y, ...) Reduce(f, .x, init = .init) } reduce_right <- function(.x, .f, ..., .init) { f <- function(x, y) .f(y, x, ...) Reduce(f, .x, init = .init, right = TRUE) } accumulate <- function(.x, .f, ..., .init) { f <- function(x, y) .f(x, y, ...) Reduce(f, .x, init = .init, accumulate = TRUE) } accumulate_right <- function(.x, .f, ..., .init) { f <- function(x, y) .f(y, x, ...) Reduce(f, .x, init = .init, right = TRUE, accumulate = TRUE) } detect <- function(.x, .f, ..., .right = FALSE, .p = is_true) { .p <- as_function(.p, env = global_env()) .f <- as_function(.f, env = global_env()) for (i in .rlang_purrr_index(.x, .right)) { if (.p(.f(.x[[i]], ...))) { return(.x[[i]]) } } NULL } detect_index <- function(.x, .f, ..., .right = FALSE, .p = is_true) { .p <- as_function(.p, env = global_env()) .f <- as_function(.f, env = global_env()) for (i in .rlang_purrr_index(.x, .right)) { if (.p(.f(.x[[i]], ...))) { return(i) } } 0L } .rlang_purrr_index <- function(x, right = FALSE) { idx <- seq_along(x) if (right) { idx <- rev(idx) } idx } list_c <- function(x) { inject(c(!!!x)) } # nocov end roxygen2/R/rd-include-rmd.R0000644000176200001440000000516315172224136015171 0ustar liggesusers#' @export roxy_tag_parse.roxy_tag_includeRmd <- function(x) { if (!is_installed("rmarkdown")) { warn_roxy_tag(x, "requires the rmarkdown package") return() } tag_two_part(x, "a path", "a section", required = FALSE, markdown = FALSE) } #' @export roxy_tag_rd.roxy_tag_includeRmd <- function(x, base_path, env) { rmd <- rel_rmd <- x$val$name section <- x$val$description if (!file.exists(rmd)) { warn_roxy_tag(x, "Can't find Rmd {.path {rmd}}") return(NULL) } if (section == "") { section <- "details" } check_string(rmd) rmd_path <- tempfile(fileext = ".Rmd") md_path <- tempfile(fileext = ".md") on.exit(unlink(c(rmd_path, md_path), recursive = TRUE), add = TRUE) wd <- getwd() setwd(base_path) on.exit(setwd(wd), add = TRUE) # This will create an absolute path rmd <- normalizePath(rmd) cache_path <- paste0(sub("\\.Rmd$", "", rmd), "_cache/") fig_path <- file.path(dirname(rmd), "figure/") linkrefs <- rmd_linkrefs_from_file(rmd) opts <- c( root.dir = dirname(rmd), cache.path = cache_path, fig.path = fig_path, child = rmd ) optss <- paste0(names(opts), "=", encodeString(opts, quote = '"')) txt <- sprintf( "```{r %s}\n```\n\n%s\n", paste(optss, collapse = ","), linkrefs ) cat(txt, file = rmd_path) local_reproducible_output() tryCatch( rmarkdown::render( rmd_path, output_format = "github_document", output_options = c( list(html_preview = FALSE), if (utils::packageVersion("rmarkdown") >= "2.12") { list(math_method = NULL) } ), output_file = md_path, quiet = TRUE, envir = new_environment(parent = global_env()) ), error = function(e) { warn_roxy_tag(x, "failed to evaluate {.path {rel_rmd}}", parent = e) } ) if (!file.exists(md_path)) { return(NULL) } tryCatch( value <- rmd_eval_rd(md_path, x), error = function(e) { warn_roxy_tag( x, "failed to process result of {.path {rel_rmd}}", parent = e ) } ) rd_section_markdown(section, value) } # Helpers ----------------------------------------------------------------- rmd_linkrefs_from_file <- function(path) { lines <- read_lines(path) txt <- paste(lines, collapse = "\n") paste(get_md_linkrefs(txt), collapse = "\n") } rmd_eval_rd <- function(path, tag) { mdtxt <- paste(read_lines(path), collapse = "\n") mdesc <- add_linkrefs_to_md(mdtxt) mdxml <- md_to_mdxml(mdesc) state <- new.env(parent = emptyenv()) state$tag <- tag state$has_sections <- TRUE rd <- mdxml_children_to_rd_top(mdxml, state = state) rd } roxygen2/R/block.R0000644000176200001440000001640315166171276013465 0ustar liggesusers#' Blocks #' #' @description #' A `roxy_block` represents a single roxygen2 block. #' #' The `block_*` functions provide a few helpers for common operations: #' * `block_has_tags(blocks, tags)`: does `block` contain any of these `tags`? #' * `block_get_tags(block, tags)`: get all instances of `tags` #' * `block_get_tag(block, tag)`: get single tag. Returns `NULL` if 0, #' throws warning if more than 1. #' * `block_get_tag_value(block, tag)`: gets `val` field from single tag. #' #' @param tags A list of [roxy_tag]s. #' @param file,line Location of the `call` (i.e. the line after the last #' line of the block). #' @param call Expression associated with block. #' @param object Optionally, the object associated with the block, found #' by inspecting/evaluating `call`. #' @param block A `roxy_block` to manipulate. #' @param tag A single tag name. #' @export #' @family extending #' @examples #' # The easiest way to see the structure of a roxy_block is to create one #' # using parse_text: #' text <- " #' #' This is a title #' #' #' #' @param x,y A number #' #' @export #' f <- function(x, y) x + y #' " #' #' # parse_text() returns a list of blocks, so I extract the first #' block <- parse_text(text)[[1]] #' block roxy_block <- function(tags, file, line, call, object = NULL) { stopifnot(is.list(tags)) check_string(file) check_number_whole(line) structure( list( tags = tags, file = file, line = line, call = call, object = object ), class = "roxy_block" ) } is_roxy_block <- function(x) inherits(x, "roxy_block") #' @export print.roxy_block <- function(x, ...) { call <- deparse(x$call, nlines = 2) if (length(call) == 2) { call <- paste0(call[[1]], " ...") } obj <- format(x$object) cat_line(" [", basename(x$file), ":", x$line, "]") cat_line(" $tag") cat_line(" ", map_chr(x$tags, format, file = x$file)) cat_line(" $call ", call) cat_line(" $object ", obj[[1]]) cat_line(" ", obj[-1]) } block_create <- function(call, srcref, tokens = c()) { if (is_empty(tokens)) { return(NULL) } tags <- parse_tags(tokens) if (length(tags) == 0) { return(NULL) } roxy_block( tags = tags, file = attr(srcref, "srcfile")$filename, line = srcref[[1]], call = call ) } block_set_env <- function(block, env) { block <- block_evaluate(block, env) block <- block_find_object(block, env) block } block_evaluate <- function(block, env) { tags <- block_get_tags(block, "eval") if (length(tags) == 0) { return(block) } # Evaluate results <- lapply(tags, roxy_tag_eval, env = env) results <- lapply(results, function(x) { if (is.null(x)) { character() } else { paste0("#' ", x) } }) # Tokenise and parse tokens <- lapply( results, tokenise_block, file = block$file, offset = block$line ) tags <- lapply(tokens, parse_tags) # Interpolate results back into original locations block_replace_tags(block, "eval", tags) } block_find_object <- function(block, env) { stopifnot(is_roxy_block(block)) object <- object_from_call( call = block$call, env = env, block = block, file = block$file ) block$object <- object class(block) <- unique(c( paste0("roxy_block_", class(object)), class(block) )) # Add in defaults generated from the object defaults <- object_defaults(object, block) defaults <- c( defaults, list(roxy_generated_tag(block, "backref", block$file)) ) default_tags <- map_chr(defaults, \(x) x[["tag"]]) defaults <- defaults[!default_tags %in% block_tags(block)] block$tags <- c(block$tags, defaults) block } # block accessors --------------------------------------------------------- block_tags <- function(block) { map_chr(block$tags, \(x) x[["tag"]]) } #' @export #' @rdname roxy_block block_has_tags <- function(block, tags) { any(block_tags(block) %in% tags) } #' @export #' @rdname roxy_block block_get_tags <- function(block, tags) { block$tags[block_tags(block) %in% tags] } #' @export #' @rdname roxy_block block_get_tag <- function(block, tag) { matches <- which(block_tags(block) %in% tag) n <- length(matches) if (n == 0) { NULL } else if (n == 1) { block$tags[[matches]] } else { warn_roxy_block(block, "Block must contain only one @{tag}") block$tags[[matches[[1]]]] } } #' @export #' @rdname roxy_block block_get_tag_value <- function(block, tag) { block_get_tag(block, tag)$val } block_replace_tags <- function(block, tags, values) { indx <- which(block_tags(block) %in% tags) stopifnot(length(indx) == length(values)) tags <- lapply(block$tags, list) tags[indx] <- values block$tags <- compact(unlist(tags, recursive = FALSE)) block } # parsing ----------------------------------------------------------------- parse_tags <- function(tokens) { # Set up evaluation environment for markdown pkgenv <- roxy_meta_get("env") %||% baseenv() evalenv <- new.env(parent = pkgenv) local_roxy_meta_set("evalenv", evalenv) markdown_activate(tokens) tokens <- parse_description(tokens) tokens <- map(tokens, roxy_tag_parse) compact(tokens) } #' @export roxy_tag_parse.roxy_tag_eval <- function(x) { tag_code(x) } #' @export roxy_tag_parse.roxy_tag_include <- function(x) { # Used to silence warning about undefined tag # Actual implementation in find_includes() NULL } parse_description <- function(tags) { if (length(tags) == 0) { return(tags) } tag_names <- map_chr(tags, \(x) x[["tag"]]) if (tag_names[1] != "") { return(tags) } intro <- tags[[1]] intro$val <- trimws(intro$raw) if (intro$val == "") { return(tags[-1]) } tags <- tags[-1] tag_names <- tag_names[-1] paragraphs <- strsplit(intro$val, '\n\n', fixed = TRUE)[[1]] lines <- re_count(paragraphs, "\n") + rep(2, length(paragraphs)) offsets <- c(0, cumsum(lines)) # 1st paragraph = title (unless has @title) if ("title" %in% tag_names) { title <- NULL } else if (length(paragraphs) > 0) { title <- roxy_tag( "title", paragraphs[1], NULL, intro$file, intro$line + offsets[[1]] ) paragraphs <- paragraphs[-1] offsets <- offsets[-1] } else { title <- roxy_tag("title", "", NULL, intro$file, intro$line) } # 2nd paragraph = description (unless has @description) if ("description" %in% tag_names || length(paragraphs) == 0) { description <- NULL } else if (length(paragraphs) > 0) { description <- roxy_tag( "description", paragraphs[1], NULL, intro$file, intro$line + offsets[[1]] ) paragraphs <- paragraphs[-1] offsets <- offsets[-1] } # Every thing else = details, combined with @details if (length(paragraphs) > 0) { details_para <- paste(paragraphs, collapse = "\n\n") # Find explicit @details tags didx <- which(tag_names == "details") if (length(didx) > 0) { explicit_details <- map_chr(tags[didx], \(x) x[["raw"]]) tags <- tags[-didx] details_para <- paste( c(details_para, explicit_details), collapse = "\n\n" ) } details <- roxy_tag( "details", details_para, NULL, intro$file, intro$line + offsets[[1]] ) } else { details <- NULL } c(compact(list(title, description, details)), tags) } roxygen2/R/rd-raw.R0000644000176200001440000000170515154610530013552 0ustar liggesusers#' @export roxy_tag_parse.roxy_tag_evalRd <- function(x) tag_code(x) #' @export roxy_tag_rd.roxy_tag_evalRd <- function(x, base_path, env) { rd_section("rawRd", roxy_tag_eval(x, env)) } #' @export roxy_tag_parse.roxy_tag_rawRd <- function(x) tag_value(x, multiline = TRUE) #' @export roxy_tag_rd.roxy_tag_rawRd <- function(x, base_path, env) { rd_section(x$tag, x$val) } #' @export format.rd_section_rawRd <- function(x, ...) { paste(x$value, collapse = "\n") } roxy_tag_eval <- function(tag, env = new.env(parent = baseenv())) { tryCatch( { out <- roxy_eval(tag$val, env) if (!is.character(out)) { warn_roxy_tag(tag, "must evaluate to a character vector") NULL } else if (anyNA(out)) { warn_roxy_tag(tag, "must not contain any missing values") NULL } else { out } }, error = function(e) { warn_roxy_tag(tag, "failed to evaluate", parent = e) NULL } ) } roxygen2/R/rd-params.R0000644000176200001440000000453015172410643014246 0ustar liggesusers#' @export roxy_tag_parse.roxy_tag_param <- function(x) { tag_two_part(x, "an argument name", "a description", multiline = TRUE) } #' @export roxy_tag_rd.roxy_tag_param <- function(x, base_path, env) { value <- setNames(x$val$description, x$val$name) rd_section(x$tag, value) } #' @export merge.rd_section_param <- function(x, y, ...) { stopifnot(identical(class(x), class(y))) # When parameters appear in both x and y, keep values from y # This happens for example when inherit_dot_params adds a "..." param after # inherit_params has done the same. to_add <- setdiff(names(x$value), names(y$value)) rd_section(x$type, c(x$value[to_add], y$value)) } #' @export format.rd_section_param <- function(x, ...) { names <- names(x$value) # add space to multiple arguments so they can wrap names <- gsub(",", ", ", names) items <- paste0("\\item{", names, "}{", x$value, "}", collapse = "\n\n") rd_macro("arguments", items, space = TRUE) } # Other helpers ----------------------------------------------------------- # Postprocessing to reset ordering of parameter documentation topics_fix_params_order <- function(topics) { for (topic in topics$topics) { # Compute correct ordering of parameter documentation # Check what's needed... needed <- topic$get_value("formals") # (Workaround for dupes that can occur but perhaps shouldn't, # cf. https://github.com/r-lib/roxygen2/commit/83d125dce50a072534988787d49ffe206d19b232#commitcomment-6742169) needed <- unique(needed) # ...and what's documented (here we look only at the first parameter # in a multi-parameter documentation) documented <- get_documented_params(topic, only_first = TRUE) # We operate on indexes to make sure that no documentation is lost during # the reordering documented_indexes <- seq_along(documented) # We compute the indexes in the current documentation in the required order # and append everything that's missing in the order found required_order <- match(needed, documented) required_order <- required_order[!is.na(required_order)] required_order <- c( required_order, setdiff(documented_indexes, required_order) ) # Overwrite all param fields to fix order param <- topic$get_value("param")[required_order] topic$add(rd_section("param", param), overwrite = TRUE) } invisible() } roxygen2/R/tokenize.R0000644000176200001440000000275314527127222014216 0ustar liggesusers# Returns list of roxy_blocks tokenize_file <- function(path, srcref_path = NULL) { lines <- read_lines(path) calls <- parse_lines(lines, srcref_path %||% path) srcrefs <- utils::getSrcref(calls) comment_refs <- comments(srcrefs) tokens <- lapply(comment_refs, tokenise_ref) blocks <- map(seq_along(tokens), function(i) { block_create( call = calls[[i]], srcref = srcrefs[[i]], tokens = tokens[[i]] ) }) compact(blocks) } parse_lines <- function(lines, path) { parse( text = lines, keep.source = TRUE, srcfile = srcfilecopy(path, lines, isFile = TRUE) ) } tokenise_ref <- function(x) { tokenise_block( as.character(x), file = attr(x, "srcfile")$filename, offset = x[[1]] ) } # For each src ref, find the comment block preceding it comments <- function(refs) { if (length(refs) == 0) { return(list()) } srcfile <- attr(refs[[1]], "srcfile") # first_line, first_byte, last_line, last_byte com <- vector("list", length(refs)) for (i in seq_along(refs)) { # Comments begin after last line of last block, and this block is included # so that it can be parsed for additional comments if (i == 1) { first_byte <- 1 first_line <- 1 } else { first_byte <- refs[[i - 1]][4] + 1 first_line <- refs[[i - 1]][3] } last_line <- refs[[i]][3] last_byte <- refs[[i]][4] lloc <- c(first_line, first_byte, last_line, last_byte) com[[i]] <- srcref(srcfile, lloc) } com } roxygen2/R/vignette.R0000644000176200001440000000455515151411610014204 0ustar liggesusers#' Re-build outdated vignettes #' #' @description #' This roclet rebuilds outdated vignettes with [tools::buildVignette], #' but we no longer recommend it because we no longer recommend storing #' built vignettes in a package. #' #' By default, it will rebuild all vignettes if the source file is newer than #' the output pdf or html. (This means it will automatically re-build the #' vignette if you change the vignette source, but _not_ when you #' change the R code). If you want finer control, add a Makefile to #' `vignettes/` and roxygen2 will use that instead. #' #' To prevent RStudio from re-building the vignettes again when checking #' your package, add `--no-build-vignettes` to the "Build Source Package" #' field in your project options. #' #' @keywords internal #' @export vignette_roclet <- function() { roclet("vignette") } #' @export roclet_process.roclet_vignette <- function(x, blocks, env, base_path) {} #' @export roclet_output.roclet_vignette <- function(x, results, base_path, ...) { vign_update_all(base_path) } # Determine if a vignette is out-of-date; i.e. it has no related files, or # any of the related files are older than the vignette. vign_outdated <- function(vign) { vign <- normalizePath(vign, mustWork = TRUE) name <- tools::file_path_sans_ext(basename(vign)) # Currently, the final product of a vignette can only be pdf or html related <- dir( dirname(vign), pattern = paste0(name, "\\.(pdf|html)$"), full.names = TRUE ) related <- setdiff(related, vign) length(related) == 0 || mtime(vign) > mtime(related) } vign_update <- function(vign) { if (!vign_outdated(vign)) { return(FALSE) } cli::cli_inform("Rebuilding {.file {basename(vign)}}") output <- tools::buildVignette( vign, dirname(vign), tangle = FALSE, clean = FALSE ) TRUE } vign_update_all <- function(pkg_path) { vig_path <- file.path(pkg_path, "vignettes") if (!file.exists(vig_path)) { return() } if (file.exists(file.path(vig_path, "Makefile"))) { cli::cli_inform("Updating vignettes with make") make <- Sys.getenv("MAKE", "make") old <- setwd(vig_path) on.exit(setwd(old), add = TRUE) system(make) } else { cli::cli_inform("Updating vignettes") vigs <- tools::pkgVignettes(dir = pkg_path) invisible(map_lgl(vigs$docs, vign_update)) } } mtime <- function(x) { max(file.info(x)$mtime) } roxygen2/vignettes/0000755000176200001440000000000015174650112014041 5ustar liggesusersroxygen2/vignettes/rd-S7.Rmd0000644000176200001440000001114415172221402015374 0ustar liggesusers--- title: "Documenting S7" description: > How to document S7 generics, methods, and classes. output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Documenting S7} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r} #| include: false knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ``` There are three things that you might document for [S7](https://rconsortium.github.io/S7/): - **Generics**: mention that the function is a generic and list the available methods. - **Methods**: link back to the generic; only document individually when the method has unique behavior or arguments. - **Classes**: document the constructor. ## Generics S7 **generics** are functions, so document them as such. Export a generic if you want users to call it or other developers to write methods for it. If the generic is internal, you don't need to document it. The documentation should mention that the function is a generic, because this tells the reader that the behavior may vary depending on the input and that they can write their own methods. For simple generics, you can do this in the description: ```{r} #| eval: false #' Size of an object #' #' @description #' `size()` is an S7 generic that determines the size of an object, #' with methods available for the following classes: #' #' `r doclisting::methods_list("size")` #' #' @param x An object. #' @param ... Not used. #' @returns A single number. #' @export size <- new_generic("size", "x") ``` For more complicated generics, you can use a `# Methods` section to provide more detail: ```{r} #| eval: false #' Size of an object #' #' @description #' `size()` determines the size of an object. #' #' # Methods #' `size()` is an S7 generic with methods available for the following #' classes: #' #' `r doclisting::methods_list("size")` #' #' @param x An object. #' @param ... Not used. #' @returns A single number. #' @export size <- new_generic("size", "x") ``` See the [S3 Generics section](rd-S3.html#generics) for more about using the [doclisting](https://doclisting.r-lib.org/) package to automatically generate method lists. It's good practice to document the default method alongside the generic using `@rdname`: ```{r} #| eval: false #' @rdname size method(size, class_any) <- function(x, ...) { length(x) } ``` ## Classes S7 **classes** are constructor functions, so document them much like you'd document any other function. Export a class if you want users to create instances or other developers to extend it (e.g. by creating subclasses). Internal classes don't need documentation. Use `@param` to document the constructor arguments (which correspond to class properties), and `@returns` to describe the object that is returned. If the class has additional properties that are not part of the constructor (e.g. read-only computed properties), use `@prop` to document them. ```{r} #| eval: false #' A range #' #' Create a range represented by a numeric `start` and `end`. The start must #' always be less than the end. #' #' @param start Start of range. #' @param end End of range. #' @prop length Length of the range (read-only). #' @returns An `Range` S7 object. #' @export Range <- new_class( "Range", properties = list( start = class_numeric, end = class_numeric, length = new_property(getter = function(self) self@end - self@start), validator = function(self) { if (self@start > self@end) { "start must be less than or equal to end" } } ) ) ``` If multiple classes share one Rd page (via `@rdname`), you can prefix the property name with the class name to group properties by class, e.g. `@prop ClassName@prop_name description`. ## Methods It is your choice whether or not to document S7 **methods**. S7 methods are registered with `method(generic, class) <- fn`. Generally, it's not necessary to document straightforward methods. It's good practice, however, to document methods that have unique behavior or arguments. If you do document a method, give it its own roxygen block. When documenting a method, always include a link back to the generic using `[generic_name()]` so the reader can easily find the full documentation and other methods. ```{r} #| eval: false #' Size of a range #' #' The size of a range is its [size()], i.e. its length. #' #' @param x A `Range` object. #' @param ... Not used. #' @returns A single number. method(size, Range) <- function(x, ...) { x@length } ``` S7 methods are registered at load time via `S7::methods_register()` in your `.onLoad()` function, not through `NAMESPACE` directives, so you never need to `@export` them. See `vignette("packages", package = "S7")` for details. roxygen2/vignettes/rd-datasets.Rmd0000644000176200001440000000465215172221402016721 0ustar liggesusers--- title: "Documenting datasets" description: > How to document datasets stored in `data/`. output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Documenting datasets} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r} #| include: false knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ``` Datasets are stored in `data/`, not as regular R objects in the package. This means you need to document them in a slightly different way: instead of documenting the data directly, you quote the dataset's name. For example, this is the roxygen2 block used for `ggplot2::diamonds`: ```{r} #| eval: false #' Prices of over 50,000 round cut diamonds #' #' A dataset containing the prices and other attributes of almost 54,000 #' diamonds. The variables are as follows: #' #' @format A data frame with 53940 rows and 10 variables: #' \describe{ #' \item{price}{price in US dollars ($326--$18,823)} #' \item{carat}{weight of the diamond (0.2--5.01)} #' \item{cut}{quality of the cut (Fair, Good, Very Good, Premium, Ideal)} #' \item{color}{diamond colour, from D (best) to J (worst)} #' \item{clarity}{a measurement of how clear the diamond is (I1 (worst), SI2, #' SI1, VS2, VS1, VVS2, VVS1, IF (best))} #' \item{x}{length in mm (0--10.74)} #' \item{y}{width in mm (0--58.9)} #' \item{z}{depth in mm (0--31.8)} #' \item{depth}{total depth percentage = z / mean(x, y) = 2 * z / (x + y) (43--79)} #' \item{table}{width of top of diamond relative to widest point (43--95)} #' } #' #' @source {ggplot2} tidyverse R package. "diamonds" ``` Datasets should never be exported with `@export` because they are not found in the `NAMESPACE`. Instead, datasets will either be automatically available if you set `LazyData: true` in your `DESCRIPTION`, or available after calling `data()` if not. This field also affects the default usage. If you have `LazyData: true`, the usage will be just the dataset name (e.g. `diamonds`). Otherwise, the usage will be wrapped in `data()` (e.g. `data(diamonds)`). Note the use of two additional tags that are particularly useful for documenting data: - `@format`, which gives an overview of the structure of the dataset. This should include a **definition list** that describes each variable. There's currently no way to generate this with Markdown, so this is one of the few places you'll need to Rd markup directly. - `@source` where you got the data form, often a URL. roxygen2/vignettes/rd-functions.Rmd0000644000176200001440000002336515172221402017123 0ustar liggesusers--- title: "Documenting functions" description: > The basics of roxygen2 tags and how to use them for documenting functions. output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Documenting functions} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r} #| include: false knitr::opts_chunk$set(comment = "#>", collapse = TRUE) ``` This vignette describes the most important roxygen2 tags for documenting functions. See `vignette("reuse")` for reusing documentation across topics. ## Exporting For a user to use a function in your package, you must **export** it with `@export`. If you export a function, you must also document it, and since other people will use it, you need to be careful if you later change the function interface. We usually put `@export` in between `@returns` and `@examples`: ```{r} #| eval: false #' Add two numbers together #' #' @param x,y A pair of numbers. #' @returns A number. #' @export #' @examples #' 1 + 2 add <- function(x, y) { x + y } ``` You'll learn what all these are pieces are next! ## The introduction Each documentation block starts with some text which defines the title, the description, and the details. Here's an example showing what the documentation for `sum()` might look like if it had been written with roxygen: ```{r} #' Sum of vector elements #' #' `sum` returns the sum of all the values present in its arguments. #' #' This is a generic function: methods can be defined for it directly #' or via the [Summary()] group generic. For this to work properly, #' the arguments `...` should be unnamed, and dispatch is on the #' first argument. sum <- function(..., na.rm = TRUE) {} ``` This introductory block is broken up as follows: - The first sentence is the **title**: that's what you see when you look at `help(package = mypackage)` and is shown at the top of each help file. It should generally fit on one line, be written in sentence case, and not end in a full stop. - The second paragraph is the **description**: this comes first in the documentation and should briefly describe what the function does. - The third and subsequent paragraphs go into the **details**: this is a (often long) section that comes after the argument description and should provide any other important details of how the function operates. The details are optional. ### Title The title appears in package indexes and search results, so think about how users will find your function. Use synonyms to describe what the function does. For example, dplyr uses titles like: - "Keep rows that match a condition" (`filter()`) - "Create, modify, and delete columns" (`mutate()`) - "Summarise each group down to one row" (`summarise()`) ### Description The description should briefly summarize the goal of the function, usually in one paragraph. This can be challenging for simple functions, because it can feel like you're just repeating the title of the function. Try to find a slightly different wording, if you can. It's okay if this feels a little repetitive; it's often useful for users to see the same thing expressed in two different ways. It's a little extra work, but the extra effort is often worth it. ```{r} #| eval: false #' Detect the presence/absence of a match #' #' `str_detect()` returns a logical vector with `TRUE` for each element of #' `string` that matches `pattern` and `FALSE` otherwise. It's equivalent to #' `grepl(pattern, string)`. ``` If you need a multi-paragraph description, use an explicit `@description` tag: ```{r} #| eval: false #' View strings and matches #' #' @description #' `str_view()` is used to print the underlying representation of a string and #' to see how a `pattern` matches. #' #' Matches are surrounded by `<>` and unusual whitespace (i.e. all whitespace #' apart from `" "` and `"\n"`) are surrounded by `{}` and escaped. ``` Basically, if you're going to include an empty line in your description, you'll need to use an explicit `@description` tag. ### Details The details section is optional and comes after the argument description in the rendered help. Use informative markdown headings to break up long details. ```{r} #' Sum of vector elements #' #' @description #' `sum` returns the sum of all the values present in its arguments. #' #' @details #' This is a generic function: methods can be defined for it directly #' or via the [Summary()] group generic. For this to work properly, #' the arguments `...` should be unnamed, and dispatch is on the #' first argument. ``` ## Required tags Functions are the most commonly documented objects. Functions require three tags: `@param`, `@returns`, and `@examples`. ### Inputs Use `@param name description` to describe each input to the function. The description should provide a succinct summary of the parameter type (e.g. a string, a numeric vector), and if not obvious from the name, what the parameter does. The description is a sentence so should start with a capital letter and end with a full stop. It can span multiple lines (or even paragraphs) if necessary. All parameters must be documented. ```{r} #| eval: false #' @param pattern Pattern to look for. #' #' The default interpretation is a regular expression, as described in #' `vignette("regular-expressions")`. Use [regex()] for finer control of the #' matching behaviour. #' #' @param string Input vector. Either a character vector, or something #' coercible to one. ``` It's a good idea to describe the default value in the `@param` description, particularly when the default is non-obvious or the signature and the rendered argument documentation are far apart. ```{r} #| eval: false #' @param na.rm Remove missing values? If `FALSE` (the default), the result #' will be `NA` if any element of `string` is `NA`. ``` For arguments with a small fixed set of possible values, list them in the description: ```{r} #| eval: false #' @param side Side on which to remove whitespace: `"left"`, `"right"`, or #' `"both"` (the default). ``` If each value needs more explanation, use a bulleted list: ```{r} #| eval: false #' @param whitespace_only A boolean. #' * `TRUE` (the default): wrapping will only occur at whitespace. #' * `FALSE`: can break on any non-word character (e.g. `/`, `-`). ``` If two or more arguments are tightly coupled, you can document them in one place by separating the names with commas (no spaces). For example, to document both `x` and `y`, you can say `@param x,y Numeric vectors`. ### Outputs `@returns description` describes the output from the function. Briefly describe the type/shape of the output, not the details. All functions must have a documented return value for initial CRAN submission. For simple cases, a single sentence suffices: ```{r} #| eval: false #' @returns A logical vector the same length as `string`. ``` For functions returning data frames, describe how the output relates to the input: ```{r} #| eval: false #' @returns #' An object of the same type as `.data`. The output has the following #' properties: #' #' * Rows are a subset of the input, but appear in the same order. #' * Columns are not modified. #' * The number of groups may be reduced (if `.preserve` is not `TRUE`). #' * Data frame attributes are preserved. ``` ### Examples `@examples` provides executable R code showing how to use the function in practice. This is a very important part of the documentation because many people look at the examples before reading anything. Example code must work without errors as it is run automatically as part of `R CMD check`. Focus on demonstrating the most important features with realistic, typical usage. Use comments to break examples into sections: ```{r} #| eval: false #' @examples #' fruit <- c("apple", "banana", "pear", "pineapple") #' str_detect(fruit, "a") #' str_detect(fruit, "^a") #' str_detect(fruit, "a$") #' #' # Also vectorised over pattern #' str_detect("aecfg", letters) ``` Examples must be self-contained and not change the user's state: if you modify `options()`, reset them; create files in `tempdir()` and delete them; don't change the working directory or write to the clipboard. For the purpose of illustration, it's often useful to include code that causes an error. `try()` is the best way to do this, because users will see the error message: ```{r} #| eval: false #' @examples #' # Row sizes must be compatible when column-binding #' try(bind_cols(tibble(x = 1:3), tibble(y = 1:2))) ``` You can also use `\dontrun{}` to prevent code from executing, but `try()` is generally preferred. For code that only works in certain environments, use `@examplesIf`: ```r #' @examplesIf interactive() #' browseURL("https://roxygen2.r-lib.org") ``` This generates ``` \examples{ \dontshow{if (interactive() (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} gh_organizations(since = 42) \dontshow{\}) # examplesIf} } ``` This way, the code evaluating whether the example can be run is not shown to users reading the help, but it still prevents R CMD check failures. `@examplesIf` is preferred over wrapping code in `if()` because users don't see the machinery, and unlike `\dontrun{}`, the code still runs when the condition is met. Instead of including examples directly in the documentation, you can put them in separate files and use `@example path/relative/to/package/root` to insert them into the documentation. All functions must have examples for initial CRAN submission. ### Usage In most cases, the function usage (which appears beneath the description in the generated docs) will be automatically derived from the function specification. For the cases where it is not, please [file an issue](https://github.com/r-lib/roxygen2/issues) and use `@usage` to override the default with what you want. If you want to suppress the usage altogether (which is sometimes useful for internal or deprecated functions), you can use `@usage NULL`. roxygen2/vignettes/rd-S4.Rmd0000644000176200001440000000402515172221402015371 0ustar liggesusers--- title: "Documenting S4" description: > How to document S4 generics, methods, and classes. output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Documenting S4} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r} #| include: false knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ``` There are three things that you might document for [S4](https://adv-r.hadley.nz/s4.html): - **Generics**: document as functions; mention that they are generics. - **Methods**: unlike S3/S7, all S4 methods must be documented. - **Classes**: document the class definition and use `@slot` for slots. ## Generics S4 **generics** are functions, so document them as such. `@export` a generic if you want users to call it or other developers to write methods for it. If the generic is internal, you don't need to export or document it. ## Classes Document **S4 classes** by adding a roxygen block before `setClass()`. `@export` a class if you want users to create instances or other developers to extend it (e.g. by creating subclasses). Internal classes don't need documentation. Use `@slot` to document the slots of the class. Here's a simple example: ```{r} #| eval: false #' An S4 class to represent a bank account #' #' @slot balance A length-one numeric vector #' @export Account <- setClass("Account", slots = list(balance = "numeric")) ``` ## Methods S4 **methods** are a little more complicated. Unlike S3 and S7 methods, all S4 methods must be documented. You only need to `@export` a method if the generic lives in another package. You can document methods in three places: - In the class. Most appropriate if the corresponding generic uses single dispatch and you created the class. - In the generic. Most appropriate if the generic uses multiple dispatches and you control it. - In its own file. Most appropriate if the method is complex or the either two options don't apply. Use either `@rdname` or `@describeIn` to control where method documentation goes. See `vignette("reuse")` for more details. roxygen2/vignettes/namespace.Rmd0000644000176200001440000001162515172221402016440 0ustar liggesusers--- title: "Managing imports and exports" description: > Generating the `NAMESPACE` file with roxygen2. output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Managing imports and exports} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r} #| include: false knitr::opts_chunk$set(comment = "#>", collapse = TRUE) ``` The package `NAMESPACE` is one of the most confusing parts of building a package. roxygen2 aims to make it as easy as possible to build a package that is a well-behaved member of the R ecosystem. This is a little frustrating at first, but soon becomes second-nature. ## Exports In order for your users to use a function[^1] from your package, you must **export** it. In most cases, you can just use the `@export` tag, and roxygen2 will automatically figure out which `NAMESPACE` directive (i.e. `export()`, `S3method()`, `exportClasses()`, or `exportMethods()`) you need. [^1]: Including S3/S4/S7 generics and constructors. For details and examples of exporting functions, and S3, S4, and S7 classes/generics/methods, see the corresponding vignettes: - `vignette("rd-functions")` for regular functions. - `vignette("rd-S3")` for S3 generics and methods, including delayed registration with `@exportS3Method`. - `vignette("rd-S4")` for S4 generics, classes, and methods. - S7 methods are registered at load time, not through `NAMESPACE`; see `vignette("rd-S7")`. Datasets should never be exported as they are not found in `NAMESPACE`. Learn more in `vignette("rd-datasets")`. ### Manual exports If `@export` does not automatically generate the correct `NAMESPACE` directive, you can use one of the tags below to exercise greater control: - `@export foo` generates `export(foo)` - `@exportS3Method generic method` generates `S3method(generic, method)` - `@exportClass foo` generates `exportClasses(foo)` - `@exportMethod foo` generates `exportMethods(foo)` - `@exportPattern foo` generates `exportPattern(foo)` For even more specialised cases you can use `@rawNamespace code` which inserts `code` literally into the `NAMESPACE`. This is useful if you need a conditional import or export, e.g. ```{r} # From dplyr: #' @rawNamespace import(vctrs, except = data_frame) NULL # From backports: #' @rawNamespace if (getRversion() < "4.0.0") export(stopifnot) NULL ``` If you need to automate this, `@evalNamespace fun()` will evaluate `fun()` in your package environment and insert the results into `NAMESPACE`. Note that because `evalNamespace()` is run in the package environment, it can only generate exports, not imports. ## Imports The `NAMESPACE` also controls which functions from other packages are made available to your package. ### Functions If you are using just a few functions from another package, we recommending adding the package to the `Imports:` field of the `DESCRIPTION` file and calling the functions explicitly using `::`, e.g., `pkg::fun()`. ```r my_function <- function(x, y) { pkg::fun(x) * y } ``` If the repetition of the package name becomes annoying you can `@importFrom` and drop the `::`: ```r #' @importFrom pkg fun my_function <- function(x, y) { fun(x) * y } ``` Imports affect every function in a package, so it's common to collect them in a central place, like `{packagename}-package.R`. This is automated by `usethis::use_import_from()`. ```{r} #' @importFrom pkg fun1 fun2 #' @importFrom pkg2 fun3 #' @importFrom pkg3 fun4 NULL ``` Note the use of `NULL` here: you must provide something for roxygen2 to document, so we use `NULL` as placeholder. It is possible, but not generally recommended to import all functions from a package with `@import package`. This is risky if you import functions from more than one package, because while it might be ok today, in the future the packages might end up with a function having the same name, and your users will get a warning every time your package is loaded. ### S3 S3 generics are just functions, so the same rules for functions apply. S3 methods always accompany the generic, so as long as you can access the generic, the methods will also be available. In other words, you don't need to do anything to import an S3 method. ### S4 - To use classes defined in another package, place `@importClassesFrom package ClassA ClassB ...` next to the classes that inherit from the imported classes, or next to the methods that implement a generic for the imported classes. - To use generics defined in another package, place `@importMethodsFrom package GenericA GenericB ...` next to the methods that use the imported generics. ### Compiled code To import compiled code from another package, use `@useDynLib` - `@useDynLib package` imports all compiled functions. - `@useDynLib package routinea routineb` imports selected compiled functions. - Any `@useDynLib` specification containing a comma, e.g. `@useDynLib mypackage, .registration = TRUE` will be inserted as is into the the NAMESPACE, e.g. `useDynLib(mypackage, .registration = TRUE)` roxygen2/vignettes/extending.Rmd0000644000176200001440000003404315172221402016470 0ustar liggesusers--- title: "Extending roxygen2" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Extending roxygen2} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r} #| include: false knitr::opts_chunk$set(comment = "#>", collapse = TRUE) ``` roxygen2 is extensible, and this vignette will show you how. It starts with an introduction to the basic workflow of `roxygenize()` and the key data structures that power it. Then we'll show you how you can use its two extension points: - Add a new tag to generate a new top-level section in an `.Rd` file. This allows you to repeat yourself less when documenting your package. (See `vignette("reuse")` for other techniques.) - Add a new roclet. This lets you take full advantage of the computational machinery behind `roxygenize()` to compute anything you want or produce any artefact you can imagine. ```{r} #| label: setup library(roxygen2) ``` ## How `roxygenize()` works You've probably used `roxygenize()` (or `devtools::document()`) a bunch without ever really thinking about what's going on behind the scenes. But if you're going to extend roxygen2, you'll need to know exactly what's happening: - Loads the package under roxygenizing, as well as any further packages ("packages" in `load_options()`). - Parses all R files in the package, using available tags. - Finds the roclets (see `roclet()`) from its `roclets` argument or the "roclets" option (`load_options()`). It defaults to using the Collate "roclet", the Rd roclet, and NAMESPACE roclet, but you can also add your own. - Runs the different methods of all those roclets, in order and independently: clean (`roclet_clean`), preprocess (`roclet_preprocess()`), process (`roclet_process()`), and output (`roclet_output()`). Only process and output are routinely used. For example, if you think of the Rd roclet, its process method digests information from the tag parsing, combines inherits, etc. to create the content of each documentation topic, and its output method writes those topics to disk. ## Key data structures Before we dive into extending roxygen2, we need to first discuss two important data structures that power roxygen: tags and blocks. ### Tags A tag (a list with S3 class `roxy_tag`) represents a single tag. It has the following fields: - `tag`: the name of the tag. - `raw`: the raw contents of the tag (i.e. everything from the end of this tag to the beginning of the next). - `val`: the parsed value, which we'll come back to shortly. - `file` and `line`: the location of the tag in the package. Used with `roxy_tag_warning()` to produce informative error messages. You *can* construct tag objects by hand with `roxy_tag()`: ```{r} roxy_tag("name", "Hadley") str(roxy_tag("name", "Hadley")) ``` However, you should rarely need to do so (except in tests), because you'll typically have them given to you in a block object, as you'll see shortly. ### Blocks A block (a list with S3 class `roxy_block`) represents a single roxygen block. It has the following fields: - `tags`: a list of `roxy_tags`. - `call`: the R code associated with the block (usually a function call). - `file` and `line`: the location of the R code. - `object`: the evaluated R object associated with the code. The easiest way to see the basic structure of a `roxy_block()` is to generate one by parsing a roxygen block with `parse_text()`: ```{r} text <- " #' This is a title #' #' This is the description. #' #' @param x,y A number #' @export f <- function(x, y) x + y " # parse_text() returns a list of blocks, so I extract the first block <- parse_text(text)[[1]] block ``` You'll notice that some of the tags didn't exist in the original block: - `@title` and `@description` are extracted from the text that appears before the first explicit tag. - `@usage` is generated automatically from the function formals. - `@.formals` is an "internal" tag that doesn't generate any output but is used to pass some important data around. - `@backref` stores the source location of the block so we can later record which `.R` files contributed to each `.Rd` file. ## Adding a new `.Rd` tag The most common way to extend roxygen2 is to create a new tag that adds output to `.Rd` files. This requires defining a few methods: 1. Define a `roxy_tag_parse()` method that describes how to parse our new tag. 2. Define a `roxy_tag_rd()` method that describes how to convert the tag into `.Rd` commands. 3. If the tag's content is meant to appear in a custom section (as opposed to, say, the examples section), define a `format()` method that describes how to create the `.Rd` string. To illustrate the basic idea, we'll create a new `@tip` tag that will create a bulleted list of tips about how to use a function. The idea is to take something like this: ```{r} #' @tip The mean of a logical vector is the proportion of `TRUE` values. #' @tip You can compute means of dates and date-times! ``` That generates Rd like this: ```latex \section{Tips and tricks}{ \itemize{ \item The mean of a logical vector is the proportion of \code{TRUE} values. \item You can compute means of dates and date-times! } } ``` The first step is to define a method for `roxy_tag_parse()` that describes how to parse the tag text. The name of the class will be `roxy_tag_{tag}`, which in this case is `roxy_tag_tip`. This function takes a `roxy_tag` as input, and its job is to set `x$val` to a convenient parsed value that will be used later by the roclet. Here we want to process the text using Markdown so we can just use `tag_markdown()`: ```{r} roxy_tag_parse.roxy_tag_tip <- function(x) { tag_markdown(x) } ``` (There are lots of other built in options that you can read about in `?tag_markdown`.) ```{r} #| include: false # Needed for vignette registerS3method("roxy_tag_parse", "roxy_tag_tip", roxy_tag_parse.roxy_tag_tip) ``` We can check this works by using `parse_text()`: ```{r} text <- " #' Title #' #' @tip The mean of a logical vector is the proportion of `TRUE` values. #' @tip You can compute means of dates and date-times! #' @md f <- function(x, y) { # ... } " block <- parse_text(text)[[1]] block str(block$tags[[2]]) ``` Here I explicitly turn Markdown parsing on using `@md`; it's usually turned on for a package using roxygen2's `load_options()`. Next, we define a method for `roxy_tag_rd()`, which must create an `rd_section()`. We're going to create a new custom section called `tip`. It will contain a character vector of tips: ```{r} roxy_tag_rd.roxy_tag_tip <- function(x, base_path, env) { rd_section("tip", x$val) } ``` ```{r} #| include: false # Needed for vignette registerS3method("roxy_tag_rd", "roxy_tag_tip", roxy_tag_rd.roxy_tag_tip) ``` This additional layer is needed because there can be multiple tags of the same type in a single block, and multiple blocks can contribute to the same `.Rd` file. The job of `rd_section()` is to combine all the tags into a single top-level Rd section. Each tag generates an `rd_section` which is then combined with any previous section using `merge()`. The default `merge.rd_section()` just concatenates the values together (`rd_section(x$type, c(x$value, y$value))`); you can override this method if you need more sophisticated behaviour. We called the custom section "tip" just like our tag, but we needn't have done so: it really depends on how you want to map the input tags to output Rd code. We then need to define a `format()` method to convert this object into text for the `.Rd` file: ```{r} format.rd_section_tip <- function(x, ...) { paste0( "\\section{Tips and tricks}{\n", "\\itemize{\n", paste0(" \\item ", x$value, "\n", collapse = ""), "}\n", "}\n" ) } ``` ```{r} #| include: false # Needed for vignette registerS3method("format", "rd_section_tip", format.rd_section_tip) ``` We can now try this out with `roclet_text()`: ```{r} topic <- roc_proc_text(rd_roclet(), text)[[1]] topic$get_section("tip") ``` Note that there is no namespacing so if you're defining multiple new tags I recommend using your package name as the common prefix. ### Using your new tag Now that the three methods are created, we still need to make them available to `roxygenize()`. First, you need to export the method so that it's registered correctly. Since your methods will be for generics defined in roxygen2 (another package), you'll need to follow the advice in [Methods for generics in other packages](rd-S3.html#methods-for-generics-in-other-packages). Next, you'll need to load the tag-defining package in the package where you want to use it. For example, if you created some new tags in {packageFoo}, and would like to use these tags in your documentation for {packageBar}, append this line to the `DESCRIPTION` of {packageBar}: ``` Config/roxygen2/packages: packageFoo ``` See `load_options()` for more details. ## Creating a new roclet Creating a new roclet is usually a two part process. First, you define new tags that your roclet will work with, unless your roclet only needs information from existing tags, or only needs the path to the package source[^vignette]. Second, you define a roclet that tells `roxygenize()` what to compute and produce based on this information. [^vignette]: For example, the no-longer recommended `vignette_roclet()` only needs the path to the package source as input; it does not use information from the tag parsing step. Or the {roxylint} package only uses existing tags; its job is to warn you about suboptimal roxygen2 style. ### Custom tags In this example we will make a new `@memo` tag which helps you to remember what you're planning to work on in the future by displaying notes when you document your package. We choose this syntax for `@memo`: ```{r} #' @memo [Headline] Description ``` For example: ```{r} #' @memo [EFFICIENCY] Currently brute-force; find better algorithm. ``` As above, we first define a parse method. This time we use custom format based on a regular expression: ```{r} roxy_tag_parse.roxy_tag_memo <- function(x) { if (!grepl("^\\[.*\\].*$", x$raw)) { roxy_tag_warning(x, "Invalid memo format") return() } parsed <- regmatches(x$raw, regexec("\\[(.*)\\](.*)", x$raw))[[1]] x$val <- list( header = parsed[[2]], message = parsed[[3]] ) x } ``` ```{r} #| include: false # Needed for vignette registerS3method( "roxy_tag_parse", "roxy_tag_memo", roxy_tag_parse.roxy_tag_memo ) ``` Then we check it works with `parse_text()`: ```{r} text <- " #' @memo [TBI] Remember to implement this! #' @memo [API] Check best API f <- function(x, y) { # ... } " block <- parse_text(text)[[1]] block str(block$tags[[1]]) ``` We don't need a format method because our tag won't be used to produce Rd sections[^tag]. [^tag]: Some tags in roxygen2 itself, like `@importFrom`, are not meant for the [`rd_roclet()`] (`@importFrom` is meant for the `namespace_roclet()`). ### The roclet Next, we create a constructor for the roclet, which uses `roclet()`. Our `memo` roclet doesn't have any options so this is very simple: ```{r} memo_roclet <- function() { roclet("memo") } ``` To give the roclet behaviour, you need to define methods. There are two methods that almost every roclet will use: - `roclet_process()` is called with a list of blocks, and returns an object of your choosing. - `roclet_output()` produces side-effects (usually writing to disk) using the result from `roclet_process()`. For this roclet, we'll have `roclet_process()` collect all the memo tags into a named list: ```{r} roclet_process.roclet_memo <- function(x, blocks, env, base_path) { results <- list() for (block in blocks) { tags <- block_get_tags(block, "memo") for (tag in tags) { msg <- paste0("[", tag$file, ":", tag$line, "] ", tag$val$message) results[[tag$val$header]] <- c(results[[tag$val$header]], msg) } } results } ``` And then have `roclet_output()` print them to the screen: ```{r} roclet_output.roclet_memo <- function(x, results, base_path, ...) { for (header in names(results)) { messages <- results[[header]] cat(paste0(header, ": ", "\n")) cat(paste0(" * ", messages, "\n", collapse = "")) } invisible(NULL) } ``` ```{r} #| include: false # Needed for vignette registerS3method("roclet_process", "roclet_memo", roclet_process.roclet_memo) registerS3method("roclet_output", "roclet_memo", roclet_output.roclet_memo) ``` Then you can test if it works by using `roc_proc_text()`: ```{r} results <- roc_proc_text( memo_roclet(), " #' @memo [TBI] Remember to implement this! #' @memo [API] Check best API f <- function(x, y) { # ... } #' @memo [API] Consider passing z option g <- function(x, y) { # ... } " ) roclet_output(memo_roclet(), results) ``` ## Adding a roclet to your workflow To use a roclet when developing a package, call ```r roxygen2::roxygenize(roclets = "yourPackage::roclet") ``` where `yourPackage::roclet` is the function which creates the roclet, e.g. `memo_roclet` above. You can also add the roclet to the target package's DESCRIPTION file, like this: ```r Config/roxygen2/roclets: collate, rd, namespace, yourPackage::roclet ``` See `load_options()` for more details. Your package only needs to be installed when the user documents the package, which doesn't correspond precisely to any field in the `DESCRIPTION`. However, you can think of it as a development dependency and hence put it in the `Suggests:` field: ```r usethis::use_package("yourPackage", type = "Suggests") ``` You don't have to do this, but it will help other developers working on the target package. ## Conclusion: further extension ideas This vignette is quite rough, so you might want to also read some roxygen2 source code to understand all the extension points. Do not hesitate to also look for examples of roxygen2 extensions. For instance, the [roxygenlabs](https://github.com/gaborcsardi/roxygenlabs) package (former incubator of roxygen2 features) used a third extension point: it *extended* the Rd roclet with further methods, thus creating a supercharged Rd roclet rather than a brand-new roclet. Or, the [plumber2](https://github.com/posit-dev/plumber2) package only uses the parsing features from roxygen2, and does not use `roxygenize()` at all. roxygen2/vignettes/index-crossref.Rmd0000644000176200001440000000630415172221402017435 0ustar liggesusers--- title: "Indexing and cross-references" output: rmarkdown::html_vignette description: > Make it easier for users to find your functions by cross-referencing them and control their indexing. vignette: > %\VignetteIndexEntry{Indexing and cross-references} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r} #| include: false knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ``` This vignette discusses tags that help users finding documentation through cross-references and indexes. ## See also `@seealso` allows you to point to other useful resources, either on the web (using a url) or to related functions (with a function link like `[function_name()]`). For `sum()`, this might look like: ```{r} #' @seealso [prod()] for products, [cumsum()] for cumulative sums, and #' [colSums()]/[rowSums()] marginal sums over high-dimensional arrays. ``` ## Family If you have a family of related functions, you can use `@family {family}` to cross-reference each function to every member of the family. A function can be a member of multiple families. By default `@family {family}`, will generate the see also text "Other {family}:", so the `@family` name should be plural (i.e., "model building helpers" not "model building helper"). If you want to override the default title, you can provide an `rd_family_title` element in a list stored in `man/roxygen/meta.R`: ```{r} #| eval: false list( rd_family_title = list(aggregations = "Aggregation functions") ) ``` ## References If the object you're documenting has connections to the scientific literature, use `@reference` to provide a citation. ## Aliases `?` and `help()` look for topic aliases; `?foo` will find any topic that contains the `foo` alias. roxygen2 generates a default alias for you based on the object you're documenting. You can add additional aliases with `@aliases alias1 alias2 alias3` or remove default alias with `@aliases NULL`. ## Search As well as looking in the aliases, `help.search()` and `???` also look in the `@title`, `@keywords`, and `@concept`s tags. - `@keywords` adds standard keywords, which must be present in `file.path(R.home("doc"), "KEYWORDS")`. - `@concept` adds arbitrary key words or phrases. Each `@concept` should contain a single word or phrase. Generally speaking, `@keywords` and `@concepts` are not terribly useful because most people find documentation using Google, not R's built-in search. There's one exception: `@keywords internal`. It's useful because it removes the function from the documentation index; it's useful for functions aimed primarily at other developers, not typical users of the package. ## Back references The original source location is added as a comment to the second line of each generated `.Rd` file in the following form: ``` % Please edit documentation in ... ``` `roxygen2` tries to capture all locations from which the documentation is assembled. For code that *generates* R code with Roxygen comments (e.g., the Rcpp package), the `@backref` tag is provided. This allows specifying the "true" source of the documentation, and will substitute the default list of source files. Use one tag per source file: ```{r} #' @backref src/file.cpp #' @backref src/file.h ``` roxygen2/vignettes/reuse.Rmd0000644000176200001440000004156515172221402015635 0ustar liggesusers--- title: "Reusing documentation" description: > Tools for reusing documentation across topics, and between documentation and vignettes. output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Reusing documentation} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r} #| include: false knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ``` roxygen2 provides several ways to avoid repeating yourself in code documentation, while assembling information from multiple places in one documentation file: - Combine documentation for closely related functions into a single file with `@describeIn` or `@rdname`. - Automatically copy tags with `@inheritParams`, `@inheritSection`, or `@inherit`. - Use functions to generate repeated text with inline R code. - Share text between documentation and vignettes with child documents. The chapter concludes by showing you how to update superseded reuse mechanisms that we no longer recommend: `@includeRmd`, `@eval`/`@evalRd`, and `@template`. ## Multiple functions in the same topic You can document multiple functions in the same file by using either `@rdname` or `@describeIn` tag. It's a technique best used with care: documenting too many functions in one place leads to confusion. Use it when all functions have the same (or very similar) arguments. ### `@rdname` Use `@rdname `[^1] to include multiple functions in the same page. Tags (e.g. `@title`, `@description`, `@examples`) will be combined, across blocks but often this yields text that is hard to understand. So we recommend that you make one block that contains the title, description, common parameters, examples and so on, and then only document individual parameters in the other blocks. [^1]: The destination is a topic name. There's a one-to-one correspondence between topic names and `.Rd` files where a topic called `foo` will produce a file called `man/foo.Rd`. There are two basic ways to do that. You can create a standalone documentation block and then add the functions to it. This typically works best when you have a family of related functions and you want to link to that family from other functions (i.e. `trig` in the examples below). ```{r} #' Trigonometric approximations #' @param x Input, in radians. #' @name trig NULL #' @rdname trig #' @export sin_ish <- function(x) x - x^3 / 6 #' @rdname trig #' @export cos_ish <- function(x) 1 - x^2 / 2 #' @rdname trig #' @export tan_ish <- function(x) x + x^3 / 3 ``` Alternatively, you can add docs to an existing function. This tends to work better if you have a "primary" function with some variants or some helpers. ```{r} #| eval: false #' Logarithms #' #' @param x A numeric vector #' @export log <- function(x, base) ... #' @rdname log #' @export log2 <- function(x) log(x, 2) #' @rdname log #' @export ln <- function(x) log(x, exp(1)) ``` ### `@describeIn` An alternative to `@rdname` is `@describeIn`. It has a slightly different syntax because as well as a topic name, you also provide a brief description for the function, like `@describein topic one sentence description`. The primary difference between `@rdname` and `@describeIn`, is that `@describeIn` creates a new section containing a bulleted list of each function, along with its description. It uses a number of heuristics to determine the heading of this section, depending on if you're documenting related functions, methods for a generic, or methods for a class. In general, I no longer recommend `@describeIn` because I don't think the heuristics it uses are as good as a thoughtful hand-crafted summary. If you're currently using `@describeIn`, you can generally replace it with `@rdname`, as long as you give some thought to the multiple-function `@description`. ### Order of includes By default, roxygen blocks are processed in the order in which they appear in the file. When you're combining multiple files, this can sometimes cause the function usage to appear in a suboptimal order. You can override the default ordering with `@order`. For example, the following the block would place `times` first in `arith.Rd` because 1 comes before 2. ```{r} #' @rdname arith #' @order 2 add <- function(x, y) x + y #' @rdname arith #' @order 1 times <- function(x, y) x * y ``` ## Automatically copy tags If two or more functions share have similarities but are different or complex enough that you don't want to document them in a single file, you can use one of the four `@inherit` tags to automatically copy various components from another topic: - `@inheritParams foo` will copy `@param` contents from `foo`. - `@inherit foo` will copy all supported components from `foo`. - `@inheritSection foo {Section title}` will copy the `@section {Section title}` section from `foo`. - `@inheritDotParams foo` will generate documentation for `...` by copying the documentation for `foo()`'s arguments. We think of this as "inheritance" rather than just copying, because anything you inherit can be overridden by a more specific definition in the documentation. This applies particularly to `@inheritParams` which allows you to copy the documentation for some parameters while documenting others directly. We'll focus on this first. ### Parameters The oldest, and most frequently used, inherits tag is `@inheritParams`. It's particularly useful when multiple functions use the same argument name for the same task, as you can document the argument once, then inherit those docs elsewhere. For example, take the dplyr functions `arrange()`, `mutate()`, and `summarise()` which all have an argument called `.data`. `arrange()` is documented like so: ```{r} #' @param .data A data frame, data frame extension (e.g. a tibble), or a #' lazy data frame (e.g. from dbplyr or dtplyr). See *Methods*, below, for #' more details. #' @param ... <[`data-masking`][rlang::args_data_masking]> Variables, or #' functions of variables. Use [desc()] to sort a variable in descending #' order. arrange <- function(.data, ...) {} ``` Then `mutate()` and `summarise()` don't need to provide `@param .data` but can instead inherit the documentation from `arrange()`: ```{r} #' @inheritParams arrange mutate <- function(.data, ...) {} #' @inheritParams arrange summarise <- function(.data, ...) {} ``` If this was all you wrote it wouldn't be quite right because `mutate()` and `summarise()` would also inherit the documentation for `...`, which has a different interpretation in these functions. So, for example, `mutate()` provides its own definition for `...`: ```{r} #' @inheritParams arrange #' @param ... <[`data-masking`][rlang::args_data_masking]> Name-value pairs. #' The name gives the name of the column in the output. #' #' The value can be: #' #' * A vector of length 1, which will be recycled to the correct length. #' * A vector the same length as the current group (or the whole data frame #' if ungrouped). #' * `NULL`, to remove the column. #' * A data frame or tibble, to create multiple columns in the output. mutate <- function(.data, ...) {} ``` Note that only the documentation for arguments with the same names are inherited. For example, `arrange()` also has a `.by_group` argument. Since no other function in dplyr has an argument with this name, its documentation will never be inherited. ### Recursive inheritance `@inheritParams` (like all `@inherits` functions) works recursively, so if `g` inherits parameters from `h`, then `f` can also inherit those parameters from `g`. However, this technique is best used sparingly: it's very easy to create complex dependency webs that are hard to reason about where making changing the documentation in one function cascades out in unexpected ways across your package. You can avoid this problem by being more explicit about which parameters are inherited: - `@inheritParams foo x y` inherits only `x` and `y`. - `@inheritParams foo -z` inherits all parameters except `z`. - `@inheritParams foo first:third` inherits parameters `first` through `third` (in argument order). ### Multiple parameters Sometimes you document two (or more) tightly coupled parameters together. For example, `dplyr::left_join()` has: ```{r} #' @param x,y A pair of data frames, data frame extensions (e.g. a tibble), or #' lazy data frames (e.g. from dbplyr or dtplyr). See *Methods*, below, for #' more details. ``` When joint parameter documentation is inherited, it's all or nothing, i.e. if a function has `@inheritParams left_join` it will only inherit the documentation for `x` and `y` if it has both `x` and `y` arguments and neither is documented by the inheriting function. ### The dot prefix Many tidyverse functions that accept named arguments in `...` also use a `.` prefix for their own arguments. This reduces the risk of an argument going to the wrong place. For example, `dplyr::mutate()` has `.by`, `.keep`, `.before`, and `.after` arguments, because if they didn't have that prefix, you wouldn't be able to create new variables called `by`, `keep`, `before`, or `after`. We call this pattern the [dot prefix](https://design.tidyverse.org/dots-prefix.html). This means that an argument with the same meaning can come in one of two forms: with and without the `.`. `@inheritParams` knows about this common pattern so ignores a `.` prefix when matching argument name. In other words, `.x` will inherit documentation for `x`, and `x` will inherit documentation from `.x`. ### Inheriting other components You can use `@inherit foo` to inherit the documentation for every supported tag from another topic. Currently, `@inherit` supports inheriting the following tags: `r paste0("\x60", roxygen2:::inherit_components, "\x60", collapse = ", ")`. By supplying a space separated list of components after the function name, you can also choose to inherit only selected components. For example, `@inherit foo returns` would just inherit the `@returns` tag, and `@inherit foo seealso source` would inherit the `@seealso` and `@source` tags. `@inherit foo sections` will inherit *every* `@section` tag (which can also be specified in markdown by using the top-level heading spec, `#`). To inherit a *specific* section from another function, use `@inheritSection foo Section title`. For example, all the "adverbs" in purrr use `#' @inheritSection safely Adverbs` to inherit a standard section that provides advice on using an adverb in package code. ### Documenting `...` When your function passes `...` on to another function, it can be useful to inline the documentation for some of the most common arguments. This technique is inspired by the documentation for `plot()`, where `...` can take any graphical parameter; `?plot` describes some of the most common arguments so that you don't have to look them up in `?par`. `@inheritDotParams` has two components: the function to inherit from and the arguments to inherit. Since you'll typically only want to document the most important arguments, `@inheritDotParams` comes with a flexible specification for argument selection inspired by `dplyr::select()`: - `@inheritDotParams foo` takes all parameters from `foo()`. - `@inheritDotParams foo a b e:h` takes parameters `a`, `b`, and all parameters between `e` and `h`. - `@inheritDotParams foo -x -y` takes all parameters except for `x` and `y`. ### Inheriting from other packages It's most common to inherit from other documentation topics within the current package, but roxygen2 also supports inheriting documentation from other packages by using `package::function` syntax, e.g.: - `@inheritParams package::function` - `@inherit package::function` - `@inheritSection package::function Section title` - `@inheritDotParams package::function` When inheriting documentation from another package bear in mind that you're now taking a fairly strong dependency on an external package, and to ensure every developer produces the same documentation you'll need to make sure that they all have the same version of the package installed. And if the package changes the name of the topic or section, your documentation will require an update. For those reasons, this technique is best used sparingly. ## Custom tags Creating a custom tag might be a strategy for repeating yourself less. For instance you could create a tag that allows ```r #' @git rebase ``` to create this Rd section: ```latex \section{Related Git documentation} {\href{https://git-scm.com/docs/git-rebase#_interactive_mode}{\code{git rebase -i}}.} ``` See `vignette("extending")` for more details. ## Inline code To insert code inline, enclose it in `` `r ` ``. Roxygen will interpret the rest of the text within backticks as R code and evaluate it, and replace the backtick expression with its value. Here's a simple example: ```{r} #| include: false roxygen2:::markdown_on() simple_inline <- "#' Title `r 1 + 1` #' #' Description `r 2 + 2` foo <- function() NULL " ``` ```{r, code=simple_inline} ``` This is equivalent to writing: ```{r, code=roxygen2:::markdown(simple_inline)} ``` The resulting text, together with the whole tag is interpreted as markdown, as usual. This means that you can use R to dynamically write markdown. For example if you defined this function in your package: ```{r} alphabet <- function(n) { paste0("`", letters[1:n], "`", collapse = ", ") } ``` You could then write: ```{r} #| echo: false env <- new.env() env$alphabet <- alphabet roxygen2:::roxy_meta_set("evalenv", env) backtick <- "#' Title #' #' @param x A string. Must be one of `r alphabet(5)` foo <- function(x) NULL " ``` ```{r, code=backtick} ``` The result is equivalent to writing the following by hand: ```{r, code=roxygen2:::markdown_evaluate(backtick)} ``` This is a powerful technique for reducing duplication because you can flexibly parameterise the function however best meets your needs. Note that the evaluation environment is deliberately a child of the package that you're documenting so you can call internal functions. ## Inline Rd code You can use `` `Rd ` `` to run code that is evaluated when a user views the help page, rather than when roxygen2 builds the documentation. This is useful for documentation that needs to be dynamic, reflecting the current state of R, of the user's library and system (e.g. what dependencies are installed), not the state when the package was built. It works by using the `\Sexpr` tag, so that `` `Rd myFun()` `` generates`\Sexpr[stage=render,results=rd]{myFun()}`. Here's a simple example, which will greet the user appropriately, depending on the time of day: ```{r} #| eval: false greeting <- function() { hour <- as.POSIXlt(Sys.time(), tz = "UTC")$hour if (hour < 12) { "Good morning!" } else if (hour < 18) { "Good afternoon!" } else { "Good evening!" } } #' Title #' #' `Rd roxygen2:::greeting()` foo <- function() NULL ``` Note that compared to inline R code, the function is run in the global environment by R's documentation system, not inside the package namespace by roxygen2. This generally means that you'll want to use `:::` to explicitly call an internal function. ## Child documents You can use the same `.Rmd` or `.md` document in the documentation, `README.Rmd`, and vignettes by using child documents: ```` ```{r child = "common.Rmd"}`r ''` ``` ```` The included Rmd file can have roxygen Markdown-style links to other help topics. E.g. `[roxygen2::roxygenize()]` will link to the manual page of the `roxygenize` function in roxygen2. See `vignette("rd-formatting")` for details. If the Rmd file contains roxygen (Markdown-style) links to other help topics, then some care is needed, as those links will not work in Rmd files by default. A workaround is to specify external HTML links for them. These external locations will *not* be used for the manual which instead always links to the help topics in the manual. Example: ``` See also the [roxygen2::roxygenize()] function. [roxygen2::roxygenize()]: https://roxygen2.r-lib.org/reference/roxygenize.html ``` This example will link to the supplied URLs in HTML / Markdown files and it will link to the `roxygenize` help topic in the manual. Note that if you add external link targets like these, then roxygen will emit a warning about these link references being defined multiple times (once externally, and once to the help topic). This warning originates in Pandoc, and it is harmless. ## Superseded Over the years, we have experimented with a number of other ways to reduce duplication across documentation files. A number of these are now superseded and we recommend changing them to use the techniques described above: - Instead of `@includeRmd man/rmd/example.Rmd`, use a child document. - Instead of `@eval` or `@evalRd`, use inline R code. - Instead of `@template` and `@templateVars` write your own function and call it from inline R code. Inline R markdown can only generate markdown text within a tag so in principle it is less flexible than `@eval`/`@evalRd`/`@template`. However, our experience has revealed that generating multiple tags at once tends to be rather inflexible, and you often end up refactoring into smaller pieces so we don't believe this reflects a real loss of functionality. roxygen2/vignettes/rd-formatting.Rmd0000644000176200001440000002737415172221402017271 0ustar liggesusers--- title: "(R)Markdown support" description: > The details of the (R)Markdown support provided by roxygen2. output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{(R)Markdown support} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r} #| include: false knitr::opts_chunk$set(comment = "#>", collapse = TRUE) ``` We expect most roxygen2 users will write documentation using markdown rather than Rd syntax, since it's familiar, and doesn't require learning any new syntax. In most cases, you can just use your existing RMarkdown knowledge and it'll work as you expect. When it doesn't, you can read this vignette to figure out what's going on and how to fix it. ## Enabling markdown support To turn on Markdown support for a package, insert this entry into the `DESCRIPTION` file of the package: ``` Config/roxygen2/markdown: TRUE ``` If you use devtools/usethis, this will be automatically inserted for you when you create a new package. If you're updating an existing package, we recommend `usethis::use_roxygen_md()` which will modify the `DESCRIPTION` and prompt you to use the [roxygen2md](https://roxygen2md.r-lib.org) package to convert your existing docs. If needed, you can also use `@md` or `@noMd` to turn markdown support on or off for a documentation block. Here is an example roxygen chunk that uses Markdown. ```r #' Use roxygen to document a package #' #' This function is a wrapper for the [roxygen2::roxygenize()] function from #' the roxygen2 package. See the documentation and vignettes of #' that package to learn how to use roxygen. #' #' @param pkg package description, can be path or package name. See #' [as.package()] for more information. #' @param clean,reload Deprecated. #' @inheritParams roxygen2::roxygenise #' @seealso [roxygen2::roxygenize()], `browseVignettes("roxygen2")` #' @export ``` ## Basic syntax roxygen uses the [commonmark package](https://github.com/r-lib/commonmark), based on the "CommonMark Reference Implementation". See for more about the parser and the markdown language it supports. The most important details are described below. ### Sections and subsections The usual Markdown heading markup creates sections and subsections. Top level headings (e.g. `# title`) create sections with the `\section{}` Rd tag. This largely supersedes use of the older `@section` tag. Top-level headings can only appear after the `@description` and `@details` tags. Since `@details` can appear multiple times in a block, you can always precede a '`#`' section with `@details`, if you want put it near the end of the block, after `@return` for example: ```r #' @details #' Trim the leading and trailing whitespace from a character vector. #' #' @param x Character vector. #' @return Character vector, with the whitespace trimmed. #' #' @details # This will be a new section #' ... ``` Top level sections are placed at a fixed position in the manual page, after the parameters and the details, but before `\note{}`, `\seealso{}` and the `\examples{}`. Their order will be the same as in the roxygen block. Headings at level two and above may appear inside any roxygen tag that formats lines of text, e.g. `@description`, `@details`, `@return`, and create subsections with the `\subsection{}` Rd tag. ```r #' @details #' ## Subsection within details #' ### Sub-subsection #' ... text ... ``` ### Inline formatting For *emphasis*, put the text between asterisks or underline characters. For **strong** text, use two asterisks at both sides. ```r #' @references #' Robert E Tarjan and Mihalis Yannakakis. (1984). Simple #' linear-time algorithms to test chordality of graphs, test acyclicity #' of hypergraphs, and selectively reduce acyclic hypergraphs. #' *SIAM Journal of Computation* **13**, 566-579. ``` ```r #' See `::is_falsy` for the definition of what is _falsy_ #' and what is _truthy_. ``` ### Code Inline code is supported via backticks. ```r #' @param ns Optionally, a named vector giving prefix-url pairs, as #' produced by `xml_ns`. If provided, all names will be explicitly #' qualified with the ns prefix, i.e. if the element `bar` is defined ... ``` For blocks of code, put your code between triple backticks: ````r #' ``` #' pkg <- make_packages( #' foo1 = { f <- function() print("hello!") ; d <- 1:10 }, #' foo2 = { f <- function() print("hello again!") ; d <- 11:20 } #' ) #' foo1::f() #' foo2::f() #' foo1::d #' foo2::d #' dispose_packages(pkg) #' ``` ```` You can also include executable code chunks using the usual knitr syntax. See below for more details. ### Lists Regular Markdown lists are recognized and converted to `\enumerate{}` or `\itemize{}` lists: ```r #' There are two ways to use this function: #' 1. If its first argument is not named, then it returns a function #' that can be used to color strings. #' 1. If its first argument is named, then it also creates a #' style with the given name. This style can be used in #' `style`. One can still use the return value #' of the function, to create a style function. ``` ```r #' The style (the `...` argument) can be anything of the #' following: #' * An R color name, see `colors()`. #' * A 6- or 8-digit hexa color string, e.g. `#ff0000` means #' red. Transparency (alpha channel) values are ignored. #' * A one-column matrix with three rows for the red, green, #' and blue channels, as returned by [grDevices::col2rgb()]. ``` Note that you do not have to leave an empty line before the list. This is different from some Markdown parsers. ### Tables Use [GFM table formatting](https://github.github.com/gfm/#tables-extension-): ```md | foo | bar | | --- | --- | | baz | bim | ``` By default, columns are left-aligned. Use colons to generate right and center aligned columns: ```md | left | center | right | | :--- | :----: | ----: | | 1 | 2 | 3 | ``` ### Links Markdown hyperlinks work as usual: ```r #' See more about the Markdown markup at the #' [Commonmark web site](http://commonmark.org/help) ``` URLs inside angle brackets are also automatically converted to hyperlinks: ```r #' The main R web site is at . ``` ### Images Markdown syntax for inline images works. The image files must be in the `man/figures` directory: ```r #' Here is an example plot: #' ![](example-plot.jpg "Example Plot Title") ``` ## Function links Markdown notation can also be used to create links to other help topics. There are three basic forms for linking to topics in the current package or its dependencies. | Markdown | Code | Rd | | :-------------- | :--- | :---------------------------- | | `[func()]` | yes | `\code{\link[=func]{func()}}` | | `[topic]` | no | `\link{topic}` | | `` [`topic`] `` | yes | `\code{\link{topic}}` | R 4.5.0 and later requires that links to all topics outside of your package be explicitly qualified with the package name. roxygen2 will attempt to do this for you by looking at the dependencies of your package. If this fails, e.g. if multiple dependencies document the same topic, you'll need to manually resolve using the syntax below. | Markdown | Code | Rd | | :------------------- | :--- | :------------------------------------ | | `[pkg::func()]` | yes | `\code{\link[pkg:func]{pkg::func()}}` | | `[pkg::topic]` | no | `\link[pkg:topic]{pkg::topic}` | | `` [`pkg::topic`] `` | yes | `\code{\link[pkg:topic]{pkg::topic}}` | You can also provide your own link text. In this case there's no advantage to supplying a function call, as you control the formatting of the text. | Markdown | Code | Rd | | :------------------------- | :--- | :------------------------------ | | `[text][topic]` | no | `\link[=topic]{text}` | | `` [`text`][topic] `` | yes | `\code{\link[=topic]{text}}` | | `[text][pkg::topic]` | no | `\link[pkg:topic]{text}` | | `` [`text`][pkg::topic] `` | yes | `\code{\link[pkg:topic]{text}}` | ## Code chunks You can insert executable code with ```` ```{r} ```` just like in knitr documents. For example: ````{r} #| echo: false simple_fenced <- "#' @title Title #' @details Details #' ```{r lorem} #' 1+1 #' ``` #' @md foo <- function() NULL " ```` ```{r, code=simple_fenced} ``` becomes: ```{r} #| echo: false #| results: "asis" cat( "\x60\x60\x60rd\n", format(roxygen2:::roc_proc_text(roxygen2::rd_roclet(), simple_fenced)[[1]]), "\n\x60\x60\x60" ) ``` This code is run every time you call `roxygenize()` (or `devtools::document()`) to generate the Rd files. This potentially makes `roxygenize()` (much) slower. Either avoid expensive computations, or turn on knitr caching with `cache = TRUE`. Make sure to omit the cache from the package with `usethis::use_build_ignore()`. Note that knitr will call the appropriate `print()` or (if available) `knitr::knit_print()` method on the result. This may generate markdown not supported by roxygen2. If needed, override the automatic methods to have your R calls return your own markdown as a character vector, wrapped in `knitr::asis_output()`. You can also use `` `r ` `` to insert the results of running R code. This is particularly useful for reducing duplication in your documentation so is documented in `vignette("reuse")`. ### Chunk options Code blocks support some knitr chunk options, e.g. to keep the output of several expressions together, you can specify `results="hold"`: ````r #' ```{r results="hold"} #' names(mtcars) #' nrow(mtcars) #' ``` ```` Some knitr chunk options are reset at the start of every code block, so if you want to change these, you'll have to specify them for every chunk. These are currently `r paste0("\x60", names(roxygen2:::knitr_chunk_defaults()), "\x60")`. Alternatively, you can set the `knitr_chunk_options` option to override these defaults, or add new chunk options that are used for the whole package. See `?load_options` for specifying roxygen2 options. ### Images Plots will create `.png` files in the `man/figures` directory with file names coming from the chunk name. Be aware that plots can quickly increase the size of your package leading to challenges for CRAN submission. ````r #' ```{r iris-pairs-plot} #' pairs(iris[1:4], main = "Anderson's Iris Data -- 3 species", #' pch = 21, bg = c("red", "green3", "blue")[unclass(iris$Species)]) #' ``` ```` By default roxygen2 only includes PDF images in the PDF manual, and SVG images in the HTML manual. If you want to avoid this restriction, set the `restrict_image_formats` roxygen2 option to `FALSE`, see `?load_options`. ## Possible problems ### Some Rd tags can't contain markdown When mixing `Rd` and Markdown notation, most `Rd` tags may contain Markdown markup, the ones that can *not* are: `r paste0("\x60", roxygen2:::escaped_for_md, "\x60", collapse = ", ")`. ### Mixing Markdown and `Rd` markup Note that turning on Markdown does *not* turn off the standard `Rd` syntax. We suggest that you use the regular `Rd` tags in a Markdown roxygen chunk only if necessary. The two parsers do occasionally interact, and the Markdown parser can pick up and reformat Rd syntax, causing an error, or corrupted manuals. ### Leading white space Leading white space is interpreted by the commonmark parser, but is ignored by the `Rd` parser (except in `\preformatted{}`). Make sure that you only include leading white space intentionally, for example, in nested lists. ### Spurious lists The commonmark parser does not require an empty line before lists, and this might lead to unintended lists if a line starts with a number followed by a dot, or with an asterisk followed by white space: ```r #' You can see more about this topic in the book cited below, on page #' 42. Clearly, the numbered list that starts here is not intentional. ``` roxygen2/vignettes/rd-packages.Rmd0000644000176200001440000000250215172221402016657 0ustar liggesusers--- title: "Documenting packages" description: > How to document a package as a whole using the `"_PACKAGE"` sentinel. output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Documenting packages} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r} #| include: false knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ``` As well as documenting every object inside the package, you can also document the package itself by documenting the special sentinel `"_PACKAGE"`. This automatically includes information parsed from the `DESCRIPTION`, including title, description, list of authors, and useful URLs. We recommend placing package documentation in `{pkgname}-package.R`, and have `@keywords internal`. Use `usethis::use_package_doc()` to set up automatically. Here's an example: ```{r} #| eval: false #' @keywords internal "_PACKAGE" ``` Package documentation is a good place to put `# Package options` that documents options used by the package. Some notes: - By default, aliases will be added so that both `?pkgname` and `package?pkgname` will find the package help. If there's an existing function called `pkgname`, use `@aliases {pkgname}-package NULL` to override the default. - Use `@references` to point to published material about the package that users might find helpful. roxygen2/vignettes/roxygen2.Rmd0000644000176200001440000001407515172221402016263 0ustar liggesusers--- title: "Get started with roxygen2" description: > Learn the big picture of roxygen2 and the basic workflow you'll use with it. output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Get started with roxygen2} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r} #| include: false knitr::opts_chunk$set(comment = "#>", collapse = TRUE) ``` Documentation is one of the most important aspects of good code. Without it, users won't know how to use your package, and are unlikely to do so. Documentation is also useful for you in the future (so you remember what the heck you were thinking!), and for other developers working on your package. The goal of roxygen2 is to make documenting your code as easy as possible. R provides a standard way of documenting packages: you write `.Rd` files in the `man/` directory. These files use a custom syntax, loosely based on LaTeX. roxygen2 provides a number of advantages over writing `.Rd` files by hand: - Code and documentation are adjacent so when you modify your code, it's easy to remember that you need to update the documentation. - roxygen2 dynamically inspects the objects that it's documenting, so it can automatically add data that you'd otherwise have to write by hand. - It abstracts over the differences in documenting S3 and S4 methods, generics and classes, so you need to learn fewer details. As well as generating `.Rd` files, roxygen will also create a `NAMESPACE` for you, and will manage the `Collate` field in `DESCRIPTION`. ## Basics A roxygen **block** is a sequence of lines starting with `#'` (optionally preceded by white space). The first lines of the block is called the **introduction** and forms the title, description, and details, as described below. The introduction continues until the first **tag**. Tags start with `@`, like `@details` or `@param`. Tags must appear at the beginning of a line and their content extends to the start of the next tag or the end of the block. Text within the description or tags can be formatted using Markdown or `Rd` commands; see `vignette("rd-formatting")` for details. A block ends when it hits R code, usually a function or object assignment. Blocks ignore empty lines, including lines made up of non-roxygen comments. If you need to separate two blocks, use `NULL`. If you want to use roxygen2 documentation tags without generating an `.Rd` file, you can use `@noRd` to suppress file generation for a given topic. This is useful if you want to use roxygen2 conventions for documenting an internal function; only people reading the source doc will be able to read the docs. ## Running roxygen There are three main ways to run roxygen: - `roxygen2::roxygenise()`. - `devtools::document()`. - `Ctrl + Shift + D`, if you're using RStudio or Positron (with [RStudio keybindings](https://positron.posit.co/migrate-rstudio-keybindings.html) enabled). You can mix handwritten Rd and roxygen2; roxygen2 will never overwrite a file it didn't create. ## Basic process: human readable documentation There are three steps in the transformation from roxygen comments in your source file to human readable documentation: 1. You add roxygen comments to your source file. 2. `roxygen2::roxygenise()` converts roxygen comments to `.Rd` files. 3. R converts `.Rd` files to human readable documentation. The process starts when you add specially formatted roxygen comments to your source file. Roxygen comments start with `#'` so you can continue to use regular comments for other purposes. ```{r} #| echo: false example <- "#' Add together two numbers #' #' @param x A number. #' @param y A number. #' @return A number. #' @export #' @examples #' add(1, 1) #' add(10, 1) add <- function(x, y) { x + y } " ``` ```{r, code=example} ``` For the example above, this will generate `man/add.Rd` that looks like: ```{r} #| echo: false #| results: "asis" cat( "\x60\x60\x60rd\n", format(roxygen2::roc_proc_text(roxygen2::rd_roclet(), example)[[1]]), "\n\x60\x60\x60", sep = "" ) ``` Rd files are a special file format loosely based on LaTeX. You can read more about the Rd format in the [R extensions](https://cran.r-project.org/doc/manuals/R-exts.html#Rd-format) manual. With roxygen2, there are few reasons to know about Rd files, so here I'll avoid discussing them as much as possible, focusing instead on what you need to know about roxygen2. When you use `?x`, `help("x")` or `example("x")` R looks for an Rd file containing `\alias{x}`. It then parses the file, converts it into HTML and displays it. These functions look for an Rd file in *installed* packages. This isn't very useful for package development, because you want to use the `.Rd` files in the *source* package. For this reason, we recommend that you use roxygen2 in conjunction with devtools: `devtools::load_all()` automatically adds shims so that `?` and friends will look in the development package. ## Basic process: making functions available to users The example above, that includes an `@export` tag to indicate the `add()` function should be part of the interface of the package available to users, would also be converted by `roxygenize()` into a line in the `NAMESPACE` file: ```{r} #| echo: false #| results: "asis" cat( "\x60\x60\x60txt\n", roxygen2::roc_proc_text(roxygen2::namespace_roclet(), example), "\n\x60\x60\x60", sep = "" ) ``` ## Learn more The other vignettes provide more detail on the most important individual components: - `vignette("rd-functions")` describes how to document your functions with roxygen2. - `vignette("rd-datasets")` and `vignette("rd-packages")` cover documenting datasets and the package itself. - `vignette("rd-S3")`, `vignette("rd-S4")`, `vignette("rd-R6")`, and `vignette("rd-S7")` discuss documenting the various OOP systems. - `vignette("rd-formatting")` gives the details of roxygen2's rmarkdown support. - `vignette("reuse")` demonstrates the tools available to reuse documentation in multiple places. - `vignette("namespace")` describes how to generate a `NAMESPACE` file, how namespacing works in R, and how you can use roxygen2 to be specific about what your package needs and supplies. roxygen2/vignettes/rd-R6.Rmd0000644000176200001440000001172615172221402015400 0ustar liggesusers--- title: "Documenting R6" description: > How to document R6 classes, methods, and fields. output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Documenting R6} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r} #| include: false knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ``` There are three things that you might document for [R6](https://adv-r.hadley.nz/r6.html): - **Classes**: document the class as a whole with a roxygen block before `R6Class()`. - **Fields**: document fields and active bindings in-line with `@field`. - **Methods**: document methods in-line; every method doc must start with a tag. R6 documentation works differently from regular function documentation because much of it is written **in-line**, inside the class definition. Additionally, because `R CMD check` doesn't know about R6, roxygen2 is in charge of checking that all public methods, fields, active bindings, and method arguments are documented. It'll issue warnings for anything that's missing, which you can suppress using the techniques described below. ## Classes Document an R6 class by placing a roxygen block before the `R6Class()` call. Use `@export` to make the class available to users. ```{r} #| eval: false #' R6 Class Representing a Person #' #' A person has a name and a hair color. #' @export Person <- R6::R6Class( "Person", public = list( # ... fields and methods ... ) ) ``` roxygen2 automatically generates additional sections: - A section with information about the superclass(es) of the class, with links. In HTML this includes a list of all inherited methods, with links. - An 'Examples' section that contains all class and method examples. This section is run by `R CMD check`, so method examples must work without errors. `@param` tags that appear at the class level (i.e. before the `R6Class()` call) are automatically inherited by all methods, if needed. ## Fields and active bindings Fields and active bindings are documented **in-line** using the `@field` tag, right before the field definition: ```{r} #| eval: false Person <- R6::R6Class( "Person", public = list( #' @field name First or full name of the person. name = NULL, #' @field birthdate Date of birth, as a [Date]. birthdate = NULL ), active = list( #' @field age Age in years, computed from `birthdate` (read-only). age = function() { as.numeric(difftime(Sys.Date(), self$birthdate, units = "days")) / 365.25 } ) ) ``` If a field or active binding is inherited from a superclass and documented there, the child class will automatically inherit the documentation. To suppress documentation of a field or active binding, use `@field name NULL`. ## Methods Methods are also documented **in-line**, right before the method definition. Unlike regular function documentation, all roxygen comment lines for a method must appear **after a tag** --- you can't start with a plain text introduction. Use `@description` to provide the method's description: ```{r} #| eval: false Person <- R6::R6Class( "Person", public = list( #' @description #' Create a new person object. #' @param name Name. #' @param hair Hair color. #' @returns A new `Person` object. initialize = function(name = NA, hair = NA) { self$name <- name self$hair <- hair self$greet() }, #' @description #' Change hair color. #' @param val New hair color. set_hair = function(val) { self$hair <- val }, #' @description #' Say hi. greet = function() { cat(paste0("Hello, my name is ", self$name, ".\n")) } ) ) ``` Like functions, methods can use the `@description`, `@details`, `@param`, `@returns`, and `@examples` tags. These are used to create a subsection for the method, within a separate 'Methods' section in the rendered help. If a method parameter is not documented with `@param`, roxygen2 will look for documentation in the following order: class-level `@param` tags, `@field` tags (for `initialize()` only), and then the same method in parent classes. If you want to leave a method without documentation, use `@noRd` to suppress the warning. ### Dynamic methods If a method is added dynamically with `$set()`, you can document it by placing a roxygen block directly above the `$set()` call: ```{r} #| eval: false #' @description #' Say goodbye. Person$set("public", "goodbye", function() { cat(paste0("Goodbye from ", self$name, ".\n")) }) ``` roxygen2 will automatically associate the block with the class. If roxygen2 can't automatically discover a method, you can use `@R6method Class$method` to explicitly associate a documentation block with a method. Place it in a standalone roxygen block above `NULL`: ```r #' @R6method Person$set_hair #' @description Change hair color. #' @param val New hair color. NULL ``` ## Opting out To turn off the special handling of R6 classes and go back to the roxygen2 6.x.x behavior, add `Config/roxygen2/r6: false` to your `DESCRIPTION` file. roxygen2/vignettes/rd-S3.Rmd0000644000176200001440000001460415172221402015374 0ustar liggesusers--- title: "Documenting S3" description: > How to document S3 generics, methods, and classes. output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Documenting S3} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r} #| include: false knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ``` There are three things that you might document for [S3](https://adv-r.hadley.nz/s3.html): - **Generics**: mention that the function is a generic and list the available methods. - **Methods**: link back to the generic; only document individually when the method has unique behavior or arguments. - **Classes**: document the constructor. ## Generics S3 **generics** are regular functions, so document them as such. `@export` a generic if you want users to call it or other developers to write methods for it. If the generic is internal, you don't need to export or document it. The documentation should mention that the function is a generic, because this tells the reader that the behavior may vary depending on the input and that they can write their own methods. For simple generics, you can do this in the description: ```{r} #| eval: false #' Frobnpolicate an object #' #' @description #' `frobnpolicate()` is an S3 generic that ..., with methods available for #' the following classes: #' #' `r doclisting::methods_list("frobnpolicate")` ``` For more complicated generics, you can use a `# Methods` section to provide more detail: ```{r} #| eval: false #' Frobnpolicate an object #' #' @description #' `frobnpolicate()` does ... #' #' # Methods #' `frobnpolicate()` is an S3 generic with methods available for the following #' classes: #' #' `r doclisting::methods_list("frobnpolicate")` ``` You might also want to include a section that provides additional details for developers implementing their own methods. Both examples above use the [doclisting](https://doclisting.r-lib.org/) package to automatically generate a list of methods, with links to their help topics. These examples use [inline R code](reuse.html#inline-code) (`` `r ` ``), which generates the list at documentation time (i.e. when you run `devtools::document()`). This only requires including doclisting in `Suggests`. If you want the list to dynamically reflect all methods that are currently registered (including methods registered by other packages), use an [inline Rd code](reuse.html#inline-rd-code) block (`` `Rd ` ``) instead: ```{r} #| eval: false #' `Rd doclisting::methods_list("frobnpolicate")` ``` Using `` `Rd ` `` requires doclisting in `Imports`, and you'll need a dummy call to eliminate the `R CMD check` NOTE about unused imports: ```{r} #| eval: false ignore_unused_imports <- function() { doclisting::methods_list } ``` ## Classes S3 **classes** have no formal definition, so document the [constructor](https://adv-r.hadley.nz/s3.html#s3-constructor). `@export` the constructor if you want users to create instances of your class or other developers to extend it (e.g. by creating subclasses). Internal constructors don't need documentation. Note that you don't need to list methods for a class: in S3, methods belong to generics, not to classes. Users should look at the generic's help page to learn about available methods. ## Methods It is your choice whether or not to document S3 **methods**. Generally, it's not necessary to document straightforward methods for common generics like `print()`. You must, however, always `@export` S3 methods, even for internal generics. This **registers** the method so that the generic can find it; a user can't directly access the method definition by typing its name. roxygen2 will warn you if you have forgotten. ```{r} #| eval: false #' @export bizarro.character <- function(x, ...) { letters <- strsplit(x, "") letters_rev <- lapply(letters, rev) vapply(letters_rev, paste, collapse = "", FUN.VALUE = character(1)) } ``` If you are exporting a method in some other way, you can use `@exportS3Method NULL` to suppress the warning. It's good practice to document methods that have unique behavior or arguments. For example, the clock package documents the methods for [`date_group()`](https://clock.r-lib.org/reference/date_group.html) on their own pages ([Date](https://clock.r-lib.org/reference/date-group.html), [POSIXt](https://clock.r-lib.org/reference/posixt-group.html)) because the methods accept different `precision` values and have type-specific return value semantics and examples. The generic page serves only as a signpost linking to the individual method pages. When documenting a method, always include a link back to the generic using `[generic_name()]` so the reader can easily find the full documentation and other methods. ### Methods for generics in other packages It's common to write methods for generics defined in other packages. There are three cases you need to be aware of: - **Base packages**: you don't need to do anything special: just `@export` the method and roxygen2 will generate the correct `S3method()` directive. - **Imported packages**: You have two options. Firstly, import the generic with `@importFrom` and `@export` the method:. ```{r} #| eval: false #' @importFrom pkg generic #' @export generic.foo <- function(x, ...) {} ``` Alternatively, use `@exportS3Method pkg::generic`: ```{r} #| eval: false #' @exportS3Method pkg::generic generic.foo <- function(x, ...) {} ``` - **Suggested packages**: you can't import the generic for a suggested package, so you must use `@exportS3Method pkg::generic`. This uses delayed registration, so the method is only be registered when the suggested package is loaded. Generally, roxygen2 can automatically figure out which generic the method belongs to. But there is occasionally ambiguity if the generic name contains `.`. You can avoid this problem in your own packages by not using `.` in generic names. But you might encounter it when writing methods for generics in other packages. For example, is `all.equal.data.frame()` the `equal.data.frame` method for `all()`, or the `data.frame` method for `all.equal()`? If this happens to you, disambiguate with `@method`: ```{r} #| eval: false #' @method all.equal data.frame #' @export all.equal.data.frame <- function(target, current, ...) { # ... } ``` roxygen2 does now automatically handle the `all.equal` case for you, so this should happen very very rarely. And, again it can be avoided by not using `.` in generic or class names. roxygen2/src/0000755000176200001440000000000015174650112012620 5ustar liggesusersroxygen2/src/escapeExamples.cpp0000644000176200001440000000213314262717326016272 0ustar liggesusers#include #include [[cpp11::register]] std::string escapeExamples(std::string x) { std::string out; out.reserve(x.length() * 1.1); char in_string = '\0'; bool in_escape = false; bool in_comment = false; std::string::const_iterator cur, end = x.end(); for (cur = x.begin(); cur != end; cur++) { if (in_comment) { // inside comment if (*cur == '\n') { in_comment = false; } } else if (in_string == '\0') { // regular code if (*cur == '#') { in_comment = true; } else if (*cur == '\'' || *cur == '"' || *cur == '`') { in_string = *cur; } } else { // inside string/symbol if (in_escape) { in_escape = false; if (*cur == 'l' || *cur == 'v') { out += '\\'; } else if (*cur == '\\') { out += "\\\\"; } } else { if (*cur == in_string) { in_string = '\0'; } else if (*cur == '\\') { in_escape = true; } } } if (*cur == '%') { out += '\\'; } out += *cur; } return out; } roxygen2/src/leadingSpaces.cpp0000644000176200001440000000071314262717326016077 0ustar liggesusers#include #include int leadingSpacesOne(std::string line) { int n = line.size(); for(int i = 0; i < n; ++i) { char cur = line[i]; if (cur != ' ') return(i); } return n; } [[cpp11::register]] cpp11::integers leadingSpaces(cpp11::strings lines) { int n = lines.size(); cpp11::writable::integers out(n); for(int i = 0; i < n; ++i) { out[i] = leadingSpacesOne(lines[i]); } return out; } roxygen2/src/cpp11.cpp0000644000176200001440000000600215165177051014254 0ustar liggesusers// Generated by cpp11: do not edit by hand // clang-format off #include "cpp11/declarations.hpp" #include // escapeExamples.cpp std::string escapeExamples(std::string x); extern "C" SEXP _roxygen2_escapeExamples(SEXP x) { BEGIN_CPP11 return cpp11::as_sexp(escapeExamples(cpp11::as_cpp>(x))); END_CPP11 } // isComplete.cpp int findEndOfTag(std::string string, bool is_code, int start); extern "C" SEXP _roxygen2_findEndOfTag(SEXP string, SEXP is_code, SEXP start) { BEGIN_CPP11 return cpp11::as_sexp(findEndOfTag(cpp11::as_cpp>(string), cpp11::as_cpp>(is_code), cpp11::as_cpp>(start))); END_CPP11 } // isComplete.cpp bool rdComplete(std::string string, bool is_code); extern "C" SEXP _roxygen2_rdComplete(SEXP string, SEXP is_code) { BEGIN_CPP11 return cpp11::as_sexp(rdComplete(cpp11::as_cpp>(string), cpp11::as_cpp>(is_code))); END_CPP11 } // leadingSpaces.cpp cpp11::integers leadingSpaces(cpp11::strings lines); extern "C" SEXP _roxygen2_leadingSpaces(SEXP lines) { BEGIN_CPP11 return cpp11::as_sexp(leadingSpaces(cpp11::as_cpp>(lines))); END_CPP11 } // parser2.cpp cpp11::list tokenise_block(cpp11::strings lines, std::string file, int offset); extern "C" SEXP _roxygen2_tokenise_block(SEXP lines, SEXP file, SEXP offset) { BEGIN_CPP11 return cpp11::as_sexp(tokenise_block(cpp11::as_cpp>(lines), cpp11::as_cpp>(file), cpp11::as_cpp>(offset))); END_CPP11 } // parser2.cpp cpp11::strings find_includes(std::string path); extern "C" SEXP _roxygen2_find_includes(SEXP path) { BEGIN_CPP11 return cpp11::as_sexp(find_includes(cpp11::as_cpp>(path))); END_CPP11 } // wrapUsage.cpp std::string wrapUsage(std::string string, int width, int indent); extern "C" SEXP _roxygen2_wrapUsage(SEXP string, SEXP width, SEXP indent) { BEGIN_CPP11 return cpp11::as_sexp(wrapUsage(cpp11::as_cpp>(string), cpp11::as_cpp>(width), cpp11::as_cpp>(indent))); END_CPP11 } extern "C" { static const R_CallMethodDef CallEntries[] = { {"_roxygen2_escapeExamples", (DL_FUNC) &_roxygen2_escapeExamples, 1}, {"_roxygen2_findEndOfTag", (DL_FUNC) &_roxygen2_findEndOfTag, 3}, {"_roxygen2_find_includes", (DL_FUNC) &_roxygen2_find_includes, 1}, {"_roxygen2_leadingSpaces", (DL_FUNC) &_roxygen2_leadingSpaces, 1}, {"_roxygen2_rdComplete", (DL_FUNC) &_roxygen2_rdComplete, 2}, {"_roxygen2_tokenise_block", (DL_FUNC) &_roxygen2_tokenise_block, 3}, {"_roxygen2_wrapUsage", (DL_FUNC) &_roxygen2_wrapUsage, 3}, {NULL, NULL, 0} }; } extern "C" attribute_visible void R_init_roxygen2(DllInfo* dll){ R_registerRoutines(dll, NULL, CallEntries, NULL, NULL); R_useDynamicSymbols(dll, FALSE); R_forceSymbols(dll, TRUE); } roxygen2/src/wrapUsage.cpp0000644000176200001440000000313314262717326015272 0ustar liggesusers#include #include #include std::vector splitByWhitespace(std::string string) { std::vector out; std::string acc = ""; char in_string = '\0'; int in_escape = 0; std::string::const_iterator cur = string.begin(), end = string.end(); while(cur != end) { if (in_string != '\0') { acc += *cur; if (in_escape) { in_escape--; } else if (*cur == '\\' && cur + 1 != end && *(cur + 1) == '\\') { in_escape = 2; } else if (*cur == in_string) { // String terminates in_string = '\0'; } } else if (*cur == ' ' || *cur == '\t' || *cur == '\n') { if (*cur == '\n') acc += *cur; // newlines are significant whitespace out.push_back(acc); acc = ""; } else if (*cur == '"' || *cur == '\'' || *cur == '`') { in_string = *cur; acc += *cur; } else { acc += *cur; } cur++; } out.push_back(acc); return out; } [[cpp11::register]] std::string wrapUsage(std::string string, int width, int indent) { std::vector pieces = splitByWhitespace(string); int n = pieces.size(); int cur_width = 0; std::string out; for (int i = 0; i < n; ++i) { int piece_width = pieces[i].size(); // Need to include space for a space if (piece_width + cur_width + 1 < width) { cur_width += piece_width; if (i != 0) { out += " "; cur_width++; } } else { cur_width = piece_width + indent; out += "\n" + std::string(indent, ' '); } out += pieces[i]; } return out; } roxygen2/src/isComplete.cpp0000644000176200001440000001113015165177023015431 0ustar liggesusers#include #include // From http://developer.r-project.org/parseRd.pdf: The characters \, %, {, // and } have special meaning in almost all parts of an Rd file. In code, // strings must also match, except in comments. enum class State { Rd, RdEscape, String, StringEscape, RComment, RdComment }; bool is_complete(int braces, State state) { return braces == 0 && (state == State::Rd || state == State::RComment || state == State::RdComment); } // Scan past a raw string constant starting at position i (the 'r' or 'R'). // Returns the position of the closing quote, or -1 if not a raw string // or the raw string is incomplete. // Raw strings: r"(...)", r'(...)', r"{...}", r"[...]", with optional dashes // like r"--(...)--)--" int scan_raw_string(std::string string, int i) { int n = string.length(); if (i + 1 >= n) return -1; char quote = string[i + 1]; if (quote != '"' && quote != '\'') return -1; int j = i + 2; // Raw strings can optionally start and end with any number of dashes int dashes = 0; while (j < n && string[j] == '-') { dashes++; j++; } // Figure out closing delimiter if (j >= n) return -1; char close; switch (string[j]) { case '(': close = ')'; break; case '{': close = '}'; break; case '[': close = ']'; break; default: return -1; } j++; // Scan for closing sequence: close + dashes + quote while (j < n) { if (string[j] == close) { int k = j + 1; int d = 0; while (k < n && d < dashes && string[k] == '-') { d++; k++; } if (d == dashes && k < n && string[k] == quote) { return k; } } j++; } return -1; } // The two functions are very similar, so we use a common // implementation and select the functionality via the // mode argument: // mode == 0: rdComplete // mode == 1: findEndOfTag int roxygen_parse_tag(std::string string, bool is_code = false, int mode = 0, int start = 0) { int n = string.length(); State state = State::Rd; char string_delim = '\0'; int braces = 0, r_braces = 0; for (int i = start; i < n; i++) { char cur = string[i]; switch (state) { case State::Rd: switch (cur) { case '{': braces++; break; case '}': braces--; break; case '\\': state = State::RdEscape; break; case '#': if (is_code) state = State::RComment; break; case '%': state = State::RdComment; break; case '\'': if (is_code) { state = State::String; string_delim = '\''; } break; case '"': if (is_code) { state = State::String; string_delim = '"'; } break; case 'r': case 'R': if (is_code) { int end = scan_raw_string(string, i); if (end >= 0) { i = end; } else if (i + 1 < n && (string[i + 1] == '"' || string[i + 1] == '\'')) { // Looks like a raw string but incomplete state = State::String; i = n - 1; } } break; } break; case State::RdEscape: state = State::Rd; break; case State::String: if (cur == string_delim) { state = State::Rd; string_delim = '\0'; } else if (cur == '\\') { state = State::StringEscape; } break; case State::StringEscape: state = State::String; break; case State::RComment: // We don't trace braces in comment independently to make it possible to // close a multi-line Rd expression in a comment. This is a hack that // needed to support @examplesIf if (cur == '\n') { state = State::Rd; } else if (cur == '{') { braces++; } else if (cur == '}') { braces--; } break; case State::RdComment: if (cur == '\n') { state = State::Rd; } break; } if (mode == 1) { bool complete = is_complete(braces, state); if (complete && i + 1 < n && string[i + 1] != '{') { return i; } } } bool complete = is_complete(braces, state); if (mode == 0) { return complete ? 1 : 0; } else { return complete ? n - 1 : -1; } } [[cpp11::register]] int findEndOfTag(std::string string, bool is_code, int start = 0) { return roxygen_parse_tag(string, is_code, 1, start); } [[cpp11::register]] bool rdComplete(std::string string, bool is_code) { return roxygen_parse_tag(string, is_code, 0) == 1 ? true : false; } roxygen2/src/parser2.cpp0000644000176200001440000001024114527140741014703 0ustar liggesusers#include #include #include #include #include #include #include #include class RoxygenLine { std::string line_; const char* begin_; const char* end_; const char* cur_; public: RoxygenLine(const std::string& line) : line_(line) { begin_ = cur_ = line_.data(); end_ = begin_ + line_.size(); } bool consumeChar(char c) { if (cur_ == end_ || *cur_ != c) return false; cur_++; return true; } int consumeWhitespace(int max = -1) { int i = 0; while (cur_ != end_ && std::isspace(*cur_)) { cur_++; i++; if (max > 0 && i >= max) break; } return i; } bool consumeRoxygenComment() { consumeWhitespace(); if (!consumeChar('#')) return false; while (consumeChar('#')); if (!consumeChar('\'')) return false; consumeWhitespace(1); return true; } bool consumeTag(std::string* pOut) { if (!consumeChar('@')) return false; while(cur_ != end_ && std::isalnum(*cur_) ) { pOut->push_back(*cur_); cur_++; } return true; } bool consumeText(std::string* pOut) { while (cur_ != end_) { if (isEscapedAt()) { pOut->push_back('@'); cur_ += 2; } else { pOut->push_back(*cur_); cur_++; } } return true; } bool isEscapedAt() { if (cur_ == end_) return false; if (*cur_ != '@') return false; const char* next = cur_ + 1; if (next == end_) return false; return *next == '@'; } }; std::string stripTrailingNewline(std::string x) { if (x[x.size() - 1] == '\n') { x.resize(x.size() - 1); } return x; } [[cpp11::register]] cpp11::list tokenise_block(cpp11::strings lines, std::string file, int offset) { std::vector tags, vals; std::vector rows; int curRow = 0; std::string curTag(""), curVal(""); for (int i = 0; i < lines.size(); ++i) { RoxygenLine line((std::string(lines[i]))); if (!line.consumeRoxygenComment()) { // Increment curRow for non-roxygen comments at start of block if (curVal.empty()) curRow++; continue; } std::string tag; if (line.consumeTag(&tag)) { line.consumeWhitespace(1); if (curVal != "" || curTag != "") { rows.push_back(curRow); tags.push_back(curTag); vals.push_back(curVal); } curRow = i; curTag.assign(tag); curVal.assign(""); } line.consumeText(&curVal); curVal.push_back('\n'); } if (curVal != "" || curTag != "") { rows.push_back(curRow); tags.push_back(curTag); vals.push_back(curVal); } // Convert to a list R_xlen_t n = rows.size(); cpp11::writable::list out(n); using namespace cpp11::literals; for (R_xlen_t i = 0; i < n; ++i) { cpp11::writable::list x({ "file"_nm = file, "line"_nm = rows[i] + offset, "tag"_nm = tags[i], "raw"_nm = stripTrailingNewline(vals[i]), "val"_nm = R_NilValue }); std::string tag("roxy_tag_"); tag += tags[i]; x.attr("class") = {tag.c_str(), "roxy_tag"}; out[i] = x; } return out; } [[cpp11::register]] cpp11::strings find_includes(std::string path) { std::vector includes; std::string path_native = Rf_translateChar(cpp11::r_string(path)); std::ifstream file(path_native.c_str()); if (!file.good()) cpp11::stop("Failed to open %s", path.c_str()); std::string rawline; while (std::getline(file, rawline)) { RoxygenLine line(rawline); if (!line.consumeRoxygenComment()) continue; std::string tag, value; if (!line.consumeTag(&tag)) continue; if (tag != "include") continue; line.consumeWhitespace(1); // Split value by whitespace // http://stackoverflow.com/questions/236129/split-a-string-in-c line.consumeText(&value); std::istringstream words(value); copy( std::istream_iterator(words), std::istream_iterator(), back_inserter(includes) ); } return cpp11::as_sexp(includes); } roxygen2/NAMESPACE0000644000176200001440000002427315174170772013271 0ustar liggesusers# Generated by roxygen2: do not edit by hand S3method(block_to_rd,default) S3method(block_to_rd,roxy_block) S3method(block_to_rd,roxy_block_r6class) S3method(c,rd) S3method(default_export,"NULL") S3method(default_export,default) S3method(default_export,rcclass) S3method(default_export,s3method) S3method(default_export,s4class) S3method(default_export,s4generic) S3method(default_export,s4method) S3method(escape,"NULL") S3method(escape,character) S3method(escape,rd) S3method(format,object) S3method(format,rd_r6_class) S3method(format,rd_r6_field) S3method(format,rd_r6_fields) S3method(format,rd_r6_inherited) S3method(format,rd_r6_method) S3method(format,rd_r6_methods) S3method(format,rd_r6_super) S3method(format,rd_section) S3method(format,rd_section_alias) S3method(format,rd_section_author) S3method(format,rd_section_backref) S3method(format,rd_section_concept) S3method(format,rd_section_description) S3method(format,rd_section_details) S3method(format,rd_section_docType) S3method(format,rd_section_encoding) S3method(format,rd_section_examples) S3method(format,rd_section_family) S3method(format,rd_section_field) S3method(format,rd_section_formals) S3method(format,rd_section_format) S3method(format,rd_section_inherit) S3method(format,rd_section_inherit_dot_params) S3method(format,rd_section_inherit_params_args) S3method(format,rd_section_inherit_section) S3method(format,rd_section_keyword) S3method(format,rd_section_minidesc) S3method(format,rd_section_name) S3method(format,rd_section_note) S3method(format,rd_section_package) S3method(format,rd_section_param) S3method(format,rd_section_prop) S3method(format,rd_section_r6_class) S3method(format,rd_section_rawRd) S3method(format,rd_section_rcmethods) S3method(format,rd_section_reexport) S3method(format,rd_section_references) S3method(format,rd_section_section) S3method(format,rd_section_seealso) S3method(format,rd_section_slot) S3method(format,rd_section_source) S3method(format,rd_section_title) S3method(format,rd_section_usage) S3method(format,rd_section_value) S3method(format,roxy_tag) S3method(merge,rd_section) S3method(merge,rd_section_author) S3method(merge,rd_section_inherit) S3method(merge,rd_section_inherit_dot_params) S3method(merge,rd_section_inherit_params_args) S3method(merge,rd_section_inherit_section) S3method(merge,rd_section_minidesc) S3method(merge,rd_section_param) S3method(merge,rd_section_prop) S3method(merge,rd_section_r6_class) S3method(merge,rd_section_reexport) S3method(merge,rd_section_section) S3method(merge,rd_section_seealso) S3method(object_defaults,"function") S3method(object_defaults,data) S3method(object_defaults,default) S3method(object_defaults,import) S3method(object_defaults,package) S3method(object_defaults,r6class) S3method(object_defaults,r6method) S3method(object_defaults,rcclass) S3method(object_defaults,s3generic) S3method(object_defaults,s3method) S3method(object_defaults,s4class) S3method(object_defaults,s4generic) S3method(object_defaults,s4method) S3method(object_defaults,s7class) S3method(object_defaults,s7generic) S3method(object_defaults,s7method) S3method(object_defaults,value) S3method(object_format,default) S3method(object_name,"function") S3method(object_name,default) S3method(object_name,s3generic) S3method(object_name,s3method) S3method(object_name,s4generic) S3method(object_name,s4method) S3method(object_usage,"function") S3method(object_usage,data) S3method(object_usage,default) S3method(object_usage,s3method) S3method(object_usage,s4generic) S3method(object_usage,s4method) S3method(object_usage,s7class) S3method(object_usage,s7generic) S3method(object_usage,s7method) S3method(object_usage,value) S3method(print,object) S3method(print,rd) S3method(print,rd_section) S3method(print,roxy_block) S3method(print,roxy_tag) S3method(roclet_clean,roclet_namespace) S3method(roclet_clean,roclet_rd) S3method(roclet_output,roclet_namespace) S3method(roclet_output,roclet_rd) S3method(roclet_output,roclet_vignette) S3method(roclet_preprocess,default) S3method(roclet_process,roclet_namespace) S3method(roclet_process,roclet_rd) S3method(roclet_process,roclet_vignette) S3method(roxy_tag_ns,default) S3method(roxy_tag_ns,roxy_tag_evalNamespace) S3method(roxy_tag_ns,roxy_tag_export) S3method(roxy_tag_ns,roxy_tag_exportClass) S3method(roxy_tag_ns,roxy_tag_exportMethod) S3method(roxy_tag_ns,roxy_tag_exportPattern) S3method(roxy_tag_ns,roxy_tag_exportS3Method) S3method(roxy_tag_ns,roxy_tag_import) S3method(roxy_tag_ns,roxy_tag_importClassesFrom) S3method(roxy_tag_ns,roxy_tag_importFrom) S3method(roxy_tag_ns,roxy_tag_importMethodsFrom) S3method(roxy_tag_ns,roxy_tag_rawNamespace) S3method(roxy_tag_ns,roxy_tag_useDynLib) S3method(roxy_tag_parse,default) S3method(roxy_tag_parse,roxy_tag_R6method) S3method(roxy_tag_parse,roxy_tag_aliases) S3method(roxy_tag_parse,roxy_tag_author) S3method(roxy_tag_parse,roxy_tag_backref) S3method(roxy_tag_parse,roxy_tag_concept) S3method(roxy_tag_parse,roxy_tag_describeIn) S3method(roxy_tag_parse,roxy_tag_description) S3method(roxy_tag_parse,roxy_tag_details) S3method(roxy_tag_parse,roxy_tag_docType) S3method(roxy_tag_parse,roxy_tag_encoding) S3method(roxy_tag_parse,roxy_tag_eval) S3method(roxy_tag_parse,roxy_tag_evalNamespace) S3method(roxy_tag_parse,roxy_tag_evalRd) S3method(roxy_tag_parse,roxy_tag_example) S3method(roxy_tag_parse,roxy_tag_examples) S3method(roxy_tag_parse,roxy_tag_examplesIf) S3method(roxy_tag_parse,roxy_tag_export) S3method(roxy_tag_parse,roxy_tag_exportClass) S3method(roxy_tag_parse,roxy_tag_exportMethod) S3method(roxy_tag_parse,roxy_tag_exportPattern) S3method(roxy_tag_parse,roxy_tag_exportS3Method) S3method(roxy_tag_parse,roxy_tag_family) S3method(roxy_tag_parse,roxy_tag_field) S3method(roxy_tag_parse,roxy_tag_format) S3method(roxy_tag_parse,roxy_tag_import) S3method(roxy_tag_parse,roxy_tag_importClassesFrom) S3method(roxy_tag_parse,roxy_tag_importFrom) S3method(roxy_tag_parse,roxy_tag_importMethodsFrom) S3method(roxy_tag_parse,roxy_tag_include) S3method(roxy_tag_parse,roxy_tag_includeRmd) S3method(roxy_tag_parse,roxy_tag_inherit) S3method(roxy_tag_parse,roxy_tag_inheritDotParams) S3method(roxy_tag_parse,roxy_tag_inheritParams) S3method(roxy_tag_parse,roxy_tag_inheritSection) S3method(roxy_tag_parse,roxy_tag_keywords) S3method(roxy_tag_parse,roxy_tag_md) S3method(roxy_tag_parse,roxy_tag_method) S3method(roxy_tag_parse,roxy_tag_name) S3method(roxy_tag_parse,roxy_tag_noMd) S3method(roxy_tag_parse,roxy_tag_noRd) S3method(roxy_tag_parse,roxy_tag_note) S3method(roxy_tag_parse,roxy_tag_order) S3method(roxy_tag_parse,roxy_tag_param) S3method(roxy_tag_parse,roxy_tag_prop) S3method(roxy_tag_parse,roxy_tag_rawNamespace) S3method(roxy_tag_parse,roxy_tag_rawRd) S3method(roxy_tag_parse,roxy_tag_rdname) S3method(roxy_tag_parse,roxy_tag_references) S3method(roxy_tag_parse,roxy_tag_return) S3method(roxy_tag_parse,roxy_tag_returns) S3method(roxy_tag_parse,roxy_tag_section) S3method(roxy_tag_parse,roxy_tag_seealso) S3method(roxy_tag_parse,roxy_tag_slot) S3method(roxy_tag_parse,roxy_tag_source) S3method(roxy_tag_parse,roxy_tag_template) S3method(roxy_tag_parse,roxy_tag_templateVar) S3method(roxy_tag_parse,roxy_tag_title) S3method(roxy_tag_parse,roxy_tag_usage) S3method(roxy_tag_parse,roxy_tag_useDynLib) S3method(roxy_tag_rd,default) S3method(roxy_tag_rd,roxy_tag_.formals) S3method(roxy_tag_rd,roxy_tag_.methods) S3method(roxy_tag_rd,roxy_tag_.package) S3method(roxy_tag_rd,roxy_tag_.reexport) S3method(roxy_tag_rd,roxy_tag_author) S3method(roxy_tag_rd,roxy_tag_backref) S3method(roxy_tag_rd,roxy_tag_concept) S3method(roxy_tag_rd,roxy_tag_description) S3method(roxy_tag_rd,roxy_tag_details) S3method(roxy_tag_rd,roxy_tag_docType) S3method(roxy_tag_rd,roxy_tag_encoding) S3method(roxy_tag_rd,roxy_tag_evalRd) S3method(roxy_tag_rd,roxy_tag_example) S3method(roxy_tag_rd,roxy_tag_examples) S3method(roxy_tag_rd,roxy_tag_examplesIf) S3method(roxy_tag_rd,roxy_tag_family) S3method(roxy_tag_rd,roxy_tag_field) S3method(roxy_tag_rd,roxy_tag_format) S3method(roxy_tag_rd,roxy_tag_includeRmd) S3method(roxy_tag_rd,roxy_tag_inherit) S3method(roxy_tag_rd,roxy_tag_inheritDotParams) S3method(roxy_tag_rd,roxy_tag_inheritParams) S3method(roxy_tag_rd,roxy_tag_inheritSection) S3method(roxy_tag_rd,roxy_tag_keywords) S3method(roxy_tag_rd,roxy_tag_note) S3method(roxy_tag_rd,roxy_tag_param) S3method(roxy_tag_rd,roxy_tag_prop) S3method(roxy_tag_rd,roxy_tag_rawRd) S3method(roxy_tag_rd,roxy_tag_references) S3method(roxy_tag_rd,roxy_tag_return) S3method(roxy_tag_rd,roxy_tag_returns) S3method(roxy_tag_rd,roxy_tag_section) S3method(roxy_tag_rd,roxy_tag_seealso) S3method(roxy_tag_rd,roxy_tag_slot) S3method(roxy_tag_rd,roxy_tag_source) S3method(roxy_tag_rd,roxy_tag_title) S3method(roxy_tag_rd,roxy_tag_usage) export(block_get_tag) export(block_get_tag_value) export(block_get_tags) export(block_has_tags) export(env_file) export(env_package) export(escape_examples) export(is_s3_generic) export(is_s3_method) export(load_installed) export(load_options) export(load_pkgload) export(load_source) export(namespace_roclet) export(needs_roxygenize) export(object) export(object_format) export(parse_file) export(parse_package) export(parse_text) export(rd_roclet) export(rd_section) export(roc_proc_text) export(roclet) export(roclet_clean) export(roclet_find) export(roclet_output) export(roclet_preprocess) export(roclet_process) export(roclet_tags) export(roxy_block) export(roxy_meta_get) export(roxy_tag) export(roxy_tag_parse) export(roxy_tag_rd) export(roxy_tag_warning) export(roxygenise) export(roxygenize) export(tag_code) export(tag_examples) export(tag_inherit) export(tag_markdown) export(tag_markdown_with_sections) export(tag_name) export(tag_name_description) export(tag_toggle) export(tag_two_part) export(tag_value) export(tag_words) export(tag_words_line) export(tags_list) export(tags_metadata) export(update_collate) export(vignette_roclet) export(warn_roxy_tag) import(rlang) importFrom(R6,R6Class) importFrom(knitr,knit) importFrom(knitr,opts_chunk) importFrom(lifecycle,deprecated) importFrom(stats,setNames) importFrom(utils,URLdecode) importFrom(utils,URLencode) importFrom(utils,head) importFrom(utils,tail) importFrom(xml2,xml_attr) importFrom(xml2,xml_children) importFrom(xml2,xml_contents) importFrom(xml2,xml_find_all) importFrom(xml2,xml_name) importFrom(xml2,xml_ns_strip) importFrom(xml2,xml_text) importFrom(xml2,xml_type) useDynLib(roxygen2, .registration=TRUE) roxygen2/LICENSE0000644000176200001440000000005614520730024013032 0ustar liggesusersYEAR: 2023 COPYRIGHT HOLDER: roxygen2 authors roxygen2/NEWS.md0000644000176200001440000020613715174647772013162 0ustar liggesusers# roxygen2 8.0.0 * roxygen2 now requires R 4.1 (#1632) and no longer depends on purrr, stringr, or stringi. As a result, no package in the devtools constellation depends on stringr, which means you no longer need stringi, making it a bit easier to install in constrained Linux environments. * All generated links now share the same style and code path. This will produce some minor differences when you re-document, but links will be more consistent overall (#1792). * roxygen2 options can now be set using `Config/roxygen2/` fields in `DESCRIPTION` (e.g. `Config/roxygen2/markdown: TRUE`) instead of the `Roxygen` field. The old `Roxygen` field is still supported. Similarly, the roxygen2 version is now stored in `Config/roxygen2/version` instead of `RoxygenNote` (#1328). roxygen2 will migrate this automatically the next time you document. * `needs_roxygenize()` provides a lightweight check that man pages are up-to-date by comparing modification times of `.Rd` files with their source files (#1411). * `roxygenize()` is no longer very slow when the package contains large non-function objects like datasets (#1720). * `vignette("rd-other")` has been split into individual vignettes: `vignette("rd-datasets")`, `vignette("rd-packages")`, `vignette("rd-S3")`, `vignette("rd-S4")`, and `vignette("rd-R6")`. `vignette("rd")` has been renamed to `vignette("rd-functions")`, and its basics section has been moved to `vignette("roxygen2")`. ## Markdown improvements * `` `Rd expr` `` inline code now generates `\Sexpr[stage=render,results=rd]{expr}`, providing a convenient syntax for evaluating R code at documentation render time (#1214). * Indented bullet lists in `@param` and other two-part tags are no longer incorrectly nested (#1102). * Horizontal rules (e.g. `----`) now generate a clear warning instead of an internal error about an unknown `thematic_break` XML node (#1707). * Inline R code (`` `r expr` ``) in non-indented list continuation lines no longer causes an error (#1651). * Link text now supports non-code markup like bold and italic, e.g. `[*italic text*][func]` generates `\link[=func]{\emph{italic text}}`, matching the markup support added to `\link` in R 4.5.0. * Links now do a better job of resolving package names, and the process is cached for better performance (#1724); infix operators (e.g. `[%in%]`) can now be linked (#1728); custom link text is better preserved (#1662); and base packages are included when reporting ambiguous functions (#1725). Links to external packages now use the topic alias instead of the Rd file name as the anchor, which fixes "Non-topic package-anchored link(s)" notes from `R CMD check` (#1709). ## Data/value docs * Documenting values (e.g. `x <- 1:10`) no longer adds `\docType{data}`, `\keyword{datasets}`, or a `\format{}` section (#1666). To document a dataset, use the approach introduced in 2013: place a roxygen block above a string that names the dataset (e.g. `"diamonds"`). * The automatic usage for a data object now includes `data()` when the package doesn't have `LazyData: true` in its `DESCRIPTION` (#1425). * `object_format()` now escapes braces in class names, fixing broken Rd output for data objects with class `{` (like `quote({})`) (#1744). ## Package docs * DOIs containing percent-encoded characters (e.g. `%3C`) in `DESCRIPTION` no longer generate invalid Rd (#1321). * Multiple comments in the `comment` argument of `person()` in `Authors@R` are now handled correctly (#1746). * Multiple email addresses in `Authors@R` now generate separate `\email{}` tags (#1689). * People with both `"aut"` and `"cre"` roles are now listed in both the Maintainer and Authors sections (#1588). * Only values that look like URLs in the `URL` field of `DESCRIPTION` are wrapped in `\url{}` (#1420). * The package logo now prefers `logo.svg` over `logo.png` when both are available (#1640). ## S3 * `vignette("rd-S3")` now includes improved advice for documenting S3 generics, classes, and methods, including how to use the new [doclisting](https://doclisting.r-lib.org/) package to automatically list methods for a generic (#1513). * Methods of `all.equal()` (e.g. `all.equal.numeric`) are no longer incorrectly identified as methods of `all()` (#1587). * The warning about undocumented methods no longer errors when the function lacks a srcref, e.g. because a debugger breakpoint is set (#1589, #1710). * The warning about undocumented methods no longer incorrectly flags S4 methods of S3 generics as unexported (#1715). ## R6 docs There are two new ways to document methods outside of the class definition. Methods added via `$set()` can now be documented with a roxygen block placed directly above the `$set()` call (#931). `@R6method Class$method` lets you document R6 methods anywhere, for cases where they are generated in a way that roxygen2 doesn't currently recognize (#991). You can also suppress documentation for individual fields and methods: `@noRd` before an R6 method suppresses its documentation, and `@field name NULL` suppresses documentation for a field or active binding (#1067). Classes now inherit more from their parents (#996): * Methods automatically inherit parameter documentation from overridden superclass methods. * Fields and active bindings inherit documentation from superclasses. There were also a large number of bug fixes and minor improvements: * Inherited method links now only link to parent classes that have documentation, preventing broken links to undocumented parents (#963, #1155). * R6 classes with only active bindings and `cloneable = FALSE` no longer error during documentation (#1610). * R6 method examples shown in method subsections now strip `\dontrun{}`, `\donttest{}`, and `\dontshow{}` wrappers, since these Rd macros are not interpreted inside `\preformatted{}` blocks (#1072). * The "Super classes" section now omits the `pkg::` prefix for parent classes from the same package, making the inheritance chain easier to read (#1567). * Method usage now shows `ClassName$new(args)` for constructors and `obj$method(args)` for other methods, making it clearer how each method is actually called (#1026). * `@description` and `@details` for R6 methods now support markdown headings (#1647). * `@example` (singular, with a file path) now works correctly in R6 class documentation (#1158). * `@field` with comma-separated names (e.g. `@field var_1,var_2 description`) no longer produces spurious warnings about undocumented active bindings or fields (#1600). * `@returns` now works as a method-level tag in R6 classes, just like `@return` (#1148). * `initialize()` method parameters now automatically inherit documentation from `@field` tags with the same name, so you don't need to duplicate descriptions. Explicit `@param` tags still take precedence (#1004). ## S7 docs Added initial support for S7 classes, generics, and methods (#1484): * S7 generics are documented like regular functions. * S7 classes are documented like regular functions, but you can use `@prop` to document additional properties that are not constructor parameters. If multiple classes share one page, use `@prop ClassName@prop_name description` to group properties by class. * S7 methods registered with `method(generic, class) <- fn` are detected automatically and generate usage of the form `## S7 method for class `. See `vignette("rd-S7")` for best practices. ## Individual tags * Tags that expect single-line input now warn when they span multiple lines, catching a common class of mistake. Affected tags: `@aliases`, `@concept`, `@encoding`, `@exportClass`, `@exportMethod`, `@exportPattern`, `@exportS3Method`, `@importFrom`, `@importClassesFrom`, `@importMethodsFrom`, `@include`, `@includeRmd`, `@inheritDotParams`, `@inheritParams`, `@inheritSection`, `@keywords`, `@method`, `@name`, `@order`, `@rdname`, `@S3method`, `@template`, and `@useDynLib` (#1642, #1688). This may break some existing usage, but it prevents a wide range of otherwise silent errors. * Reexported functions now display with `()` appended (e.g. `fun()` instead of `fun`) on the reexports page, except for infix operators like `%>%` (#1222). They also use the modern (>= 4.1.0) linking style. * `@description` no longer errors when the markdown text starts with a heading (#1705). * `@examples` no longer warns about unmatched braces inside raw strings, or inside strings within R comments, e.g. `# '{greeting}'` (#1492). * `@examplesIf` now warns if there is no example code after the condition (#1695). * `@family` tags no longer generate duplicate "See also" entries when multiple blocks share the same `@rdname` (#1530), and no longer add a trailing space after the colon in the default family prefix (#1628). Custom `rd_family_title` values now automatically get a colon appended if they don't already end with one (#1656). * `@inheritDotParams` generates an informative warning when the source function can't be found, instead of a cryptic error (#1602). It also warns and produces no output when there are no parameters to inherit, instead of generating an empty `\describe` block that causes HTML validation warnings (#1671). * `@inheritDotParams` now uses documented parameters rather than formals, so it works the same way as `@inheritParams` (#1840). This may introduce new false positives (replacing the old approach's false negatives), which you can prevent by explicitly listing the argument names to inherit. * `@inheritDotParams` now correctly matches parameters documented with a dot-prefixed alias (e.g. `.by, by`) whose formal argument lacks the dot (e.g. `by`), as is common in the tidyverse (#1826). * `@inheritParams` now supports argument filtering using the same syntax as `@inheritDotParams`. For example, `@inheritParams foo x y` inherits only `x` and `y`, and `@inheritParams foo -z` inherits everything except `z` (#1849). * `@inheritParams` now correctly inherits parameters that are documented together with `\dots` using comma-separated names, e.g. `@param b,\dots description` (#1718). * `@inheritParams` now correctly updates `\linkS4class{}` links when inheriting parameter documentation from other packages, converting them to absolute links (#1634). * `@param` (and other two-part tags) now correctly handles backtick-quoted names that contain spaces, e.g. `` @param `arg 1` description `` (#1696). * `tag_words_line()` is deprecated in favor of `tag_words()`, which now checks for single-line content by default. Use `tag_words(x, multiline = TRUE)` or `tag_value(x, multiline = TRUE)` if your tag legitimately spans multiple lines. # roxygen2 7.3.3 * Package documentation now converts ROR IDs into a useful link (#1698, @maelle). * The check for unexported S3 methods was improved, so it does not hang any more if a largish data object is in the package (#1593, @jranke). * Custom [`@family` titles](https://roxygen2.r-lib.org/articles/index-crossref.html) now support Markdown syntax (#1608, @salim-b). * Unqualified markdown links to topics in external packages are now automatically resolved (#1612). * `@examplesIf` always uses `withAutoprint()` (@MichaelChirico, #1581). The raw Rd will be cleaner, but such examples will now only run under R>=3.4.0 (2017). # roxygen2 7.3.2 * `@includeRmd` now additionally sets `options(cli.hyperlink = FALSE)` to make code run in included `.Rmd`s even more consistent across sessions (#1620). # roxygen2 7.3.1 * S3 method export warning no longer fails if class contains `{` or `}` (#1575). * `@family` lists are now ordered more carefully, "foo1" comes after "foo" (#1563, @krlmlr). * `@importFrom` works again for quoted non-syntactic names, e.g. `@importFrom magrittr "%>%"` or ``@importFrom rlang `:=` `` (#1570, @MichaelChirico). The unquoted form `@importFrom magrittr %>%` continues to work. Relatedly, `@importFrom` directives matching no known functions (e.g. `@importFrom utils plot pdf`) produce valid NAMESPACE files again. * Multi-line `@rawNamespace` no longer break re-runs of `namespace_roclet()` (#1572, @MichaelChirico). # roxygen2 7.3.0 ## New features * `@docType package` now works more like documenting `"_PACKAGE"`, creating a `{packagename}-package` alias and clearly suggesting that you should switch to `"_PACKAGE"` instead (#1491). * `_PACKAGE` will no longer generate an alias for your package name if a function of the same name exists (#1160). * The NAMESPACE roclet now reports if you have S3 methods that are missing an `@export` tag. All S3 methods need to be `@export`ed (which confusingly really registers the method) even if the generic is not. This avoids rare, but hard to debug, problems (#1175). You can suppress the warning with `@exportS3Method NULL` (#1550). * The `NAMESPACE` roclet once again regenerates imports _before_ loading package code and parsing roxygen blocks. This has been the goal for a long time (#372), but we accidentally broke it when adding support for code execution in markdown blocks. This resolves a family of problems where you somehow bork your `NAMESPACE` and can't easily get out of it because you can't re-document the package because your code doesn't reload. ## Minor improvements and bug fixes * If you document a function from another package it is automatically imported. Additionally, if you set `@rdname` or `@name` you can opt out of the default `reexports` topic generation and provide your own docs (#1408). * Generate correct usage for S4 methods with non-syntactic class names. * The `ROXYGEN_PKG` env var provides the name of the package being documented (#1517). * `@describeIn foo` now suggests that you might want `@rdname` instead (#1493). It also gives a more informative warning if you use it with an unsupported type (#1490). * In `DESCRIPTION`, URLs containing escapes in `URL` and `BugReports` are now correctly handled (@HenningLorenzen-ext-bayer, #1415). Authors can now have multiple email addresses (@jmbarbone, #1487). * `escape_examples()` is now exported (#1450). * `@exportS3Method` provides the needed metadata to generate correct usage for S3 methods, just like `@method` (#1202). * `is_s3_generic()` now ignores non-function objects when looking for a candidate function. I believe this is closer to how R operates. * `@import` and friends are now ignored if they try to import from the package being documented. This is useful to add self-dependencies in standalone files meant to be used in other packages (r-lib/usethis#1853). * `@importFrom` throws a friendlier error if you try and import a non-existing functions (@MichaelChirico, #1409). * `@include` now gives an informative warning if you use a path that doesn't exist (#1497). * `@inherit` can now also inherit from `@format` (#1293). # roxygen2 7.2.3 * roxygen2 now supports HTML blocks in markdown. They are only included in the HTML manual. They can also be produced as the output of code chunks. * Improved support for RStudio IDE. # roxygen2 7.2.2 * `@includeRmd` calls `local_reproducible_output()` to make code run in included `.Rmd`s more consistent with other sources (#1431). * Fix duplicated argument in `roxy_block()` to avoid CRAN removal. # roxygen2 7.2.1 ## Tags * All built-in tags are now documented so that you can do (e.g.) `?"@param"` to get a basic description of `@param` and a pointer where to learn more (#1165). This is powered by a new `tags_list()` lists all tags defined by roxygen2 and `tags_metadata()` provides some useful information about them for use by (e.g.) IDEs (#1375). * `@describeIn` can now be used to combine more types of functions (generics, methods and other functions) into a single topic. The resulting section organises the functions by type (#1181) and displays methods like function calls. Methods are recognized only if they extend the generic in the destination,or if the destination can heuristically be identified as a constructor. * Code evaluated in inline markdown code chunks and `@eval`/`@evalRd`/ `@evalNamespace` is now evaluated in an environment designed to be more reproducible and to suppress output that won't work in Rd (e.g. turning off colour and unicode support in cli) (#1351). They now also set knitr options `comment = #>` (#1380) and `collapse = TRUE` (#1376). * `@export` will now export both the class and constructor function when applied to expressions like `foo <- setClass("foo")` (#1216). * `@includeRmd` now gives better feedback when it fails (#1089). ## (R)markdown * New `knitr_chunk_options` option (in the `Roxygen` entry of `DESCRIPTION` or in `man/roxygen/meta.R`) is added to the knitr chunk options that roxygen2 uses for markdown code blocks and inline code (#1390). * PDF figures are only included the PDF manual, and SVG figures are only included in the HTML manual (#1399). * You can now use alternative knitr engines in markdown code blocks (#1149). * Generated HTML for code blocks never includes "NA" for language (#1251). * Using a level 1 heading in the wrong tag now gives a more useful warning (#1374). * Fix bug interpolating the results of indented inline RMarkdown (#1353). ## Other * If you have a daily build of RStudio, the lists of changed Rd files are now clickable so you can immediately see the rendered development documentation (#1354). * R6 documentation no longer shows inherited methods if there aren't any (#1371), and only links to superclass docs if they're actually available (#1236). * Automated usage no longer mangles nbsp in default arguments (#1342). # roxygen2 7.2.0 ## New features * The NAMESPACE roclet now preserves all existing non-import directives during it's first pre-processing pass. This eliminates the "NAMESPACE has changed" messages and reduces the incidence of namespace borking (#1254). * `@inheritParams` now only inherits exact multiparameter matches, so if you're inheriting from a function with `@param x,y` you'll only get the parameter documentation if your function needs docs for both x and y (#950). * All warning messages have been reviewed to be more informative and actionable (#1317). `@title` now checks for multiple paragraphs. `@export` gives a more informative warning if it contains too many lines. (#1074). All tags warn now if only provide whitespace (#1228), and problems with the first tag in each block are reported with the correct line number (#1235). * If you have a daily build of RStudio, roxygen2 warnings will now include a clickable hyperlink that will take you directly to the problem (#1323). This technology is under active development across the IDE and the cli package but is extremely exciting. ## Minor improvements and bug fixes * roxygen2 can once again read UTF-8 paths on windows (#1277). * `@author`s are de-duplicated in merged documentation (@DanChaltiel, #1333). * `@exportS3Method pkg::generic` now works when `pkg::generic` isn't imported by your package (#1085). * `@includeRmd` is now adapted to change in rmarkdown 2.12 regarding math support in `github_document()` (#1304). * `@inherit` and friends perform less aggressive link tweaking, eliminating many spurious warnings. Additionally, when you do get a warning, you'll now always learn which topic it's coming from (#1135). Inherited `\ifelse{}{}{}` tags are now inserted correctly (without additional `{}`) (#1062). * `@inherit` now supports inheriting "Notes" with `@inherit pkg::fun note` (@pat-s, #1218) * Automatic `@usage` now correctly wraps arguments containing syntactically significant whitespace (e.g anonymous functions) (#1281) and non-syntactic values surrounded by backticks (#1257). * Markdown: * Code blocks are always wrapped in `
    ` even if the language is unknown (#1234). * Links with markup (e.g. ``[foo `bar`][target]``) now cause an informative warning instead of generating invalid Rd. * Curly braces in links are now escaped (#1259). * Inline R code is now powered by knitr. Where available, (knit) print methods are applied (#1179). This change alters outputs and brings roxygen in line with console and R markdown behavior. `x <- "foo"` no longer inserts anything into the resulting documentation, but `x <- "foo"; x` will. This also means that returning a character vector will insert commas between components, not newlines. * roxygen2 no longer generates invalid HTML (#1290). * DOIs, arXiv links, and urls in the `Description` field of the `DESCRIPTION` are now converted to the appropriate Rd markup (@dieghernan, #1265, #1164). DOIs in the `URL` field of the `DESCRIPTION` are now converted to Rd's special `\doi{}` tag (@ThierryO, #1296). # roxygen2 7.1.2 * The new `@examplesIf` tag can be used to create conditional examples. These examples only run if a specified condition holds (#962). * roxygen2 is now licensed as MIT (#1163). * Bug fix for upcoming stringr 2.0.0 release. * Code blocks with language now add `sourceCode` to the generated div; this makes syntax highlighting more consistent across downlit/pandoc/knitr/roxygen2. * Percent signs in markdown link targets, e.g. `[text](https://foo/ba%20r)` are now handled correctly (#1209). # roxygen2 7.1.1 * When processing cross package markdown links (e.g. `[pkg::fun()]`), roxygen2 now looks up the file it needs to link to, instead of linking to the topic, to avoid "Non-file package-anchored links" `R CMD check` warnings. * R6 methods and re-exported functions are always sorted in the C locale; this ensures they're always sorted the same way in every environment (#1077). * roxygen2 now supports inline markdown code and code chunks inside Rd tags. In particular in `\out{}` (#1115). # roxygen2 7.1.0 ## New features * roxygen2 now supports inline markdown code and also code chunks, using the same notation as the knitr package. For example: ```R #' This manual was generated at: `r Sys.time()`. #' ... #' `mtcars` is a data frame with `r ncol(mtcars)` columns, here #' is a summary of them: #' #' ```{r} #' summary(mtcars) #' ``` ``` See `vignette("rd-formatting")` for details. * roxygen2 now keeps using Windows (CR LF) line endings for files that already have CR LF line endings, and uses LF for new files (#989). ## Minor improvements and bug fixes * Auto-generated package documentation can now handle author ORCID comments containing full url (#1040). * Hyperlinks to R6 methods are also added in the PDF manual (#1006). * Empty annotations (alternate text) for figures added via markdown are now omitted. This caused issues when generating pkgdown web sites (#1051). * Roxygen metadata can now have a `packages` element, giving a character vector of package names to load. This makes it easier to use extension package that provide new tags for existing roclets (#1013). See `?load_options` for more details. ```yaml Roxygen: list(markdown = TRUE, packages = "roxygenlabs") ``` * `@evalNamespace()` works again (#1022). * `@description NULL` and `@details NULL` no longer fail; instead, these tags are ignored, except for `@description NULL` in package level documentation, where it can be used to suppress the auto-generated Description section (#1008). * Multiple `@format` tags are now combined (#1015). * The warning for `@section` titles spanning multiple lines now includes a hint that you're missing a colon (@maelle, #994). * Can now document objects created with `delayedAssign()` by forcing evaluation at documentation time (#1041) # roxygen2 7.0.2 * `\example{}` escaping has been improved (again!) so that special escapes within strings are correctly escaped (#990). # roxygen2 7.0.1 * `@includeRmd` has now an optional second argument, the top level section the included file will go to. It defaults to the details section (#970). Code chunks are now evaluated in a child of the global environment (#972). * `@inheritParams` does a better job of munging links. Links of the form `\link[=topic]{text}` are now automatically converted to `\link[pkg:topic]{text}` when inherited from other packages (#979). Internal `has_topic()` helper has a better implementation; this means that links should no longer be munged unnecessarily (#973). * `\example{}` escaping has been considerably simplified (#967), and is now documented in `escape_example()`. * In `\usage{}`, S3/S4 methods are no longer double-escaped (#976). * Markdown tables with cells that contain multiple elements (e.g. text and code) are now rendered correctly (#985). * Markdown code blocks containing operators and other special syntax (e.g. `function`, `if`, `+`) now converted to `\code{}` not `\verb{}` (#971). # roxygen2 7.0.0 ## New features ### New tags * `@includeRmd {path.Rmd}` converts an `.Rmd`/`.md` file to `.Rd` and includes it in the manual page. This allows sharing text between vignettes, `README.Rmd`, and the documentation. See `vignette("rd-functions")` for details (#902). * `@order {n}` tag controls the order in which blocks are processed. You can use it to override the usual ordering which proceeds from the top of each file to the bottom. `@order 1` will be processed before `@order 2`, and before any blocks that don't have an explicit order set (#863). * `@exportS3Method` tag allows you to generate `S3method()` namespace directives (note the different in capitalisation) (#796). Its primary use is for "delayed" method registration, which allows you to define methods for generics found in suggested packages (available in R 3.6 and greater). For example, ```R #' @exportS3Method package::generic generic.foo <- function(x, ...) { } ``` will generate ``` S3method(package::generic, foo) ``` (See [`vctrs::s3_register()`](https://vctrs.r-lib.org/reference/s3_register.html) you need a version that works for earlier versions of R). It also has a two argument form allows you generate arbitrary `S3method()` directives: ```R #' @exportS3Method generic class NULL ``` ``` S3method(generic, class) ``` * New `@returns` is an alias for `@return` (#952). ### R6 roxygen2 can now document R6 classes (#922). See `vignette("rd-R6")` for details. ### Markdown improvements * Rd comments (`%`) are now automatically escaped. You will need to replace any existing uses of `\%` with `%` (#879). * Markdown headings are supported in tags like `@description`, `@details`, and `@return` (#907, #908). Level 1 headings create a new top-level `\section{}`. Level 2 headings and below create nested `\subsections{}`. * Markdown tables are converted to a `\tabular{}` macro (#290). roxygen2 supports the [GFM table syntax](https://github.github.com/gfm/#tables-extension-) which looks like this: ```md | foo | bar | | --- | --- | | baz | bim | ``` * Markdown code (``` `foofy` ```) is converted to to either `\code{}` or `\verb{}`, depending on whether it not it parses as R code. This better matches the description of `\code{}` and `\verb{}` macros, solves a certain class of escaping problems, and should make it easier to include arbitrary "code" snippets in documentation without causing Rd failures (#654). * Markdown links can now contain formatting, e.g. `[*mean*][mean]` will now generate `\link[=mean]{\emph{mean}}`. * Use of unsupported markdown features (e.g. blockquotes, inline HTML, and horizontal rules) generates informative error messages (#804). ### Default usage * The default formatting for function usage that spans multiple lines has now changed. Previously, the usage was wrapped to produce the smallest number of lines, e.g.: ```R parse_package(path = ".", env = env_package(path), registry = default_tags(), global_options = list()) ``` Now it is wrapped so that each argument gets its own line (#820): ```R parse_package( path = ".", env = env_package(path), registry = default_tags(), global_options = list() ) ``` If you prefer the old behaviour you can put the following in your `DESCRIPTION`: ``` Roxygen: list(old_usage = TRUE) ``` ### Code loading roxygen2 now provides three strategies for loading your code (#822): * `load_pkgload()`, the default, uses [pkgload](https://github.com/r-lib/pkgload). Compared to the previous release, this now automatically recompiles your package if needed. * `load_source()` attaches required packages and `source()`s all files in `R/`. This is a cruder simulation of package loading than pkgload (and e.g. is unreliable if you use S4 extensively), but it does not require that the package be compiled. Use if the default strategy (used in roxygen2 6.1.0 and above) causes you grief. * `load_installed()` assumes you have installed the package. This is best used as part of a bigger automated workflow. You can override the default either by calling (e.g.) `roxygenise(load_code = "source"))` or by setting the `load` option in your DESCRIPTION: `Roxygen: list(load = "source")`. ### Options * As well as storing roxygen options in the `Roxygen` field of the `DESCRIPTION` you can now also store them in `man/roxygen/meta.R` (#889). The evaluation of this file should produce a named list that maps option names to values. * roxygen now also looks for templates in `man/roxygen/templates` (#888). * New `rd_family_title` option: this should be a named list, and is used to overrides the default "Other family: " prefix that `@family` generates. For example, to override the prefix generated by `@family foo` place `rd_family_title <- list(foo = "Custom prefix: ")` in `man/roxygen/meta.R` (#830, @kevinushey). ## Breaking changes * Rd comments (`%`) are automatically escaped in markdown formatted text. This is a backward incompatible change because you will need to replace existing uses of `\%` with `%` (#879). * Using `@docType package` no longer automatically adds `-name`. Instead document `_PACKAGE` to get all the defaults for package documentation, or use `@name` to override the default file name. * `@S3method` has been removed. It was deprecated in roxygen2 4.0.0 released 2014-05-02, over 5 years ago. * Using the old `wrap` option will now trigger a warning, as hasn't worked for quite some time. Suppress the error by deleting the option from your `DESCRIPTION`. ### Extending roxygen2 The process for extending roxygen2 with new tags and new roclets has been completely overhauled, and is now documented in `vignette("extending")`. If you're one of the few people who have written a roxygen2 extension, this will break your code - but the documentation, object structure, and print methods are now so much better that I hope it's not too annoying! Because this interface is now documented, it will not change in the future without warning and a deprecation cycle. If you have previously made a new roclet, the major changes are: * The previously internal data structures used to represent blocks and tags have been overhauled. They are now documented and stable. See `roxy_block()` and `roxy_tag()` for details. * `roclet_tags()` is no longer used; instead define a `roxy_tag_parse()` method. For example, if you create a new `@mytag` tag, it will generate a class of `roxy_tag_mytag`, and will be parsed by `roxy_tag_parse.roxy_tag_mytag()` method. The method should return a new `roxy_tag()` object with the `val` field set. This means that the `registry` argument is no longer needed and has been removed. * `rd_section()` and `roxy_tag_rd()` are now exported so that you can more easily extend `rd_roclet()` with your own tags that generate output in `.Rd` files. * `global_options` is no longer passed to all roclet methods. Instead, use `roxy_meta_get()` to retrieve values stored in the options (#918). * `tag_two_part()` and `tag_words()` are now simple functions, not function factories. * `tag_markdown_restricted()` has been removed because it did exactly the same thing as `tag_markdown()`. A big thanks goes to @mikldk for starting on the vignette and motivating me to make the extension process much more pleasant (#882). ## Bug fixes and minor improvements * Empty roxygen2 lines at the start of a block are now silently removed (#710). * Whitespace is automatically trimmed off the `RoxygenNote` field when comparing the installed version of roxygen2 to the version used to generate the documentation (#802). * Files generated on Windows systems now retain their existing line endings, or use unix-style line endings for new files (@jonthegeek, @jimhester, #840). * roxygen2 now recognises fully qualified S4 functions like `methods::setGeneric()`, `methods::setClass()` and `methods::setMethod()` (#880). * Package documentation now converts ORCIDs into a useful link (#721). The package logo (if found at `man/images/logo.png`) is now scaled to 120px wide (@peterdesmet, #834). * Documenting an S4 method that has a `.local()` wrapper no longer fails with an obscure error message (#847). * Functions documented in `reexports` are now sorted alphabetically by package (#765). * `@describeIn` can now be used with any combination of function types (#666, #848). * `@description` and `@detail` tags are automatically generated from the leading description block, and now have correct line numbers (#917). * `@example` and `@examples` are interwoven in the order in which they appear (#868). * In `@examples`, escaped `'` and `"` in strings are no longer doubly escaped (#873). * `@family` automatically adds `()` when linking to functions (#815), and print each link on its own line (to improve diffs). * When `@inherit`ing from external documentation, `\link{foo}` links are automatically transformed to `\link{package}{foo}` so that they work in the generated documentation (#635). `\href{}` links in external inherited are now inserted correctly (without additional `{}`) (#778). * `@inherit`ing a a function with no arguments no longer throws a confusing error message (#898). * `@inheritDotParams` automatically ignores arguments that can't be inherited through `...` because they are used by the current function (@mjskay, #885). * `@inheritDotParams` includes link to function and wraps parameters in `\code{}` (@halldc, #842). * `@inheritDotParams` can be repeated to inherit dot docs from multiple functions (@gustavdelius, #767). * `@inheritDotParams` avoids multiple `...` arguments (@gustavdelius, #857). * `@inheritParams` ignores leading dots when comparing argument names (#862). * `@inheritParams` warns if there are no parameters that require documentation (#836). * `@param` containing only whitespace gives a clear warning message (#869). * Multiple `@usage` statements in a single block now generate a warning. Previously, the first was used without a warning. # roxygen2 6.1.1 * Now specifically imports recent version of desc package (>= 1.2.0) to fix various parsing issues (@crsh, #773, #777, #779). Multi-line DESCRIPTION collate directives now correctly parsed on windows (@brodieG, #790). * `roxygenise()` no longer recompiles packages containing src code (#784). * `roxygenise()` now stops with an informative error message when run in a directory that's not the package root (@mikmart, #704). # roxygen2 6.1.0 ## New features * The `NAMESPACE` roclet now works in two passes - it first generates the `NAMESPACE` containing only import directives because this can be generated without evaluating the code in the package. This alleviates a problem where it was previously possible to get into a state that you could only get out of by carefully editing the `NAMESPACE` by hand (#372). * `@evalRd foo()` evaluates `foo()` defined in the package namespace and inserts the results into the current block (#645). The code should return a character vector with one entry for each line (and they should not start with `#'`). There are two small limitations to the current implementation: 1. The generated roxygen will not affect the `@md`/`@noMd` status 2. `@evalRd` does not work inside templates. * `@evalNamespace` does for `NAMESPACE` what `@evalRd` does for Rd files: you give it R code that produces a literal entry in `NAMESPACE` when run. This should make it easier to export functions that are generated by other functions in your package (#531, @egnha). * `@inherits` can now inherit examples (#588). * `vignette("rd-functions")` received a thorough updating for current best-practices. The vignette still needs more work so pull requests are greatly appreciated (#650). * `roxygenise()` uses `pkgload::load_all()` instead of a home grown solution to simulate package loading (this is needed because roxygen2 uses run-time information to generate the documentation). This should reduce S4 related problems and ensures that `devtools::document()` and `roxygenise()` always have exactly the same behaviour (#568, #595). * If an inherited section cannot be found, the warning contains the help page from which that section was requested (#732, @krlmlr). * roxygen2 now always reads and writes using UTF-8 encoding. If used with a package that does not have `Encoding: UTF-8` in the DESCRIPTION, you'll now get a warning (#564, #592). ## Extension API * Roxygen blocks now have an official structure as encoded in `roxy_block()`. It is a named list containing the tags with attributes providing other metadata. * The `parsed` argument to `roclet_process()` have been replaced with separate `blocks` and `env` arguments. * New `roclet_preprocess()` generic makes it possible for roclets to perform actions before code is evaluated. * `parse_package()`, `parse_file()` and `parse_code()` provide an exported API that allows you to use roxygen's parsing code independently of creating roclets. ## Minor improvements and bug fixes * All tags (including `@alias`) are now de-duplicated and consistently sorted. This reduces spurious diffs (#586, @flying-sheep). * `@concept` now generates one `\concept` per tag (#611). * The default `@description` (i.e. the title) is now added much later in the process. That means that `@inherit description` now works when you have specified a title for the inheritor (#629) and the default description is slightly nicer when merging multiple blocks. * `@family` automatically adds its value to concepts (#611). * `@inherits`: The mechanism for extracting inherited Rd does a better job of preserving escapes (#624) * Empty `.Rbuildignore` now handled correctly (#576). * Stricter regular expression ensures only files ending with `.R` or `.r` are parsed for roxygen comments (#625). * Objects with names starting with a dot are now by default documented in files with prefix 'dot-'. * Roclets can now access global options as designed. This allows templates to use markdown formatting if set globally (#594). * You can now autogenerate package documentation even if you don't have `Authors@R` (#606). * Multiple given and/or family names are now supported in the `Authors@R` field of the DESCRIPTION file (#672, @sgibb). * If a package logo exists (`man/figures/logo.png`) it will be automatically included in generated package docs (#609). * Usage for data objects now correctly generated, avoiding double escaping other components of usage (#562). * Improvements to markdown translation: * Code in link text is now properly rendered as code (#620, @egnha). * Whitespace between words in link text is now preserved as single space for links of the form `[text][fcn]` and `[text](URL)` (#628, #754, #760, @egnha and @jennybc). * `%` in inline code (#640), code blocks (@nteetor, #699) and links (#724) is now automatically escaped. * Parsing of markdown links has been tweaked to reduce false positives (#555). If you still get a false positive, you can now put `\\` in front of the `[` to avoid it being converted to a link (#720). Links can no longer be followed by `{` to avoid spurious matches to Rd commands like `\Sexpr{}`. * Unsupported markdown features now generate a mildly helpful warning instead of throwing an utterly useless error (#560). * `person()` now supports all [MARC Relator](https://www.loc.gov/marc/relators/relaterm.html) role codes (#662, @publicus). * `topic_add_usage()` now outputs formatted "Usage" section with max width of 80 characters thanks to a now more flexible `wrap_string()` (@JoshOBrien, #719). # roxygen2 6.0.1 * Allowing empty lines in .Rbuildignore. Previously, empty lines caused all files to be ignored. (#572, @jakob-r) * Automatically generating a usage section for an infix function containing "<-" no longer removes "<-" from the function name (#554). # roxygen2 6.0.0 ## Markdown * Most fields can now be written using Markdown markup instead of the traditional Rd language. You can turn on Markdown globally by adding `Roxygen: list(markdown = TRUE)` to `DESCRIPTION`. The `@md` / `@noMd` tags turn Markdown parsing on / off for the given block. See `vignette("markdown")` for more details (#364, #431, #499, #506, #507), by @gaborcsardi ## Improved inheritance * New `@inheritDotParams` allows you to automatically generate parameter documentation for `...` for the common case where you pass `...` on to another function (#512). Because you often override some arguments, it comes with a flexible specification for argument selection: * `@inheritDotParams foo` takes all parameters from `foo()` * `@inheritDotParams foo a b e:h` takes parameters `a`, `b`, and all parameters between `e` and `h` * `@inheritDotParams foo -x -y` takes all parameters except for `x` and `y`. The documentation generated is similar to the style used in `?plot` and will eventually be incorporated in to RStudio's autocomplete. * New `@inherit` generalises `@inheritParams`, and allows to you inherit parameters, return, references, title, description, details, sections, and seealso. The default `@inherit my_fun` will inherit all, you can document an object entirely by specifying only the `@inherit` tag. Alternatively, you can select specific tags to inherit with `@inherit my_fun return params` (#384). * New `@inheritSection fun title` allows you to inherit the contents of a single section from another topic (#513). * `@inheritParams` now works recursively, so that you can inherit parameters from a function that inherited its parameters from somewhere else. It also better handles `\dots` as an alias for `...` (#504). ## Minor improvements and bug fixes ### Tags * `@aliases` are no longer sorted alphabetically, but instead match the order of their usage. This gives you more control in pkgdown. * `@describeIn` now escapes special characters in function names (#450). * `@family` see alsos are added in the same order they appear, not alphabetically (#315). Fixed an issue where `.`s were sometimes added between words within a `@family` tag (#477, @kevinushey). * `@author` is rendered after `@seealso`. * `@example` gives a nice warning message if you accidentally use it instead of `@examples` (#494). Multiple `@examples` sections are merged (#472, @krlmlr). * Roxygen will no longer write out topics that don't have a name or title, and will instead generate a warning. This makes it easier to detect if you've accidentally used `@rdname` with an incorrect value (#474). ### S3 * Non-primitive, internal S3 generics (e.g. 'rbind', 'cbind') are now properly detected as S3 generics. (#488, @kevinushey) * Ensure that `functions` with S3 class are still treated as functions (#455). * S3 method declarations via `R.methodS3::setMethodS3()` and function declarations via `R.oo::setConstructorS3()` are now supported (@HenrikBengtsson, #525). ### S4 * You can now document `setClassUnion()`s (#514). * The default alias for S4 method now re-adds trailing ANY signatures that are sometimes dropped (#460). * Back references are now wrapped over multiple lines, if long (#493, @LiNk-NY). ### Other * `"_PACKAGE"` documentation now generates a default `@seealso` combining the `URL` and `BugReport` fields, and a default `@author` field generated from the `Authors@R` field (#527). It now works from `roxygenise()`; before it only worked from `devtools::document()` (#439, @krlmlr). * Manually created `NAMESPACE` or documentation files are never overwritten, even if using `roxygen2` for the first time (@krlmlr, #436). * Changes to DESCRIPTION (i.e. `Collate:` and `RoxygenNote`) now use the desc package. This will minimise spurious changes (#430). * `default_data_format()` has been renamed to `object_format()`. * New `roclet_find()` provides a more flexible way to specify roclets: as roclet name (e.g. "rd_roclet"), in an package ("foo::roclet_bar"), or with options ("foo::roclet_bar(baz = TRUE)"). * The usage of replacement functions uses non-breaking spaces so that `<-` will never get put on its own line (#484). * Roxygen now parses nonASCII documentation correctly (as long as UTF-8 encoded or specified Encoding in DESCRIPTION) (#532, @shrektan), and ignores files listed in `.Rbuildignore` (#446, @fmichonneau). ## Extending roxygen2 * Deprecated `register.preref.parser()` and `register.preref.parsers()` have been removed. `register_tags()` has also been removed in favour of a new `roclet_tags()` generic. * `roclet()` (the constructor), `roclet_tags()`, `roclet_process()` `roclet_output()`, `roc_clean()` and now exported making it possible to create roclets in other packages. Helper functions `roxy_tag()` and `roxy_tag_warning()` are also exported. * `new_roclet()` is no longer exported - use `roclet()` instead. # roxygen2 5.0.1 * Use `ls()`, not `names()` to list elements of environment: fixes R 3.1.0 incompatibility (#422, @kevinushey). * `@export` again allows trailing new line (#415). * Fixed bug in `@noRd`, where usage would cause error (#418). # roxygen2 5.0.0 ## New features * Roxygen now records its version in a single place: the `RoxygenNote` field in the `DESCRIPTION` (#338). This will be the last time an roxygen2 upgrade changes every file in `man/`. * You can now easily re-export functions that you've imported from another package: ```R #' @export magrittr::`%>%` ``` All imported-and-re-exported functions will be documented in the same file (`rexports.Rd`), containing a brief description and links to the original documentation (#376). * You can more easily generate package documentation by documenting the special string "_PACKAGE" (@krlmlr, #349): ```R #' @details Details "_PACKAGE" ``` The title and description will be automatically filled in from the `DESCRIPTION`. * New tags `@rawRd` and `@rawNamespace` allow you to insert raw (unescaped) in Rd and the `NAMESPACE` (this is useful for conditional imports). `@evalRd()` is similar, but instead of literal Rd, you give it R code that produces literal Rd code when run. This should make it easier to experiment with new types of output (#385). * roxygen2 now parses the source code files in the order specified in the `Collate` field in `DESCRIPTION`. This improves the ordering of the generated documentation when using `@describeIn` and/or `@rdname` split across several `.R` files, as often happens when working with S4 (#323, #324). ## Minor features and bug fixes * The contents of documented functions are now also parsed for roxygen comments. This allows, e.g., documenting a parameter's type close to where this type is checked, or documenting implementation details close to the source, and simplifies future extensions such as the documentation of R6 classes (#397, @krlmlr). * Data objects get a simpler default `@format` that describes only the object's class and dimensions. The former default, generated by generated by `str()`, didn't usually produce useful output and was quite slow. The new S3 generic `default_data_format()` generates the format and can be overridden to generate a custom format (#410, @krlmlr). * The roxygen parsers has been completely rewritten in C++ (#295). This gives a nice performance boost and gives: * Better error messages: you now get the exact the line number of the tag, not just the start of the block. * The parser has been simplified a little: tags now must always start on a new line. This is recommended practice anyway, and it means that escaping inline `@` (with `@@`) is now optional. (#235) * Unknown tags now emit a warning, rather than an error. * `@examples` no longer complains about non-matching braces inside strings (#329). * `@family` now cross-links each manual page only once, instead of linking to all aliases (@gaborcsardi, #283, #367). * The special `@include` parser has also been rewritten in C++, giving a performance boost for larger packages (#401). This is particularly important because it's also called from `devtools::load_all()`. Additionally, a space before `@include` is no longer necessary (@krlmlr, #342). * `@inheritParams foo::bar` ensures that `%` remains escaped (#313). * If you document multiple arguments with one `@param`, (e.g. `@param a,b,c`) each parameter will get a space after it so it can be wrapped in the generated Rd file (#373). * `@section`s with identical titles are now merged together, just like `@description` and `@details`. This is useful in conjunction with the `@rdname` tag. (@krlmlr, #300). * Automatic `@usage` is now correctly generated for functions with string arguments containing `"\""` (#265). * `load_options()` is now exported so `devtools::document()` doesn't have to run `update_collate()` twice (#395). * `update_collate()` only rewrites the `Collate` entry in the DESCRIPTION file when it changes (#325, #723). * An empty `NAMESPACE` file is written if it is maintained by `roxygen2` (@krlmlr, #348). * Data that is not lazy-loaded can be documented (@krlmlr, #390). ## Internal changes * `register.preref.parser()` and `register.preref.parsers()` have been deprecated - please use `register_tags()` instead. * Parser callbacks registered with `register_tags()` are now called for fields parsed from the "introduction" (the text before the first tag) (@gaborcsardi, #370). # roxygen2 4.1.1 * Formatting of the `Authors@R` field in the DESCRIPTION file is now retained (@jranke, #330). * The collate roclet falls back to `base::strwrap()` when generating the collate field. This makes roxygen2 compatible with the next version of stringr. * New "vignette" roclet. This vignette automatically rebuilds all out of date vignettes (#314). * An off-by-one error in the C++ Roxygen preparser was fixed. * The new `@backref` tag makes it possible to override the sourceref for R code generators like `Rcpp` (@krlmlr, #291, #294). # roxygen2 4.1.0 * The source of the documentation is added to autogenerated `.Rd` files. * If there are no `@include` tags, roxygen2 leaves the collate field alone. This makes it easier to convert an existing project that uses a predefined collate, but if you start with `@include` and later remove them, you'll need to also remove the collate field (#302, #303). * Protected a `dir()` with `sort_c()` - If you'd noticed an inconsistency in ordering between `devtools::document()` and `devtools::check()` this was the cause of that. * Fixed broken regular expression that caused problems with stringr 1.0.0. * The `Authors@R` field in `DESCRIPTION` is now longer wrapped(@krlmlr, #284). * `@describeIn` with plain functions now correctly includes the function name and can be applied to data documentation. (@jimhester, #285, #288). * Works again when called from `Rscript` and `methods` is not loaded (@krlmlr, #305). # roxygen2 4.0.2 * If you don't use `@exports` or other namespace directives, your namespace file will not be touched (#276). * Methods no longer automatically attempt to inherit parameters from their generic. It's too fraught with difficulty (#261). * Roxygen now understands what to do with `setReplaceMethod()` (#266). * Parameter documentation is ordered according to the order of the formals, if possible (@krlmlr, #63). * Export `is_s3_method()`. * Roxygen no longer fails when run in non-UTF-8 locales on windows. # roxygen2 4.0.1 * Explicit `updateRoxygen()` is no longer needed - `roxygenize()` does the right thing the first time it is run. * Exporting a S4 generic works (#246). * `roxygenise()` no longer complains about absence of `wrap` field because it's so unlikely that anyone wants the old behaviour (#245). # roxygen2 4.0.0 roxygen2 4.0.0 is a major update to roxygen2 that makes provides enhanced error handling and considerably safer default behaviour. Now, roxygen2 will never overwrite a file that it did not create. This means that before you run it for the first time, you'll need to run `roxygen2::upgradeRoxygen()`. That will flag all existing files as being created by roxygen2. ## New features * Six vignettes provide a comprehensive overview of using roxygen2 in practice. Run `browseVignettes("roxygen2")` to access. * `@describeIn` makes it easier to describe multiple functions in one file. This is especially useful if you want to document methods with their generic, or with a common class, but it's also useful if you want to document multiple related functions in one file (#185). * `@field` documents the fields on a reference class (#181). It works the same way as `@slot` for S4 classes. * You can now document objects defined elsewhere (like datasets) by documenting their name as a string (#221). For example, to document an dataset called `mydata`, you can do: ```R #' Mydata set #' #' Some data I collected about myself "mydata" ``` * roxygen2 now adds a comment to all generated files so that you know they've been generated, and should not be hand edited. * roxygen2 no longer wraps the text in Rd files by default, i.e. the default option is `wrap = FALSE` now. To override it, you have to specify a field `Roxygen: list(wrap = TRUE)` in `DESCRIPTION` (#178). * Roxygenise automatically deletes out-of-date Rd files in `man/`. ## Improved error handling * roxygen2 will never overwrite a file that was not generated by roxygen2. This means that the first time you use this version of roxygen, you'll need to delete all existing Rd files. `roxygenise()` gains a clean argument that will automatically remove any files previously created by roxygen2. * Parsing is stricter: many issues that were previously warnings are now errors. All errors should now give you the line number of the roxygen block associated with the error. * Every input is now checked to make sure that you have matching braces (e.g. every `{` has a matching `}`). This should prevent frustrating errors that require careful reading of `.Rd` files (#183). * `@section` titles and `@export` tags can now only span a single line to prevent common bugs. * `@S3method` is deprecated - just use `@export` (#198). * Namespace tags now throw parsing errors if you give them bad inputs (#220). * Better error message if you try to document something other than NULL, an assignment, a class, a generic or a method (#194). ## Bug fixes and minor improvements * Better parsing of non-syntactic function names in other packages when used in `@inheritParams` (#236). * Deprecated arguments to `roxygenise()` (`roxygen.dir`, `copy.package`, `overwrite`, `unlink.target`) removed. * Remove unneeded codetools and tools dependencies. * Bump required Rcpp version to 0.11.0, and remove custom makefiles. * Non-syntactic argument names (like `_x`) are now surrounded by back-ticks in the usage (#191). * The internal parsers are no longer part of the public roxygen2 interface. * Usage statements in generated roxygen statements non-longer contain non-ASCII characters and will be wrapped if long (#180). * By default, reference classes now only document their own methods, not their methods of parents (#201). * Default aliases always include the original name of the object, even if overridden by `@name`. This also means that `A <- setClass("A")` will get two aliases by default: `A` and `A-class` (#202). Use `@aliases NULL` to suppress default alias. * Non-syntactic class names (like `<-`) are now escaped in the usage section of S4 methods (#205). * Eliminated two more cases where wrapping occurred even when `wrap = FALSE`. # roxygen2 3.1.0 ## Documentation for reference classes It's now possible to document reference classes, using the "docstring" convention described in `?setRefClass`. If you want to provide a short paragraph description of what a method does, make the first component of the message a string containing the description, e.g.: ```R setRefClass("A", methods = list( f = function(a, b) { "Take numbers \code{a} and \code{b} and add them together" a + b } )) ``` Unlike the documentation for R functions, the documentation for methods can be quite succinct. Roxygen adopts the convention that documented methods are public, and will be listed in the man page for the object. Undocumented methods are private and will not be shown in the documentation. The methods for all superclasses are also listed, so that you don't need to flip through multiple pages of documentation to understand what you can do with an object. All documented methods will be placed in a bulleted list in a section titled "Methods", the method usage will be automatically prepended to the docstring. ## Minor fixes and improvements * Fixes for Rcpp 0.11.0 compatibility. * `roxygenise()` now invisible returns a list of all files generated by individual roclets. This is useful for tools that want to figure out if there are extra files in the `man/` directory. * `is_s3_generic()` now recognises group generics (#166). * Don't try and add parameters for data objects (#165). * Sort output of families using C locale (#171). * `@family` now escapes function names in references (#172). # roxygen2 3.0.0 roxygen2 now fully supports S4 and RC (reference classes) - you should no longer need to manually add `@alias` or `@usage` tags for S4 classes, methods and generics, or for RC classes. * The default usage definitions are much better, generating the correct usage for data sets (#122), S3 methods (without additional `@method` tag), S4 generics, S4 methods, and for replacement (#119) and infix functions. Backslashes in function arguments in are correctly escaped. Usage statements also use a more sophisticated line wrapping algorithm so that they should cause fewer problems with the R CMD check line limit. (#89, #125). * S4 classes, S4 methods, and RC classes are given better default topics, and the file names corresponding to those topics are shorter. * S4 methods will automatically inherit parameter documentation from their generic. * `@slot name description` allows you to document the slots of a S4 class. S3 support has also been improved: roxygen2 now figures out whether a function is a S3 method or generic. (In the rare cases it does so incorrectly, use `@method` to manually describe the generic and class associated with a method). This means you can remove existing uses of `@method`, and can replace `@S3method` with `@export`. Roxygen now has support for package specific options through the `Roxygen` field in the `DESCRIPTION`. The value of the field should be R code that results in a list. Currently only `wrap` and `roclet` values are supported: * Turn off Rd re-wrapping with adding `Roxygen: list(wrap = FALSE)` * Change the default roclets by specifying `Roxygen: list(roclets = c("collate", "rd"))` Roxygen 3.0 also includes a number of minor fixes and improvements: * Infix functions are now escaped correctly in the `NAMESPACE`. (Thanks to @crowding, #111) * `roxygenise()` now works more like `devtools::document()` and only ever works in the current directory. The arguments `roxygen.dir`, `overwrite`, `copy.package` and `unlink.target` have been deprecated due to potential data loss problems. * The collate roclet is no longer a roclet: it processes R files using custom code (only statically, not dynamically) and is designed to be executed before the code is sourced. Run `update_collate()` to update the Collate directive based on `@include` tags - if there are none present, a collate directive will not be generated. * `@useDynLib` now works with more possible specifications - if you include a comma in the tag value, the output will be passed as is. This means that `@useDynLib mypackage, .registration = TRUE` will now generate `useDynLib(mypackage, .registration = TRUE)` in the `NAMESPACE`. (#124) * `inst` directory not created by default (#56). * Explicitly depend on `utils` and `methods` packages to make roxygen compatible with `Rscript` (#72). Import `digest` package instead of depending on it. * Always use C locale when sorting `NAMESPACE` file or tags in `.Rd` files. This ensures a consistent ordering across systems (#127). * Templates with extension `.r` are supported on case-sensitive file systems (#115). Template variables now actually work (#160, thanks to @bronaugh). * Suppress default aliases, format and usage with `@aliases NULL`, `@format NULL` and `@usage NULL`. # roxygen2 2.2.2 * Correctly use keyword `datasets` not `dataset` (Fixes #60) * Reference classes no longer given incorrect docType (data). # roxygen2 2.2.1 * Use unicode escapes in test files so tests pass on all platforms. * Work around bug in `gsub` in C locale by manually specifying `Encoding()`. # roxygen2 2.2 ## New features * Package docType will automatically add package alias, if needed. (Fixes #4) * Data docType will automatically add `datasets` keyword, default usage, and default format. (Fixes #5). Data docType automatically added to data objects. * New `@encoding` tag for manually setting non-ASCII encodings when needed. (Fixes #7) ## Bug fixes * `write.description()` now tries much harder to respect users' original DESCRIPTION field formatting instead of forcibly re-wrapping certain fields at 60 characters. * `@details` and `@description` now work correctly * `@useDynLib` now works correctly: @useDynLib packageName routine1 routine2 produces useDynLib(packageName, routine1) useDynLib(packageName, routine2) in the `NAMESPACE` file, instead of separate (wrong) useDynLib statements as before. * All namespace import directives now behave in the same way as the export directives, producing multiple single directives instead one multiple directive: `@importClassesFrom pkg a b` now produces `importClassesFrom(pkg, a)` and `importClassesFrom(pkg, b)` * In example files included with `@example` you can now use infix operators (e.g. %*%) or other things with %, because they will be preceded by a backslash in the Rd file. This behaviour was already in place for examples directly included with `@examples`. * Aliases are no longer quoted, and % is escaped with a backslash (Fixes #24). Names also have % escaped (Fixes #50) * Replacement functions (e.g. `foo<-`) now get correct usage statements: `foo() <- value` instead of `foo()<-value`. (Fixes #38) * Functions with no arguments now correctly get usage statements (Fixes #35) * Indentation in examples now preserved (Fixes #27) * roxygen2 will replace characters that are not valid in filenames with a character substitute, e.g. `[]` becomes `sub`, `<-` becomes `set` (Fixes #6) * Usage strings use non-breaking spaces to prevent string default values containing whitespace to be split across multiple lines. This may cause problems in the unlikely event that you have default value containing a non-breaking space (`"\uA0"') (Fixes #21) * Functions with quoted names now get correct usage statements (Fixes #41) * Objects that no longer exist are not documented (Fixes #42) * Errors now display file name and line number of roxygen block to help you find the problem. Thanks to code contributions from Renaud Gaujoux. (Fixes #13) * Documentation with no untagged text but with `@title`, `@description` and `@details` tags now produces correct output. # roxygen2 2.1 ## New features * package dependencies loaded automatically * added support for the `@source` tag ## Bug fixes * `NAMESPACE` file no longer needs to exist * `Collate` field in `DESCRIPTION` no longer needs to exist * `=` now recognised as way of assigning functions * `x$y <- function() {...}` no longer causes error * `@example` no longer added extra new-lines. * Correct directory normalisation under windows fixes broken test. * A special thanks goes to Yihui Xie who contributed all of the fixes and improvements (bar one) in this version! # roxygen2 2.0 ## Major changes * now works with run-time details to give more accurate output. This requires that the source code that roxygen is documenting be loaded prior to documentation. roxygen will attempt to do so, but you need to ensure required packages are loaded. Run-time data fixes some long standing bugs where roxygen couldn't correctly figure out function usage. We are not aware of any cases where you still need to use the `@usage` tag. * written in idiomatic R, and uses S3 instead of a homegrown class system. * roclets build up an internal data structure instead of writing to disk directly. This means that you can now use the `@rdname` tag to merge documentation for multiple functions into one file, and that only unique namespace directives are written to `NAMESPACE` (which makes `@importFrom` much more useful). * some features have been removed, and may or may not (based on your feedback) be reincluded. These include the callgraph roclet, and `R CMD roxygen`, which only worked on some systems. * a templating system: use the `@template` tag to insert a `brew` template stored in `man-roxygen`. Template variables can be set using `@templateVar name value` and retrieved from within the template with `<%= name %>` * extensive use of caching to make repeated runs as fast as possible. To clear caches and guarantee a complete rebuild, use `clear_caches()`. * parsing of "introduction" (the text before the first tag) has changed. Now the title consists of the first paragraph (i.e. all text before the first empty line), the second paragraph is the description and all others are put in the details. Any component can be overridden with `@title`, `@description` and `@details` as appropriate. ## Minor changes * `@name` is always output as an alias, even if `@aliases` are used. * `@export` correctly uses `@method` to generate `S3method` namespace directive ## New tags * `@rdname filename` sets the output filename (without extension). Use for functions non-alphanumeric functions (e.g. `[<-`) or if you want to document multiple functions in one file * `@template templatename` includes a documentation template (see above) * `@section Section title: contents` includes a section with any title. Don't forget the colon! That separates the title of the section from its contents. * `@description` and `@details` tags allow you to specify description and details components in a template * `@family family name` automatically adds see-also cross-references between all functions in a family. A function can belong to multiple families. * `@inheritParams name` allows you to inherit the documentation for parameters from another function, either within the current package (`function`) or in any other installed package (`package:function`). Currently only supports single inheritance (i.e. you can't inherit from a function that inherits from another function), but you can have multiple @inheritParams tags. * `@format` has been implemented; it existed in the roxygen package but was actually ignored roxygen2/inst/0000755000176200001440000000000015174650112013006 5ustar liggesusersroxygen2/inst/doc/0000755000176200001440000000000015174650112013553 5ustar liggesusersroxygen2/inst/doc/rd-S7.Rmd0000644000176200001440000001114415172221402015106 0ustar liggesusers--- title: "Documenting S7" description: > How to document S7 generics, methods, and classes. output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Documenting S7} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r} #| include: false knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ``` There are three things that you might document for [S7](https://rconsortium.github.io/S7/): - **Generics**: mention that the function is a generic and list the available methods. - **Methods**: link back to the generic; only document individually when the method has unique behavior or arguments. - **Classes**: document the constructor. ## Generics S7 **generics** are functions, so document them as such. Export a generic if you want users to call it or other developers to write methods for it. If the generic is internal, you don't need to document it. The documentation should mention that the function is a generic, because this tells the reader that the behavior may vary depending on the input and that they can write their own methods. For simple generics, you can do this in the description: ```{r} #| eval: false #' Size of an object #' #' @description #' `size()` is an S7 generic that determines the size of an object, #' with methods available for the following classes: #' #' `r doclisting::methods_list("size")` #' #' @param x An object. #' @param ... Not used. #' @returns A single number. #' @export size <- new_generic("size", "x") ``` For more complicated generics, you can use a `# Methods` section to provide more detail: ```{r} #| eval: false #' Size of an object #' #' @description #' `size()` determines the size of an object. #' #' # Methods #' `size()` is an S7 generic with methods available for the following #' classes: #' #' `r doclisting::methods_list("size")` #' #' @param x An object. #' @param ... Not used. #' @returns A single number. #' @export size <- new_generic("size", "x") ``` See the [S3 Generics section](rd-S3.html#generics) for more about using the [doclisting](https://doclisting.r-lib.org/) package to automatically generate method lists. It's good practice to document the default method alongside the generic using `@rdname`: ```{r} #| eval: false #' @rdname size method(size, class_any) <- function(x, ...) { length(x) } ``` ## Classes S7 **classes** are constructor functions, so document them much like you'd document any other function. Export a class if you want users to create instances or other developers to extend it (e.g. by creating subclasses). Internal classes don't need documentation. Use `@param` to document the constructor arguments (which correspond to class properties), and `@returns` to describe the object that is returned. If the class has additional properties that are not part of the constructor (e.g. read-only computed properties), use `@prop` to document them. ```{r} #| eval: false #' A range #' #' Create a range represented by a numeric `start` and `end`. The start must #' always be less than the end. #' #' @param start Start of range. #' @param end End of range. #' @prop length Length of the range (read-only). #' @returns An `Range` S7 object. #' @export Range <- new_class( "Range", properties = list( start = class_numeric, end = class_numeric, length = new_property(getter = function(self) self@end - self@start), validator = function(self) { if (self@start > self@end) { "start must be less than or equal to end" } } ) ) ``` If multiple classes share one Rd page (via `@rdname`), you can prefix the property name with the class name to group properties by class, e.g. `@prop ClassName@prop_name description`. ## Methods It is your choice whether or not to document S7 **methods**. S7 methods are registered with `method(generic, class) <- fn`. Generally, it's not necessary to document straightforward methods. It's good practice, however, to document methods that have unique behavior or arguments. If you do document a method, give it its own roxygen block. When documenting a method, always include a link back to the generic using `[generic_name()]` so the reader can easily find the full documentation and other methods. ```{r} #| eval: false #' Size of a range #' #' The size of a range is its [size()], i.e. its length. #' #' @param x A `Range` object. #' @param ... Not used. #' @returns A single number. method(size, Range) <- function(x, ...) { x@length } ``` S7 methods are registered at load time via `S7::methods_register()` in your `.onLoad()` function, not through `NAMESPACE` directives, so you never need to `@export` them. See `vignette("packages", package = "S7")` for details. roxygen2/inst/doc/index-crossref.R0000644000176200001440000000122415174650102016627 0ustar liggesusers## ----------------------------------------------------------------------------- knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ## ----------------------------------------------------------------------------- #' @seealso [prod()] for products, [cumsum()] for cumulative sums, and #' [colSums()]/[rowSums()] marginal sums over high-dimensional arrays. ## ----------------------------------------------------------------------------- # list( # rd_family_title = list(aggregations = "Aggregation functions") # ) ## ----------------------------------------------------------------------------- #' @backref src/file.cpp #' @backref src/file.h roxygen2/inst/doc/extending.html0000644000176200001440000014056115174650102016434 0ustar liggesusers Extending roxygen2

    Extending roxygen2

    roxygen2 is extensible, and this vignette will show you how. It starts with an introduction to the basic workflow of roxygenize() and the key data structures that power it. Then we’ll show you how you can use its two extension points:

    • Add a new tag to generate a new top-level section in an .Rd file. This allows you to repeat yourself less when documenting your package. (See vignette("reuse") for other techniques.)

    • Add a new roclet. This lets you take full advantage of the computational machinery behind roxygenize() to compute anything you want or produce any artefact you can imagine.

    library(roxygen2)

    How roxygenize() works

    You’ve probably used roxygenize() (or devtools::document()) a bunch without ever really thinking about what’s going on behind the scenes. But if you’re going to extend roxygen2, you’ll need to know exactly what’s happening:

    • Loads the package under roxygenizing, as well as any further packages (“packages” in load_options()).

    • Parses all R files in the package, using available tags.

    • Finds the roclets (see roclet()) from its roclets argument or the “roclets” option (load_options()). It defaults to using the Collate “roclet”, the Rd roclet, and NAMESPACE roclet, but you can also add your own.

    • Runs the different methods of all those roclets, in order and independently: clean (roclet_clean), preprocess (roclet_preprocess()), process (roclet_process()), and output (roclet_output()). Only process and output are routinely used. For example, if you think of the Rd roclet, its process method digests information from the tag parsing, combines inherits, etc. to create the content of each documentation topic, and its output method writes those topics to disk.

    Key data structures

    Before we dive into extending roxygen2, we need to first discuss two important data structures that power roxygen: tags and blocks.

    Tags

    A tag (a list with S3 class roxy_tag) represents a single tag. It has the following fields:

    • tag: the name of the tag.

    • raw: the raw contents of the tag (i.e. everything from the end of this tag to the beginning of the next).

    • val: the parsed value, which we’ll come back to shortly.

    • file and line: the location of the tag in the package. Used with roxy_tag_warning() to produce informative error messages.

    You can construct tag objects by hand with roxy_tag():

    roxy_tag("name", "Hadley")
    #> [????:???] @name 'Hadley' {unparsed}
    str(roxy_tag("name", "Hadley"))
    #> List of 5
    #>  $ file: chr NA
    #>  $ line: chr NA
    #>  $ raw : chr "Hadley"
    #>  $ tag : chr "name"
    #>  $ val : NULL
    #>  - attr(*, "class")= chr [1:2] "roxy_tag_name" "roxy_tag"

    However, you should rarely need to do so (except in tests), because you’ll typically have them given to you in a block object, as you’ll see shortly.

    Blocks

    A block (a list with S3 class roxy_block) represents a single roxygen block. It has the following fields:

    • tags: a list of roxy_tags.
    • call: the R code associated with the block (usually a function call).
    • file and line: the location of the R code.
    • object: the evaluated R object associated with the code.

    The easiest way to see the basic structure of a roxy_block() is to generate one by parsing a roxygen block with parse_text():

    text <- "
      #' This is a title
      #'
      #' This is the description.
      #'
      #' @param x,y A number
      #' @export
      f <- function(x, y) x + y
    "
    
    # parse_text() returns a list of blocks, so I extract the first
    block <- parse_text(text)[[1]]
    block
    #> <roxy_block> [<text>:8]
    #>   $tag
    #>     [line:  2] @title 'This is a title' {parsed}
    #>     [line:  4] @description 'This is the description.' {parsed}
    #>     [line:  6] @param 'x,y A number' {parsed}
    #>     [line:  7] @export '' {parsed}
    #>     [line:  8] @usage '<generated>' {parsed}
    #>     [line:  8] @.formals '<generated>' {parsed}
    #>     [line:  8] @backref '<generated>' {parsed}
    #>   $call   f <- function(x, y) x + y
    #>   $object <function> 
    #>     $topic f
    #>     $alias f

    You’ll notice that some of the tags didn’t exist in the original block:

    • @title and @description are extracted from the text that appears before the first explicit tag.
    • @usage is generated automatically from the function formals.
    • @.formals is an “internal” tag that doesn’t generate any output but is used to pass some important data around.
    • @backref stores the source location of the block so we can later record which .R files contributed to each .Rd file.

    Adding a new .Rd tag

    The most common way to extend roxygen2 is to create a new tag that adds output to .Rd files. This requires defining a few methods:

    1. Define a roxy_tag_parse() method that describes how to parse our new tag.

    2. Define a roxy_tag_rd() method that describes how to convert the tag into .Rd commands.

    3. If the tag’s content is meant to appear in a custom section (as opposed to, say, the examples section), define a format() method that describes how to create the .Rd string.

    To illustrate the basic idea, we’ll create a new @tip tag that will create a bulleted list of tips about how to use a function. The idea is to take something like this:

    #' @tip The mean of a logical vector is the proportion of `TRUE` values.
    #' @tip You can compute means of dates and date-times!

    That generates Rd like this:

    \section{Tips and tricks}{
    \itemize{
      \item The mean of a logical vector is the proportion of \code{TRUE} values.
      \item You can compute means of dates and date-times!
    }
    }

    The first step is to define a method for roxy_tag_parse() that describes how to parse the tag text. The name of the class will be roxy_tag_{tag}, which in this case is roxy_tag_tip. This function takes a roxy_tag as input, and its job is to set x$val to a convenient parsed value that will be used later by the roclet. Here we want to process the text using Markdown so we can just use tag_markdown():

    roxy_tag_parse.roxy_tag_tip <- function(x) {
      tag_markdown(x)
    }

    (There are lots of other built in options that you can read about in ?tag_markdown.)

    We can check this works by using parse_text():

    text <- "
      #' Title
      #'
      #' @tip The mean of a logical vector is the proportion of `TRUE` values.
      #' @tip You can compute means of dates and date-times!
      #' @md
      f <- function(x, y) {
        # ...
      }
    "
    block <- parse_text(text)[[1]]
    block
    #> <roxy_block> [<text>:7]
    #>   $tag
    #>     [line:  2] @title 'Title' {parsed}
    #>     [line:  4] @tip 'The mean of a logical vector is the proportion ...' {parsed}
    #>     [line:  5] @tip 'You can compute means of dates and date-times!' {parsed}
    #>     [line:  6] @md '' {parsed}
    #>     [line:  7] @usage '<generated>' {parsed}
    #>     [line:  7] @.formals '<generated>' {parsed}
    #>     [line:  7] @backref '<generated>' {parsed}
    #>   $call   f <- function(x, y) { ...
    #>   $object <function> 
    #>     $topic f
    #>     $alias f
    
    str(block$tags[[2]])
    #> List of 5
    #>  $ file: chr "<text>"
    #>  $ line: int 4
    #>  $ tag : chr "tip"
    #>  $ raw : chr "The mean of a logical vector is the proportion of `TRUE` values."
    #>  $ val : chr "The mean of a logical vector is the proportion of \\code{TRUE} values."
    #>  - attr(*, "class")= chr [1:2] "roxy_tag_tip" "roxy_tag"

    Here I explicitly turn Markdown parsing on using @md; it’s usually turned on for a package using roxygen2’s load_options().

    Next, we define a method for roxy_tag_rd(), which must create an rd_section(). We’re going to create a new custom section called tip. It will contain a character vector of tips:

    roxy_tag_rd.roxy_tag_tip <- function(x, base_path, env) {
      rd_section("tip", x$val)
    }

    This additional layer is needed because there can be multiple tags of the same type in a single block, and multiple blocks can contribute to the same .Rd file. The job of rd_section() is to combine all the tags into a single top-level Rd section. Each tag generates an rd_section which is then combined with any previous section using merge(). The default merge.rd_section() just concatenates the values together (rd_section(x$type, c(x$value, y$value))); you can override this method if you need more sophisticated behaviour.

    We called the custom section “tip” just like our tag, but we needn’t have done so: it really depends on how you want to map the input tags to output Rd code.

    We then need to define a format() method to convert this object into text for the .Rd file:

    format.rd_section_tip <- function(x, ...) {
      paste0(
        "\\section{Tips and tricks}{\n",
        "\\itemize{\n",
        paste0("  \\item ", x$value, "\n", collapse = ""),
        "}\n",
        "}\n"
      )
    }

    We can now try this out with roclet_text():

    topic <- roc_proc_text(rd_roclet(), text)[[1]]
    topic$get_section("tip")
    #> \section{Tips and tricks}{
    #> \itemize{
    #>   \item The mean of a logical vector is the proportion of \code{TRUE} values.
    #>   \item You can compute means of dates and date-times!
    #> }
    #> }
    #> 

    Note that there is no namespacing so if you’re defining multiple new tags I recommend using your package name as the common prefix.

    Using your new tag

    Now that the three methods are created, we still need to make them available to roxygenize(). First, you need to export the method so that it’s registered correctly. Since your methods will be for generics defined in roxygen2 (another package), you’ll need to follow the advice in Methods for generics in other packages.

    Next, you’ll need to load the tag-defining package in the package where you want to use it. For example, if you created some new tags in {packageFoo}, and would like to use these tags in your documentation for {packageBar}, append this line to the DESCRIPTION of {packageBar}:

    Config/roxygen2/packages: packageFoo

    See load_options() for more details.

    Creating a new roclet

    Creating a new roclet is usually a two part process. First, you define new tags that your roclet will work with, unless your roclet only needs information from existing tags, or only needs the path to the package source1. Second, you define a roclet that tells roxygenize() what to compute and produce based on this information.

    Custom tags

    In this example we will make a new @memo tag which helps you to remember what you’re planning to work on in the future by displaying notes when you document your package. We choose this syntax for @memo:

    #' @memo [Headline] Description

    For example:

    #' @memo [EFFICIENCY] Currently brute-force; find better algorithm.

    As above, we first define a parse method. This time we use custom format based on a regular expression:

    roxy_tag_parse.roxy_tag_memo <- function(x) {
      if (!grepl("^\\[.*\\].*$", x$raw)) {
        roxy_tag_warning(x, "Invalid memo format")
        return()
      }
    
      parsed <- regmatches(x$raw, regexec("\\[(.*)\\](.*)", x$raw))[[1]]
    
      x$val <- list(
        header = parsed[[2]],
        message = parsed[[3]]
      )
      x
    }

    Then we check it works with parse_text():

    text <- "
      #' @memo [TBI] Remember to implement this!
      #' @memo [API] Check best API
      f <- function(x, y) {
        # ...
      }
    "
    block <- parse_text(text)[[1]]
    block
    #> <roxy_block> [<text>:4]
    #>   $tag
    #>     [line:  2] @memo '[TBI] Remember to implement this!' {parsed}
    #>     [line:  3] @memo '[API] Check best API' {parsed}
    #>     [line:  4] @usage '<generated>' {parsed}
    #>     [line:  4] @.formals '<generated>' {parsed}
    #>     [line:  4] @backref '<generated>' {parsed}
    #>   $call   f <- function(x, y) { ...
    #>   $object <function> 
    #>     $topic f
    #>     $alias f
    
    str(block$tags[[1]])
    #> List of 5
    #>  $ file: chr "<text>"
    #>  $ line: int 2
    #>  $ tag : chr "memo"
    #>  $ raw : chr "[TBI] Remember to implement this!"
    #>  $ val :List of 2
    #>   ..$ header : chr "TBI"
    #>   ..$ message: chr " Remember to implement this!"
    #>  - attr(*, "class")= chr [1:2] "roxy_tag_memo" "roxy_tag"

    We don’t need a format method because our tag won’t be used to produce Rd sections2.

    The roclet

    Next, we create a constructor for the roclet, which uses roclet(). Our memo roclet doesn’t have any options so this is very simple:

    memo_roclet <- function() {
      roclet("memo")
    }

    To give the roclet behaviour, you need to define methods. There are two methods that almost every roclet will use:

    • roclet_process() is called with a list of blocks, and returns an object of your choosing.

    • roclet_output() produces side-effects (usually writing to disk) using the result from roclet_process().

    For this roclet, we’ll have roclet_process() collect all the memo tags into a named list:

    roclet_process.roclet_memo <- function(x, blocks, env, base_path) {
      results <- list()
    
      for (block in blocks) {
        tags <- block_get_tags(block, "memo")
    
        for (tag in tags) {
          msg <- paste0("[", tag$file, ":", tag$line, "] ", tag$val$message)
          results[[tag$val$header]] <- c(results[[tag$val$header]], msg)
        }
      }
    
      results
    }

    And then have roclet_output() print them to the screen:

    roclet_output.roclet_memo <- function(x, results, base_path, ...) {
      for (header in names(results)) {
        messages <- results[[header]]
        cat(paste0(header, ": ", "\n"))
        cat(paste0(" * ", messages, "\n", collapse = ""))
      }
    
      invisible(NULL)
    }

    Then you can test if it works by using roc_proc_text():

    results <- roc_proc_text(
      memo_roclet(),
      "
    #' @memo [TBI] Remember to implement this!
    #' @memo [API] Check best API
    f <- function(x, y) {
      # ...
    }
    
    #' @memo [API] Consider passing z option
    g <- function(x, y) {
      # ...
    }
    "
    )
    roclet_output(memo_roclet(), results)
    #> TBI: 
    #>  * [<text>:2]  Remember to implement this!
    #> API: 
    #>  * [<text>:3]  Check best API
    #>  * [<text>:8]  Consider passing z option

    Adding a roclet to your workflow

    To use a roclet when developing a package, call

    roxygen2::roxygenize(roclets = "yourPackage::roclet")

    where yourPackage::roclet is the function which creates the roclet, e.g. memo_roclet above.

    You can also add the roclet to the target package’s DESCRIPTION file, like this:

    Config/roxygen2/roclets: collate, rd, namespace, yourPackage::roclet

    See load_options() for more details.

    Your package only needs to be installed when the user documents the package, which doesn’t correspond precisely to any field in the DESCRIPTION. However, you can think of it as a development dependency and hence put it in the Suggests: field:

    usethis::use_package("yourPackage", type = "Suggests")

    You don’t have to do this, but it will help other developers working on the target package.

    Conclusion: further extension ideas

    This vignette is quite rough, so you might want to also read some roxygen2 source code to understand all the extension points.

    Do not hesitate to also look for examples of roxygen2 extensions. For instance, the roxygenlabs package (former incubator of roxygen2 features) used a third extension point: it extended the Rd roclet with further methods, thus creating a supercharged Rd roclet rather than a brand-new roclet. Or, the plumber2 package only uses the parsing features from roxygen2, and does not use roxygenize() at all.


    1. For example, the no-longer recommended vignette_roclet() only needs the path to the package source as input; it does not use information from the tag parsing step. Or the {roxylint} package only uses existing tags; its job is to warn you about suboptimal roxygen2 style.↩︎

    2. Some tags in roxygen2 itself, like @importFrom, are not meant for the [rd_roclet()] (@importFrom is meant for the namespace_roclet()).↩︎

    roxygen2/inst/doc/rd-S7.R0000644000176200001440000000431615174650105014600 0ustar liggesusers## ----------------------------------------------------------------------------- knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ## ----------------------------------------------------------------------------- # #' Size of an object # #' # #' @description # #' `size()` is an S7 generic that determines the size of an object, # #' with methods available for the following classes: # #' # #' `r doclisting::methods_list("size")` # #' # #' @param x An object. # #' @param ... Not used. # #' @returns A single number. # #' @export # size <- new_generic("size", "x") ## ----------------------------------------------------------------------------- # #' Size of an object # #' # #' @description # #' `size()` determines the size of an object. # #' # #' # Methods # #' `size()` is an S7 generic with methods available for the following # #' classes: # #' # #' `r doclisting::methods_list("size")` # #' # #' @param x An object. # #' @param ... Not used. # #' @returns A single number. # #' @export # size <- new_generic("size", "x") ## ----------------------------------------------------------------------------- # #' @rdname size # method(size, class_any) <- function(x, ...) { # length(x) # } ## ----------------------------------------------------------------------------- # #' A range # #' # #' Create a range represented by a numeric `start` and `end`. The start must # #' always be less than the end. # #' # #' @param start Start of range. # #' @param end End of range. # #' @prop length Length of the range (read-only). # #' @returns An `Range` S7 object. # #' @export # Range <- new_class( # "Range", # properties = list( # start = class_numeric, # end = class_numeric, # length = new_property(getter = function(self) self@end - self@start), # validator = function(self) { # if (self@start > self@end) { # "start must be less than or equal to end" # } # } # ) # ) ## ----------------------------------------------------------------------------- # #' Size of a range # #' # #' The size of a range is its [size()], i.e. its length. # #' # #' @param x A `Range` object. # #' @param ... Not used. # #' @returns A single number. # method(size, Range) <- function(x, ...) { # x@length # } roxygen2/inst/doc/namespace.R0000644000176200001440000000104015174650102015624 0ustar liggesusers## ----------------------------------------------------------------------------- knitr::opts_chunk$set(comment = "#>", collapse = TRUE) ## ----------------------------------------------------------------------------- # From dplyr: #' @rawNamespace import(vctrs, except = data_frame) NULL # From backports: #' @rawNamespace if (getRversion() < "4.0.0") export(stopifnot) NULL ## ----------------------------------------------------------------------------- #' @importFrom pkg fun1 fun2 #' @importFrom pkg2 fun3 #' @importFrom pkg3 fun4 NULL roxygen2/inst/doc/rd-datasets.Rmd0000644000176200001440000000465215172221402016433 0ustar liggesusers--- title: "Documenting datasets" description: > How to document datasets stored in `data/`. output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Documenting datasets} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r} #| include: false knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ``` Datasets are stored in `data/`, not as regular R objects in the package. This means you need to document them in a slightly different way: instead of documenting the data directly, you quote the dataset's name. For example, this is the roxygen2 block used for `ggplot2::diamonds`: ```{r} #| eval: false #' Prices of over 50,000 round cut diamonds #' #' A dataset containing the prices and other attributes of almost 54,000 #' diamonds. The variables are as follows: #' #' @format A data frame with 53940 rows and 10 variables: #' \describe{ #' \item{price}{price in US dollars ($326--$18,823)} #' \item{carat}{weight of the diamond (0.2--5.01)} #' \item{cut}{quality of the cut (Fair, Good, Very Good, Premium, Ideal)} #' \item{color}{diamond colour, from D (best) to J (worst)} #' \item{clarity}{a measurement of how clear the diamond is (I1 (worst), SI2, #' SI1, VS2, VS1, VVS2, VVS1, IF (best))} #' \item{x}{length in mm (0--10.74)} #' \item{y}{width in mm (0--58.9)} #' \item{z}{depth in mm (0--31.8)} #' \item{depth}{total depth percentage = z / mean(x, y) = 2 * z / (x + y) (43--79)} #' \item{table}{width of top of diamond relative to widest point (43--95)} #' } #' #' @source {ggplot2} tidyverse R package. "diamonds" ``` Datasets should never be exported with `@export` because they are not found in the `NAMESPACE`. Instead, datasets will either be automatically available if you set `LazyData: true` in your `DESCRIPTION`, or available after calling `data()` if not. This field also affects the default usage. If you have `LazyData: true`, the usage will be just the dataset name (e.g. `diamonds`). Otherwise, the usage will be wrapped in `data()` (e.g. `data(diamonds)`). Note the use of two additional tags that are particularly useful for documenting data: - `@format`, which gives an overview of the structure of the dataset. This should include a **definition list** that describes each variable. There's currently no way to generate this with Markdown, so this is one of the few places you'll need to Rd markup directly. - `@source` where you got the data form, often a URL. roxygen2/inst/doc/rd-functions.Rmd0000644000176200001440000002336515172221402016635 0ustar liggesusers--- title: "Documenting functions" description: > The basics of roxygen2 tags and how to use them for documenting functions. output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Documenting functions} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r} #| include: false knitr::opts_chunk$set(comment = "#>", collapse = TRUE) ``` This vignette describes the most important roxygen2 tags for documenting functions. See `vignette("reuse")` for reusing documentation across topics. ## Exporting For a user to use a function in your package, you must **export** it with `@export`. If you export a function, you must also document it, and since other people will use it, you need to be careful if you later change the function interface. We usually put `@export` in between `@returns` and `@examples`: ```{r} #| eval: false #' Add two numbers together #' #' @param x,y A pair of numbers. #' @returns A number. #' @export #' @examples #' 1 + 2 add <- function(x, y) { x + y } ``` You'll learn what all these are pieces are next! ## The introduction Each documentation block starts with some text which defines the title, the description, and the details. Here's an example showing what the documentation for `sum()` might look like if it had been written with roxygen: ```{r} #' Sum of vector elements #' #' `sum` returns the sum of all the values present in its arguments. #' #' This is a generic function: methods can be defined for it directly #' or via the [Summary()] group generic. For this to work properly, #' the arguments `...` should be unnamed, and dispatch is on the #' first argument. sum <- function(..., na.rm = TRUE) {} ``` This introductory block is broken up as follows: - The first sentence is the **title**: that's what you see when you look at `help(package = mypackage)` and is shown at the top of each help file. It should generally fit on one line, be written in sentence case, and not end in a full stop. - The second paragraph is the **description**: this comes first in the documentation and should briefly describe what the function does. - The third and subsequent paragraphs go into the **details**: this is a (often long) section that comes after the argument description and should provide any other important details of how the function operates. The details are optional. ### Title The title appears in package indexes and search results, so think about how users will find your function. Use synonyms to describe what the function does. For example, dplyr uses titles like: - "Keep rows that match a condition" (`filter()`) - "Create, modify, and delete columns" (`mutate()`) - "Summarise each group down to one row" (`summarise()`) ### Description The description should briefly summarize the goal of the function, usually in one paragraph. This can be challenging for simple functions, because it can feel like you're just repeating the title of the function. Try to find a slightly different wording, if you can. It's okay if this feels a little repetitive; it's often useful for users to see the same thing expressed in two different ways. It's a little extra work, but the extra effort is often worth it. ```{r} #| eval: false #' Detect the presence/absence of a match #' #' `str_detect()` returns a logical vector with `TRUE` for each element of #' `string` that matches `pattern` and `FALSE` otherwise. It's equivalent to #' `grepl(pattern, string)`. ``` If you need a multi-paragraph description, use an explicit `@description` tag: ```{r} #| eval: false #' View strings and matches #' #' @description #' `str_view()` is used to print the underlying representation of a string and #' to see how a `pattern` matches. #' #' Matches are surrounded by `<>` and unusual whitespace (i.e. all whitespace #' apart from `" "` and `"\n"`) are surrounded by `{}` and escaped. ``` Basically, if you're going to include an empty line in your description, you'll need to use an explicit `@description` tag. ### Details The details section is optional and comes after the argument description in the rendered help. Use informative markdown headings to break up long details. ```{r} #' Sum of vector elements #' #' @description #' `sum` returns the sum of all the values present in its arguments. #' #' @details #' This is a generic function: methods can be defined for it directly #' or via the [Summary()] group generic. For this to work properly, #' the arguments `...` should be unnamed, and dispatch is on the #' first argument. ``` ## Required tags Functions are the most commonly documented objects. Functions require three tags: `@param`, `@returns`, and `@examples`. ### Inputs Use `@param name description` to describe each input to the function. The description should provide a succinct summary of the parameter type (e.g. a string, a numeric vector), and if not obvious from the name, what the parameter does. The description is a sentence so should start with a capital letter and end with a full stop. It can span multiple lines (or even paragraphs) if necessary. All parameters must be documented. ```{r} #| eval: false #' @param pattern Pattern to look for. #' #' The default interpretation is a regular expression, as described in #' `vignette("regular-expressions")`. Use [regex()] for finer control of the #' matching behaviour. #' #' @param string Input vector. Either a character vector, or something #' coercible to one. ``` It's a good idea to describe the default value in the `@param` description, particularly when the default is non-obvious or the signature and the rendered argument documentation are far apart. ```{r} #| eval: false #' @param na.rm Remove missing values? If `FALSE` (the default), the result #' will be `NA` if any element of `string` is `NA`. ``` For arguments with a small fixed set of possible values, list them in the description: ```{r} #| eval: false #' @param side Side on which to remove whitespace: `"left"`, `"right"`, or #' `"both"` (the default). ``` If each value needs more explanation, use a bulleted list: ```{r} #| eval: false #' @param whitespace_only A boolean. #' * `TRUE` (the default): wrapping will only occur at whitespace. #' * `FALSE`: can break on any non-word character (e.g. `/`, `-`). ``` If two or more arguments are tightly coupled, you can document them in one place by separating the names with commas (no spaces). For example, to document both `x` and `y`, you can say `@param x,y Numeric vectors`. ### Outputs `@returns description` describes the output from the function. Briefly describe the type/shape of the output, not the details. All functions must have a documented return value for initial CRAN submission. For simple cases, a single sentence suffices: ```{r} #| eval: false #' @returns A logical vector the same length as `string`. ``` For functions returning data frames, describe how the output relates to the input: ```{r} #| eval: false #' @returns #' An object of the same type as `.data`. The output has the following #' properties: #' #' * Rows are a subset of the input, but appear in the same order. #' * Columns are not modified. #' * The number of groups may be reduced (if `.preserve` is not `TRUE`). #' * Data frame attributes are preserved. ``` ### Examples `@examples` provides executable R code showing how to use the function in practice. This is a very important part of the documentation because many people look at the examples before reading anything. Example code must work without errors as it is run automatically as part of `R CMD check`. Focus on demonstrating the most important features with realistic, typical usage. Use comments to break examples into sections: ```{r} #| eval: false #' @examples #' fruit <- c("apple", "banana", "pear", "pineapple") #' str_detect(fruit, "a") #' str_detect(fruit, "^a") #' str_detect(fruit, "a$") #' #' # Also vectorised over pattern #' str_detect("aecfg", letters) ``` Examples must be self-contained and not change the user's state: if you modify `options()`, reset them; create files in `tempdir()` and delete them; don't change the working directory or write to the clipboard. For the purpose of illustration, it's often useful to include code that causes an error. `try()` is the best way to do this, because users will see the error message: ```{r} #| eval: false #' @examples #' # Row sizes must be compatible when column-binding #' try(bind_cols(tibble(x = 1:3), tibble(y = 1:2))) ``` You can also use `\dontrun{}` to prevent code from executing, but `try()` is generally preferred. For code that only works in certain environments, use `@examplesIf`: ```r #' @examplesIf interactive() #' browseURL("https://roxygen2.r-lib.org") ``` This generates ``` \examples{ \dontshow{if (interactive() (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} gh_organizations(since = 42) \dontshow{\}) # examplesIf} } ``` This way, the code evaluating whether the example can be run is not shown to users reading the help, but it still prevents R CMD check failures. `@examplesIf` is preferred over wrapping code in `if()` because users don't see the machinery, and unlike `\dontrun{}`, the code still runs when the condition is met. Instead of including examples directly in the documentation, you can put them in separate files and use `@example path/relative/to/package/root` to insert them into the documentation. All functions must have examples for initial CRAN submission. ### Usage In most cases, the function usage (which appears beneath the description in the generated docs) will be automatically derived from the function specification. For the cases where it is not, please [file an issue](https://github.com/r-lib/roxygen2/issues) and use `@usage` to override the default with what you want. If you want to suppress the usage altogether (which is sometimes useful for internal or deprecated functions), you can use `@usage NULL`. roxygen2/inst/doc/index-crossref.html0000644000176200001440000003133015174650102017373 0ustar liggesusers Indexing and cross-references

    Indexing and cross-references

    This vignette discusses tags that help users finding documentation through cross-references and indexes.

    See also

    @seealso allows you to point to other useful resources, either on the web (using a url) or to related functions (with a function link like [function_name()]). For sum(), this might look like:

    #' @seealso [prod()] for products, [cumsum()] for cumulative sums, and
    #'   [colSums()]/[rowSums()] marginal sums over high-dimensional arrays.

    Family

    If you have a family of related functions, you can use @family {family} to cross-reference each function to every member of the family. A function can be a member of multiple families.

    By default @family {family}, will generate the see also text “Other {family}:”, so the @family name should be plural (i.e., “model building helpers” not “model building helper”).

    If you want to override the default title, you can provide an rd_family_title element in a list stored in man/roxygen/meta.R:

    list(
      rd_family_title = list(aggregations = "Aggregation functions")
    )

    References

    If the object you’re documenting has connections to the scientific literature, use @reference to provide a citation.

    Aliases

    ? and help() look for topic aliases; ?foo will find any topic that contains the foo alias.

    roxygen2 generates a default alias for you based on the object you’re documenting. You can add additional aliases with @aliases alias1 alias2 alias3 or remove default alias with @aliases NULL.

    Back references

    The original source location is added as a comment to the second line of each generated .Rd file in the following form:

    % Please edit documentation in ...

    roxygen2 tries to capture all locations from which the documentation is assembled. For code that generates R code with Roxygen comments (e.g., the Rcpp package), the @backref tag is provided. This allows specifying the “true” source of the documentation, and will substitute the default list of source files. Use one tag per source file:

    #' @backref src/file.cpp
    #' @backref src/file.h
    roxygen2/inst/doc/rd-packages.R0000644000176200001440000000040615174650110016055 0ustar liggesusers## ----------------------------------------------------------------------------- knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ## ----------------------------------------------------------------------------- # #' @keywords internal # "_PACKAGE" roxygen2/inst/doc/rd-S4.Rmd0000644000176200001440000000402515172221402015103 0ustar liggesusers--- title: "Documenting S4" description: > How to document S4 generics, methods, and classes. output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Documenting S4} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r} #| include: false knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ``` There are three things that you might document for [S4](https://adv-r.hadley.nz/s4.html): - **Generics**: document as functions; mention that they are generics. - **Methods**: unlike S3/S7, all S4 methods must be documented. - **Classes**: document the class definition and use `@slot` for slots. ## Generics S4 **generics** are functions, so document them as such. `@export` a generic if you want users to call it or other developers to write methods for it. If the generic is internal, you don't need to export or document it. ## Classes Document **S4 classes** by adding a roxygen block before `setClass()`. `@export` a class if you want users to create instances or other developers to extend it (e.g. by creating subclasses). Internal classes don't need documentation. Use `@slot` to document the slots of the class. Here's a simple example: ```{r} #| eval: false #' An S4 class to represent a bank account #' #' @slot balance A length-one numeric vector #' @export Account <- setClass("Account", slots = list(balance = "numeric")) ``` ## Methods S4 **methods** are a little more complicated. Unlike S3 and S7 methods, all S4 methods must be documented. You only need to `@export` a method if the generic lives in another package. You can document methods in three places: - In the class. Most appropriate if the corresponding generic uses single dispatch and you created the class. - In the generic. Most appropriate if the generic uses multiple dispatches and you control it. - In its own file. Most appropriate if the method is complex or the either two options don't apply. Use either `@rdname` or `@describeIn` to control where method documentation goes. See `vignette("reuse")` for more details. roxygen2/inst/doc/rd-S4.R0000644000176200001440000000062315174650104014571 0ustar liggesusers## ----------------------------------------------------------------------------- knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ## ----------------------------------------------------------------------------- # #' An S4 class to represent a bank account # #' # #' @slot balance A length-one numeric vector # #' @export # Account <- setClass("Account", slots = list(balance = "numeric")) roxygen2/inst/doc/namespace.html0000644000176200001440000004063415174650103016404 0ustar liggesusers Managing imports and exports

    Managing imports and exports

    The package NAMESPACE is one of the most confusing parts of building a package. roxygen2 aims to make it as easy as possible to build a package that is a well-behaved member of the R ecosystem. This is a little frustrating at first, but soon becomes second-nature.

    Exports

    In order for your users to use a function1 from your package, you must export it. In most cases, you can just use the @export tag, and roxygen2 will automatically figure out which NAMESPACE directive (i.e. export(), S3method(), exportClasses(), or exportMethods()) you need.

    For details and examples of exporting functions, and S3, S4, and S7 classes/generics/methods, see the corresponding vignettes:

    • vignette("rd-functions") for regular functions.
    • vignette("rd-S3") for S3 generics and methods, including delayed registration with @exportS3Method.
    • vignette("rd-S4") for S4 generics, classes, and methods.
    • S7 methods are registered at load time, not through NAMESPACE; see vignette("rd-S7").

    Datasets should never be exported as they are not found in NAMESPACE. Learn more in vignette("rd-datasets").

    Manual exports

    If @export does not automatically generate the correct NAMESPACE directive, you can use one of the tags below to exercise greater control:

    • @export foo generates export(foo)
    • @exportS3Method generic method generates S3method(generic, method)
    • @exportClass foo generates exportClasses(foo)
    • @exportMethod foo generates exportMethods(foo)
    • @exportPattern foo generates exportPattern(foo)

    For even more specialised cases you can use @rawNamespace code which inserts code literally into the NAMESPACE. This is useful if you need a conditional import or export, e.g.

    # From dplyr:
    #' @rawNamespace import(vctrs, except = data_frame)
    NULL
    #> NULL
    
    # From backports:
    #' @rawNamespace if (getRversion() < "4.0.0") export(stopifnot)
    NULL
    #> NULL

    If you need to automate this, @evalNamespace fun() will evaluate fun() in your package environment and insert the results into NAMESPACE. Note that because evalNamespace() is run in the package environment, it can only generate exports, not imports.

    Imports

    The NAMESPACE also controls which functions from other packages are made available to your package.

    Functions

    If you are using just a few functions from another package, we recommending adding the package to the Imports: field of the DESCRIPTION file and calling the functions explicitly using ::, e.g., pkg::fun().

    my_function <- function(x, y) {
      pkg::fun(x) * y
    }

    If the repetition of the package name becomes annoying you can @importFrom and drop the :::

    #' @importFrom pkg fun
    my_function <- function(x, y) {
      fun(x) * y
    }

    Imports affect every function in a package, so it’s common to collect them in a central place, like {packagename}-package.R. This is automated by usethis::use_import_from().

    #' @importFrom pkg fun1 fun2
    #' @importFrom pkg2 fun3
    #' @importFrom pkg3 fun4
    NULL
    #> NULL

    Note the use of NULL here: you must provide something for roxygen2 to document, so we use NULL as placeholder.

    It is possible, but not generally recommended to import all functions from a package with @import package. This is risky if you import functions from more than one package, because while it might be ok today, in the future the packages might end up with a function having the same name, and your users will get a warning every time your package is loaded.

    S3

    S3 generics are just functions, so the same rules for functions apply. S3 methods always accompany the generic, so as long as you can access the generic, the methods will also be available. In other words, you don’t need to do anything to import an S3 method.

    S4

    • To use classes defined in another package, place @importClassesFrom package ClassA ClassB ... next to the classes that inherit from the imported classes, or next to the methods that implement a generic for the imported classes.
    • To use generics defined in another package, place @importMethodsFrom package GenericA GenericB ... next to the methods that use the imported generics.

    Compiled code

    To import compiled code from another package, use @useDynLib

    • @useDynLib package imports all compiled functions.

    • @useDynLib package routinea routineb imports selected compiled functions.

    • Any @useDynLib specification containing a comma, e.g. @useDynLib mypackage, .registration = TRUE will be inserted as is into the the NAMESPACE, e.g. useDynLib(mypackage, .registration = TRUE)


    1. Including S3/S4/S7 generics and constructors.↩︎

    roxygen2/inst/doc/namespace.Rmd0000644000176200001440000001162515172221402016152 0ustar liggesusers--- title: "Managing imports and exports" description: > Generating the `NAMESPACE` file with roxygen2. output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Managing imports and exports} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r} #| include: false knitr::opts_chunk$set(comment = "#>", collapse = TRUE) ``` The package `NAMESPACE` is one of the most confusing parts of building a package. roxygen2 aims to make it as easy as possible to build a package that is a well-behaved member of the R ecosystem. This is a little frustrating at first, but soon becomes second-nature. ## Exports In order for your users to use a function[^1] from your package, you must **export** it. In most cases, you can just use the `@export` tag, and roxygen2 will automatically figure out which `NAMESPACE` directive (i.e. `export()`, `S3method()`, `exportClasses()`, or `exportMethods()`) you need. [^1]: Including S3/S4/S7 generics and constructors. For details and examples of exporting functions, and S3, S4, and S7 classes/generics/methods, see the corresponding vignettes: - `vignette("rd-functions")` for regular functions. - `vignette("rd-S3")` for S3 generics and methods, including delayed registration with `@exportS3Method`. - `vignette("rd-S4")` for S4 generics, classes, and methods. - S7 methods are registered at load time, not through `NAMESPACE`; see `vignette("rd-S7")`. Datasets should never be exported as they are not found in `NAMESPACE`. Learn more in `vignette("rd-datasets")`. ### Manual exports If `@export` does not automatically generate the correct `NAMESPACE` directive, you can use one of the tags below to exercise greater control: - `@export foo` generates `export(foo)` - `@exportS3Method generic method` generates `S3method(generic, method)` - `@exportClass foo` generates `exportClasses(foo)` - `@exportMethod foo` generates `exportMethods(foo)` - `@exportPattern foo` generates `exportPattern(foo)` For even more specialised cases you can use `@rawNamespace code` which inserts `code` literally into the `NAMESPACE`. This is useful if you need a conditional import or export, e.g. ```{r} # From dplyr: #' @rawNamespace import(vctrs, except = data_frame) NULL # From backports: #' @rawNamespace if (getRversion() < "4.0.0") export(stopifnot) NULL ``` If you need to automate this, `@evalNamespace fun()` will evaluate `fun()` in your package environment and insert the results into `NAMESPACE`. Note that because `evalNamespace()` is run in the package environment, it can only generate exports, not imports. ## Imports The `NAMESPACE` also controls which functions from other packages are made available to your package. ### Functions If you are using just a few functions from another package, we recommending adding the package to the `Imports:` field of the `DESCRIPTION` file and calling the functions explicitly using `::`, e.g., `pkg::fun()`. ```r my_function <- function(x, y) { pkg::fun(x) * y } ``` If the repetition of the package name becomes annoying you can `@importFrom` and drop the `::`: ```r #' @importFrom pkg fun my_function <- function(x, y) { fun(x) * y } ``` Imports affect every function in a package, so it's common to collect them in a central place, like `{packagename}-package.R`. This is automated by `usethis::use_import_from()`. ```{r} #' @importFrom pkg fun1 fun2 #' @importFrom pkg2 fun3 #' @importFrom pkg3 fun4 NULL ``` Note the use of `NULL` here: you must provide something for roxygen2 to document, so we use `NULL` as placeholder. It is possible, but not generally recommended to import all functions from a package with `@import package`. This is risky if you import functions from more than one package, because while it might be ok today, in the future the packages might end up with a function having the same name, and your users will get a warning every time your package is loaded. ### S3 S3 generics are just functions, so the same rules for functions apply. S3 methods always accompany the generic, so as long as you can access the generic, the methods will also be available. In other words, you don't need to do anything to import an S3 method. ### S4 - To use classes defined in another package, place `@importClassesFrom package ClassA ClassB ...` next to the classes that inherit from the imported classes, or next to the methods that implement a generic for the imported classes. - To use generics defined in another package, place `@importMethodsFrom package GenericA GenericB ...` next to the methods that use the imported generics. ### Compiled code To import compiled code from another package, use `@useDynLib` - `@useDynLib package` imports all compiled functions. - `@useDynLib package routinea routineb` imports selected compiled functions. - Any `@useDynLib` specification containing a comma, e.g. `@useDynLib mypackage, .registration = TRUE` will be inserted as is into the the NAMESPACE, e.g. `useDynLib(mypackage, .registration = TRUE)` roxygen2/inst/doc/rd-formatting.html0000644000176200001440000010252715174650106017230 0ustar liggesusers (R)Markdown support

    (R)Markdown support

    We expect most roxygen2 users will write documentation using markdown rather than Rd syntax, since it’s familiar, and doesn’t require learning any new syntax. In most cases, you can just use your existing RMarkdown knowledge and it’ll work as you expect. When it doesn’t, you can read this vignette to figure out what’s going on and how to fix it.

    Enabling markdown support

    To turn on Markdown support for a package, insert this entry into the DESCRIPTION file of the package:

    Config/roxygen2/markdown: TRUE

    If you use devtools/usethis, this will be automatically inserted for you when you create a new package. If you’re updating an existing package, we recommend usethis::use_roxygen_md() which will modify the DESCRIPTION and prompt you to use the roxygen2md package to convert your existing docs.

    If needed, you can also use @md or @noMd to turn markdown support on or off for a documentation block.

    Here is an example roxygen chunk that uses Markdown.

    #' Use roxygen to document a package
    #'
    #' This function is a wrapper for the [roxygen2::roxygenize()] function from
    #' the roxygen2 package. See the documentation and vignettes of
    #' that package to learn how to use roxygen.
    #'
    #' @param pkg package description, can be path or package name.  See
    #'   [as.package()] for more information.
    #' @param clean,reload Deprecated.
    #' @inheritParams roxygen2::roxygenise
    #' @seealso [roxygen2::roxygenize()], `browseVignettes("roxygen2")`
    #' @export

    Basic syntax

    roxygen uses the commonmark package, based on the “CommonMark Reference Implementation”. See https://commonmark.org/help/ for more about the parser and the markdown language it supports. The most important details are described below.

    Sections and subsections

    The usual Markdown heading markup creates sections and subsections. Top level headings (e.g. # title) create sections with the \section{} Rd tag. This largely supersedes use of the older @section tag.

    Top-level headings can only appear after the @description and @details tags. Since @details can appear multiple times in a block, you can always precede a ‘#’ section with @details, if you want put it near the end of the block, after @return for example:

    #' @details
    #' Trim the leading and trailing whitespace from a character vector.
    #'
    #' @param x Character vector.
    #' @return Character vector, with the whitespace trimmed.
    #'
    #' @details # This will be a new section
    #' ...

    Top level sections are placed at a fixed position in the manual page, after the parameters and the details, but before \note{}, \seealso{} and the \examples{}. Their order will be the same as in the roxygen block.

    Headings at level two and above may appear inside any roxygen tag that formats lines of text, e.g. @description, @details, @return, and create subsections with the \subsection{} Rd tag.

    #' @details
    #' ## Subsection within details
    #' ### Sub-subsection
    #' ... text ...

    Inline formatting

    For emphasis, put the text between asterisks or underline characters. For strong text, use two asterisks at both sides.

    #' @references
    #' Robert E Tarjan and Mihalis Yannakakis. (1984). Simple
    #' linear-time algorithms to test chordality of graphs, test acyclicity
    #' of hypergraphs, and selectively reduce acyclic hypergraphs.
    #' *SIAM Journal of Computation* **13**, 566-579.
    #' See `::is_falsy` for the definition of what is _falsy_
    #' and what is _truthy_.

    Code

    Inline code is supported via backticks.

    #' @param ns Optionally, a named vector giving prefix-url pairs, as
    #'   produced by `xml_ns`. If provided, all names will be explicitly
    #'   qualified with the ns prefix, i.e. if the element `bar` is defined ...

    For blocks of code, put your code between triple backticks:

    #' ```
    #' pkg <- make_packages(
    #'   foo1 = { f <- function() print("hello!") ; d <- 1:10 },
    #'   foo2 = { f <- function() print("hello again!") ; d <- 11:20 }
    #' )
    #' foo1::f()
    #' foo2::f()
    #' foo1::d
    #' foo2::d
    #' dispose_packages(pkg)
    #' ```

    You can also include executable code chunks using the usual knitr syntax. See below for more details.

    Lists

    Regular Markdown lists are recognized and converted to \enumerate{} or \itemize{} lists:

    #' There are two ways to use this function:
    #' 1. If its first argument is not named, then it returns a function
    #'    that can be used to color strings.
    #' 1. If its first argument is named, then it also creates a
    #'    style with the given name. This style can be used in
    #'    `style`. One can still use the return value
    #'    of the function, to create a style function.
    #' The style (the `...` argument) can be anything of the
    #' following:
    #' * An R color name, see `colors()`.
    #' * A 6- or 8-digit hexa color string, e.g. `#ff0000` means
    #'   red. Transparency (alpha channel) values are ignored.
    #' * A one-column matrix with three rows for the red, green,
    #'   and blue channels, as returned by [grDevices::col2rgb()].

    Note that you do not have to leave an empty line before the list. This is different from some Markdown parsers.

    Tables

    Use GFM table formatting:

    | foo | bar |
    | --- | --- |
    | baz | bim |

    By default, columns are left-aligned. Use colons to generate right and center aligned columns:

    | left | center | right |
    | :--- | :----: | ----: |
    | 1    | 2      | 3     |

    Images

    Markdown syntax for inline images works. The image files must be in the man/figures directory:

    #' Here is an example plot:
    #' ![](example-plot.jpg "Example Plot Title")
    Markdown Code Rd
    [func()] yes \code{\link[=func]{func()}}
    [topic] no \link{topic}
    [`topic`] yes \code{\link{topic}}

    R 4.5.0 and later requires that links to all topics outside of your package be explicitly qualified with the package name. roxygen2 will attempt to do this for you by looking at the dependencies of your package. If this fails, e.g. if multiple dependencies document the same topic, you’ll need to manually resolve using the syntax below.

    Markdown Code Rd
    [pkg::func()] yes \code{\link[pkg:func]{pkg::func()}}
    [pkg::topic] no \link[pkg:topic]{pkg::topic}
    [`pkg::topic`] yes \code{\link[pkg:topic]{pkg::topic}}

    You can also provide your own link text. In this case there’s no advantage to supplying a function call, as you control the formatting of the text.

    Markdown Code Rd
    [text][topic] no \link[=topic]{text}
    [`text`][topic] yes \code{\link[=topic]{text}}
    [text][pkg::topic] no \link[pkg:topic]{text}
    [`text`][pkg::topic] yes \code{\link[pkg:topic]{text}}

    Code chunks

    You can insert executable code with ```{r} just like in knitr documents. For example:

    #' @title Title
    #' @details Details
    #' ```{r lorem}
    #' 1+1
    #' ```
    #' @md
    foo <- function() NULL

    becomes:

     % Generated by roxygen2: do not edit by hand
    % Please edit documentation in ./<text>
    \name{foo}
    \alias{foo}
    \title{Title}
    \usage{
    foo()
    }
    \description{
    Title
    }
    \details{
    Details
    
    \if{html}{\out{<div class="sourceCode r">}}\preformatted{1+1
    #> [1] 2
    }\if{html}{\out{</div>}}
    } 

    This code is run every time you call roxygenize() (or devtools::document()) to generate the Rd files. This potentially makes roxygenize() (much) slower. Either avoid expensive computations, or turn on knitr caching with cache = TRUE. Make sure to omit the cache from the package with usethis::use_build_ignore().

    Note that knitr will call the appropriate print() or (if available) knitr::knit_print() method on the result. This may generate markdown not supported by roxygen2. If needed, override the automatic methods to have your R calls return your own markdown as a character vector, wrapped in knitr::asis_output().

    You can also use `r ` to insert the results of running R code. This is particularly useful for reducing duplication in your documentation so is documented in vignette("reuse").

    Chunk options

    Code blocks support some knitr chunk options, e.g. to keep the output of several expressions together, you can specify results="hold":

    #' ```{r results="hold"}
    #' names(mtcars)
    #' nrow(mtcars)
    #' ```

    Some knitr chunk options are reset at the start of every code block, so if you want to change these, you’ll have to specify them for every chunk. These are currently error, fig.path, fig.process, comment, collapse.

    Alternatively, you can set the knitr_chunk_options option to override these defaults, or add new chunk options that are used for the whole package. See ?load_options for specifying roxygen2 options.

    Images

    Plots will create .png files in the man/figures directory with file names coming from the chunk name. Be aware that plots can quickly increase the size of your package leading to challenges for CRAN submission.

    #' ```{r iris-pairs-plot}
    #' pairs(iris[1:4], main = "Anderson's Iris Data -- 3 species",
    #'   pch = 21, bg = c("red", "green3", "blue")[unclass(iris$Species)])
    #' ```

    By default roxygen2 only includes PDF images in the PDF manual, and SVG images in the HTML manual. If you want to avoid this restriction, set the restrict_image_formats roxygen2 option to FALSE, see ?load_options.

    Possible problems

    Some Rd tags can’t contain markdown

    When mixing Rd and Markdown notation, most Rd tags may contain Markdown markup, the ones that can not are: \acronym, \code, \command, \CRANpkg, \deqn, \doi, \dontrun, \dontshow, \donttest, \email, \env, \eqn, \figure, \file, \if, \ifelse, \kbd, \link, \linkS4class, \method, \mjeqn, \mjdeqn, \mjseqn, \mjsdeqn, \mjteqn, \mjtdeqn, \newcommand, \option, \out, \packageAuthor, \packageDescription, \packageDESCRIPTION, \packageIndices, \packageMaintainer, \packageTitle, \pkg, \PR, \preformatted, \renewcommand, \S3method, \S4method, \samp, \special, \testonly, \url, \var, \verb.

    Mixing Markdown and Rd markup

    Note that turning on Markdown does not turn off the standard Rd syntax. We suggest that you use the regular Rd tags in a Markdown roxygen chunk only if necessary. The two parsers do occasionally interact, and the Markdown parser can pick up and reformat Rd syntax, causing an error, or corrupted manuals.

    Leading white space

    Leading white space is interpreted by the commonmark parser, but is ignored by the Rd parser (except in \preformatted{}). Make sure that you only include leading white space intentionally, for example, in nested lists.

    Spurious lists

    The commonmark parser does not require an empty line before lists, and this might lead to unintended lists if a line starts with a number followed by a dot, or with an asterisk followed by white space:

    #' You can see more about this topic in the book cited below, on page
    #' 42. Clearly, the numbered list that starts here is not intentional.
    roxygen2/inst/doc/extending.Rmd0000644000176200001440000003404315172221402016202 0ustar liggesusers--- title: "Extending roxygen2" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Extending roxygen2} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r} #| include: false knitr::opts_chunk$set(comment = "#>", collapse = TRUE) ``` roxygen2 is extensible, and this vignette will show you how. It starts with an introduction to the basic workflow of `roxygenize()` and the key data structures that power it. Then we'll show you how you can use its two extension points: - Add a new tag to generate a new top-level section in an `.Rd` file. This allows you to repeat yourself less when documenting your package. (See `vignette("reuse")` for other techniques.) - Add a new roclet. This lets you take full advantage of the computational machinery behind `roxygenize()` to compute anything you want or produce any artefact you can imagine. ```{r} #| label: setup library(roxygen2) ``` ## How `roxygenize()` works You've probably used `roxygenize()` (or `devtools::document()`) a bunch without ever really thinking about what's going on behind the scenes. But if you're going to extend roxygen2, you'll need to know exactly what's happening: - Loads the package under roxygenizing, as well as any further packages ("packages" in `load_options()`). - Parses all R files in the package, using available tags. - Finds the roclets (see `roclet()`) from its `roclets` argument or the "roclets" option (`load_options()`). It defaults to using the Collate "roclet", the Rd roclet, and NAMESPACE roclet, but you can also add your own. - Runs the different methods of all those roclets, in order and independently: clean (`roclet_clean`), preprocess (`roclet_preprocess()`), process (`roclet_process()`), and output (`roclet_output()`). Only process and output are routinely used. For example, if you think of the Rd roclet, its process method digests information from the tag parsing, combines inherits, etc. to create the content of each documentation topic, and its output method writes those topics to disk. ## Key data structures Before we dive into extending roxygen2, we need to first discuss two important data structures that power roxygen: tags and blocks. ### Tags A tag (a list with S3 class `roxy_tag`) represents a single tag. It has the following fields: - `tag`: the name of the tag. - `raw`: the raw contents of the tag (i.e. everything from the end of this tag to the beginning of the next). - `val`: the parsed value, which we'll come back to shortly. - `file` and `line`: the location of the tag in the package. Used with `roxy_tag_warning()` to produce informative error messages. You *can* construct tag objects by hand with `roxy_tag()`: ```{r} roxy_tag("name", "Hadley") str(roxy_tag("name", "Hadley")) ``` However, you should rarely need to do so (except in tests), because you'll typically have them given to you in a block object, as you'll see shortly. ### Blocks A block (a list with S3 class `roxy_block`) represents a single roxygen block. It has the following fields: - `tags`: a list of `roxy_tags`. - `call`: the R code associated with the block (usually a function call). - `file` and `line`: the location of the R code. - `object`: the evaluated R object associated with the code. The easiest way to see the basic structure of a `roxy_block()` is to generate one by parsing a roxygen block with `parse_text()`: ```{r} text <- " #' This is a title #' #' This is the description. #' #' @param x,y A number #' @export f <- function(x, y) x + y " # parse_text() returns a list of blocks, so I extract the first block <- parse_text(text)[[1]] block ``` You'll notice that some of the tags didn't exist in the original block: - `@title` and `@description` are extracted from the text that appears before the first explicit tag. - `@usage` is generated automatically from the function formals. - `@.formals` is an "internal" tag that doesn't generate any output but is used to pass some important data around. - `@backref` stores the source location of the block so we can later record which `.R` files contributed to each `.Rd` file. ## Adding a new `.Rd` tag The most common way to extend roxygen2 is to create a new tag that adds output to `.Rd` files. This requires defining a few methods: 1. Define a `roxy_tag_parse()` method that describes how to parse our new tag. 2. Define a `roxy_tag_rd()` method that describes how to convert the tag into `.Rd` commands. 3. If the tag's content is meant to appear in a custom section (as opposed to, say, the examples section), define a `format()` method that describes how to create the `.Rd` string. To illustrate the basic idea, we'll create a new `@tip` tag that will create a bulleted list of tips about how to use a function. The idea is to take something like this: ```{r} #' @tip The mean of a logical vector is the proportion of `TRUE` values. #' @tip You can compute means of dates and date-times! ``` That generates Rd like this: ```latex \section{Tips and tricks}{ \itemize{ \item The mean of a logical vector is the proportion of \code{TRUE} values. \item You can compute means of dates and date-times! } } ``` The first step is to define a method for `roxy_tag_parse()` that describes how to parse the tag text. The name of the class will be `roxy_tag_{tag}`, which in this case is `roxy_tag_tip`. This function takes a `roxy_tag` as input, and its job is to set `x$val` to a convenient parsed value that will be used later by the roclet. Here we want to process the text using Markdown so we can just use `tag_markdown()`: ```{r} roxy_tag_parse.roxy_tag_tip <- function(x) { tag_markdown(x) } ``` (There are lots of other built in options that you can read about in `?tag_markdown`.) ```{r} #| include: false # Needed for vignette registerS3method("roxy_tag_parse", "roxy_tag_tip", roxy_tag_parse.roxy_tag_tip) ``` We can check this works by using `parse_text()`: ```{r} text <- " #' Title #' #' @tip The mean of a logical vector is the proportion of `TRUE` values. #' @tip You can compute means of dates and date-times! #' @md f <- function(x, y) { # ... } " block <- parse_text(text)[[1]] block str(block$tags[[2]]) ``` Here I explicitly turn Markdown parsing on using `@md`; it's usually turned on for a package using roxygen2's `load_options()`. Next, we define a method for `roxy_tag_rd()`, which must create an `rd_section()`. We're going to create a new custom section called `tip`. It will contain a character vector of tips: ```{r} roxy_tag_rd.roxy_tag_tip <- function(x, base_path, env) { rd_section("tip", x$val) } ``` ```{r} #| include: false # Needed for vignette registerS3method("roxy_tag_rd", "roxy_tag_tip", roxy_tag_rd.roxy_tag_tip) ``` This additional layer is needed because there can be multiple tags of the same type in a single block, and multiple blocks can contribute to the same `.Rd` file. The job of `rd_section()` is to combine all the tags into a single top-level Rd section. Each tag generates an `rd_section` which is then combined with any previous section using `merge()`. The default `merge.rd_section()` just concatenates the values together (`rd_section(x$type, c(x$value, y$value))`); you can override this method if you need more sophisticated behaviour. We called the custom section "tip" just like our tag, but we needn't have done so: it really depends on how you want to map the input tags to output Rd code. We then need to define a `format()` method to convert this object into text for the `.Rd` file: ```{r} format.rd_section_tip <- function(x, ...) { paste0( "\\section{Tips and tricks}{\n", "\\itemize{\n", paste0(" \\item ", x$value, "\n", collapse = ""), "}\n", "}\n" ) } ``` ```{r} #| include: false # Needed for vignette registerS3method("format", "rd_section_tip", format.rd_section_tip) ``` We can now try this out with `roclet_text()`: ```{r} topic <- roc_proc_text(rd_roclet(), text)[[1]] topic$get_section("tip") ``` Note that there is no namespacing so if you're defining multiple new tags I recommend using your package name as the common prefix. ### Using your new tag Now that the three methods are created, we still need to make them available to `roxygenize()`. First, you need to export the method so that it's registered correctly. Since your methods will be for generics defined in roxygen2 (another package), you'll need to follow the advice in [Methods for generics in other packages](rd-S3.html#methods-for-generics-in-other-packages). Next, you'll need to load the tag-defining package in the package where you want to use it. For example, if you created some new tags in {packageFoo}, and would like to use these tags in your documentation for {packageBar}, append this line to the `DESCRIPTION` of {packageBar}: ``` Config/roxygen2/packages: packageFoo ``` See `load_options()` for more details. ## Creating a new roclet Creating a new roclet is usually a two part process. First, you define new tags that your roclet will work with, unless your roclet only needs information from existing tags, or only needs the path to the package source[^vignette]. Second, you define a roclet that tells `roxygenize()` what to compute and produce based on this information. [^vignette]: For example, the no-longer recommended `vignette_roclet()` only needs the path to the package source as input; it does not use information from the tag parsing step. Or the {roxylint} package only uses existing tags; its job is to warn you about suboptimal roxygen2 style. ### Custom tags In this example we will make a new `@memo` tag which helps you to remember what you're planning to work on in the future by displaying notes when you document your package. We choose this syntax for `@memo`: ```{r} #' @memo [Headline] Description ``` For example: ```{r} #' @memo [EFFICIENCY] Currently brute-force; find better algorithm. ``` As above, we first define a parse method. This time we use custom format based on a regular expression: ```{r} roxy_tag_parse.roxy_tag_memo <- function(x) { if (!grepl("^\\[.*\\].*$", x$raw)) { roxy_tag_warning(x, "Invalid memo format") return() } parsed <- regmatches(x$raw, regexec("\\[(.*)\\](.*)", x$raw))[[1]] x$val <- list( header = parsed[[2]], message = parsed[[3]] ) x } ``` ```{r} #| include: false # Needed for vignette registerS3method( "roxy_tag_parse", "roxy_tag_memo", roxy_tag_parse.roxy_tag_memo ) ``` Then we check it works with `parse_text()`: ```{r} text <- " #' @memo [TBI] Remember to implement this! #' @memo [API] Check best API f <- function(x, y) { # ... } " block <- parse_text(text)[[1]] block str(block$tags[[1]]) ``` We don't need a format method because our tag won't be used to produce Rd sections[^tag]. [^tag]: Some tags in roxygen2 itself, like `@importFrom`, are not meant for the [`rd_roclet()`] (`@importFrom` is meant for the `namespace_roclet()`). ### The roclet Next, we create a constructor for the roclet, which uses `roclet()`. Our `memo` roclet doesn't have any options so this is very simple: ```{r} memo_roclet <- function() { roclet("memo") } ``` To give the roclet behaviour, you need to define methods. There are two methods that almost every roclet will use: - `roclet_process()` is called with a list of blocks, and returns an object of your choosing. - `roclet_output()` produces side-effects (usually writing to disk) using the result from `roclet_process()`. For this roclet, we'll have `roclet_process()` collect all the memo tags into a named list: ```{r} roclet_process.roclet_memo <- function(x, blocks, env, base_path) { results <- list() for (block in blocks) { tags <- block_get_tags(block, "memo") for (tag in tags) { msg <- paste0("[", tag$file, ":", tag$line, "] ", tag$val$message) results[[tag$val$header]] <- c(results[[tag$val$header]], msg) } } results } ``` And then have `roclet_output()` print them to the screen: ```{r} roclet_output.roclet_memo <- function(x, results, base_path, ...) { for (header in names(results)) { messages <- results[[header]] cat(paste0(header, ": ", "\n")) cat(paste0(" * ", messages, "\n", collapse = "")) } invisible(NULL) } ``` ```{r} #| include: false # Needed for vignette registerS3method("roclet_process", "roclet_memo", roclet_process.roclet_memo) registerS3method("roclet_output", "roclet_memo", roclet_output.roclet_memo) ``` Then you can test if it works by using `roc_proc_text()`: ```{r} results <- roc_proc_text( memo_roclet(), " #' @memo [TBI] Remember to implement this! #' @memo [API] Check best API f <- function(x, y) { # ... } #' @memo [API] Consider passing z option g <- function(x, y) { # ... } " ) roclet_output(memo_roclet(), results) ``` ## Adding a roclet to your workflow To use a roclet when developing a package, call ```r roxygen2::roxygenize(roclets = "yourPackage::roclet") ``` where `yourPackage::roclet` is the function which creates the roclet, e.g. `memo_roclet` above. You can also add the roclet to the target package's DESCRIPTION file, like this: ```r Config/roxygen2/roclets: collate, rd, namespace, yourPackage::roclet ``` See `load_options()` for more details. Your package only needs to be installed when the user documents the package, which doesn't correspond precisely to any field in the `DESCRIPTION`. However, you can think of it as a development dependency and hence put it in the `Suggests:` field: ```r usethis::use_package("yourPackage", type = "Suggests") ``` You don't have to do this, but it will help other developers working on the target package. ## Conclusion: further extension ideas This vignette is quite rough, so you might want to also read some roxygen2 source code to understand all the extension points. Do not hesitate to also look for examples of roxygen2 extensions. For instance, the [roxygenlabs](https://github.com/gaborcsardi/roxygenlabs) package (former incubator of roxygen2 features) used a third extension point: it *extended* the Rd roclet with further methods, thus creating a supercharged Rd roclet rather than a brand-new roclet. Or, the [plumber2](https://github.com/posit-dev/plumber2) package only uses the parsing features from roxygen2, and does not use `roxygenize()` at all. roxygen2/inst/doc/index-crossref.Rmd0000644000176200001440000000630415172221402017147 0ustar liggesusers--- title: "Indexing and cross-references" output: rmarkdown::html_vignette description: > Make it easier for users to find your functions by cross-referencing them and control their indexing. vignette: > %\VignetteIndexEntry{Indexing and cross-references} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r} #| include: false knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ``` This vignette discusses tags that help users finding documentation through cross-references and indexes. ## See also `@seealso` allows you to point to other useful resources, either on the web (using a url) or to related functions (with a function link like `[function_name()]`). For `sum()`, this might look like: ```{r} #' @seealso [prod()] for products, [cumsum()] for cumulative sums, and #' [colSums()]/[rowSums()] marginal sums over high-dimensional arrays. ``` ## Family If you have a family of related functions, you can use `@family {family}` to cross-reference each function to every member of the family. A function can be a member of multiple families. By default `@family {family}`, will generate the see also text "Other {family}:", so the `@family` name should be plural (i.e., "model building helpers" not "model building helper"). If you want to override the default title, you can provide an `rd_family_title` element in a list stored in `man/roxygen/meta.R`: ```{r} #| eval: false list( rd_family_title = list(aggregations = "Aggregation functions") ) ``` ## References If the object you're documenting has connections to the scientific literature, use `@reference` to provide a citation. ## Aliases `?` and `help()` look for topic aliases; `?foo` will find any topic that contains the `foo` alias. roxygen2 generates a default alias for you based on the object you're documenting. You can add additional aliases with `@aliases alias1 alias2 alias3` or remove default alias with `@aliases NULL`. ## Search As well as looking in the aliases, `help.search()` and `???` also look in the `@title`, `@keywords`, and `@concept`s tags. - `@keywords` adds standard keywords, which must be present in `file.path(R.home("doc"), "KEYWORDS")`. - `@concept` adds arbitrary key words or phrases. Each `@concept` should contain a single word or phrase. Generally speaking, `@keywords` and `@concepts` are not terribly useful because most people find documentation using Google, not R's built-in search. There's one exception: `@keywords internal`. It's useful because it removes the function from the documentation index; it's useful for functions aimed primarily at other developers, not typical users of the package. ## Back references The original source location is added as a comment to the second line of each generated `.Rd` file in the following form: ``` % Please edit documentation in ... ``` `roxygen2` tries to capture all locations from which the documentation is assembled. For code that *generates* R code with Roxygen comments (e.g., the Rcpp package), the `@backref` tag is provided. This allows specifying the "true" source of the documentation, and will substitute the default list of source files. Use one tag per source file: ```{r} #' @backref src/file.cpp #' @backref src/file.h ``` roxygen2/inst/doc/rd-S7.html0000644000176200001440000004666415174650105015357 0ustar liggesusers Documenting S7

    Documenting S7

    There are three things that you might document for S7:

    • Generics: mention that the function is a generic and list the available methods.
    • Methods: link back to the generic; only document individually when the method has unique behavior or arguments.
    • Classes: document the constructor.

    Generics

    S7 generics are functions, so document them as such. Export a generic if you want users to call it or other developers to write methods for it. If the generic is internal, you don’t need to document it.

    The documentation should mention that the function is a generic, because this tells the reader that the behavior may vary depending on the input and that they can write their own methods. For simple generics, you can do this in the description:

    #' Size of an object
    #'
    #' @description
    #' `size()` is an S7 generic that determines the size of an object,
    #' with methods available for the following classes:
    #'
    #' `r doclisting::methods_list("size")`
    #'
    #' @param x An object.
    #' @param ... Not used.
    #' @returns A single number.
    #' @export
    size <- new_generic("size", "x")

    For more complicated generics, you can use a # Methods section to provide more detail:

    #' Size of an object
    #'
    #' @description
    #' `size()` determines the size of an object.
    #'
    #' # Methods
    #' `size()` is an S7 generic with methods available for the following
    #' classes:
    #'
    #' `r doclisting::methods_list("size")`
    #'
    #' @param x An object.
    #' @param ... Not used.
    #' @returns A single number.
    #' @export
    size <- new_generic("size", "x")

    See the S3 Generics section for more about using the doclisting package to automatically generate method lists.

    It’s good practice to document the default method alongside the generic using @rdname:

    #' @rdname size
    method(size, class_any) <- function(x, ...) {
      length(x)
    }

    Classes

    S7 classes are constructor functions, so document them much like you’d document any other function. Export a class if you want users to create instances or other developers to extend it (e.g. by creating subclasses). Internal classes don’t need documentation.

    Use @param to document the constructor arguments (which correspond to class properties), and @returns to describe the object that is returned. If the class has additional properties that are not part of the constructor (e.g. read-only computed properties), use @prop to document them.

    #' A range
    #'
    #' Create a range represented by a numeric `start` and `end`. The start must
    #' always be less than the end.
    #'
    #' @param start Start of range.
    #' @param end End of range.
    #' @prop length Length of the range (read-only).
    #' @returns An `Range` S7 object.
    #' @export
    Range <- new_class(
      "Range",
      properties = list(
        start = class_numeric,
        end = class_numeric,
        length = new_property(getter = function(self) self@end - self@start),
        validator = function(self) {
          if (self@start > self@end) {
            "start must be less than or equal to end"
          }
        }
      )
    )

    If multiple classes share one Rd page (via @rdname), you can prefix the property name with the class name to group properties by class, e.g. @prop ClassName@prop_name description.

    Methods

    It is your choice whether or not to document S7 methods. S7 methods are registered with method(generic, class) <- fn. Generally, it’s not necessary to document straightforward methods.

    It’s good practice, however, to document methods that have unique behavior or arguments. If you do document a method, give it its own roxygen block. When documenting a method, always include a link back to the generic using [generic_name()] so the reader can easily find the full documentation and other methods.

    #' Size of a range
    #'
    #' The size of a range is its [size()], i.e. its length.
    #'
    #' @param x A `Range` object.
    #' @param ... Not used.
    #' @returns A single number.
    method(size, Range) <- function(x, ...) {
      x@length
    }

    S7 methods are registered at load time via S7::methods_register() in your .onLoad() function, not through NAMESPACE directives, so you never need to @export them. See vignette("packages", package = "S7") for details.

    roxygen2/inst/doc/reuse.Rmd0000644000176200001440000004156515172221402015347 0ustar liggesusers--- title: "Reusing documentation" description: > Tools for reusing documentation across topics, and between documentation and vignettes. output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Reusing documentation} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r} #| include: false knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ``` roxygen2 provides several ways to avoid repeating yourself in code documentation, while assembling information from multiple places in one documentation file: - Combine documentation for closely related functions into a single file with `@describeIn` or `@rdname`. - Automatically copy tags with `@inheritParams`, `@inheritSection`, or `@inherit`. - Use functions to generate repeated text with inline R code. - Share text between documentation and vignettes with child documents. The chapter concludes by showing you how to update superseded reuse mechanisms that we no longer recommend: `@includeRmd`, `@eval`/`@evalRd`, and `@template`. ## Multiple functions in the same topic You can document multiple functions in the same file by using either `@rdname` or `@describeIn` tag. It's a technique best used with care: documenting too many functions in one place leads to confusion. Use it when all functions have the same (or very similar) arguments. ### `@rdname` Use `@rdname `[^1] to include multiple functions in the same page. Tags (e.g. `@title`, `@description`, `@examples`) will be combined, across blocks but often this yields text that is hard to understand. So we recommend that you make one block that contains the title, description, common parameters, examples and so on, and then only document individual parameters in the other blocks. [^1]: The destination is a topic name. There's a one-to-one correspondence between topic names and `.Rd` files where a topic called `foo` will produce a file called `man/foo.Rd`. There are two basic ways to do that. You can create a standalone documentation block and then add the functions to it. This typically works best when you have a family of related functions and you want to link to that family from other functions (i.e. `trig` in the examples below). ```{r} #' Trigonometric approximations #' @param x Input, in radians. #' @name trig NULL #' @rdname trig #' @export sin_ish <- function(x) x - x^3 / 6 #' @rdname trig #' @export cos_ish <- function(x) 1 - x^2 / 2 #' @rdname trig #' @export tan_ish <- function(x) x + x^3 / 3 ``` Alternatively, you can add docs to an existing function. This tends to work better if you have a "primary" function with some variants or some helpers. ```{r} #| eval: false #' Logarithms #' #' @param x A numeric vector #' @export log <- function(x, base) ... #' @rdname log #' @export log2 <- function(x) log(x, 2) #' @rdname log #' @export ln <- function(x) log(x, exp(1)) ``` ### `@describeIn` An alternative to `@rdname` is `@describeIn`. It has a slightly different syntax because as well as a topic name, you also provide a brief description for the function, like `@describein topic one sentence description`. The primary difference between `@rdname` and `@describeIn`, is that `@describeIn` creates a new section containing a bulleted list of each function, along with its description. It uses a number of heuristics to determine the heading of this section, depending on if you're documenting related functions, methods for a generic, or methods for a class. In general, I no longer recommend `@describeIn` because I don't think the heuristics it uses are as good as a thoughtful hand-crafted summary. If you're currently using `@describeIn`, you can generally replace it with `@rdname`, as long as you give some thought to the multiple-function `@description`. ### Order of includes By default, roxygen blocks are processed in the order in which they appear in the file. When you're combining multiple files, this can sometimes cause the function usage to appear in a suboptimal order. You can override the default ordering with `@order`. For example, the following the block would place `times` first in `arith.Rd` because 1 comes before 2. ```{r} #' @rdname arith #' @order 2 add <- function(x, y) x + y #' @rdname arith #' @order 1 times <- function(x, y) x * y ``` ## Automatically copy tags If two or more functions share have similarities but are different or complex enough that you don't want to document them in a single file, you can use one of the four `@inherit` tags to automatically copy various components from another topic: - `@inheritParams foo` will copy `@param` contents from `foo`. - `@inherit foo` will copy all supported components from `foo`. - `@inheritSection foo {Section title}` will copy the `@section {Section title}` section from `foo`. - `@inheritDotParams foo` will generate documentation for `...` by copying the documentation for `foo()`'s arguments. We think of this as "inheritance" rather than just copying, because anything you inherit can be overridden by a more specific definition in the documentation. This applies particularly to `@inheritParams` which allows you to copy the documentation for some parameters while documenting others directly. We'll focus on this first. ### Parameters The oldest, and most frequently used, inherits tag is `@inheritParams`. It's particularly useful when multiple functions use the same argument name for the same task, as you can document the argument once, then inherit those docs elsewhere. For example, take the dplyr functions `arrange()`, `mutate()`, and `summarise()` which all have an argument called `.data`. `arrange()` is documented like so: ```{r} #' @param .data A data frame, data frame extension (e.g. a tibble), or a #' lazy data frame (e.g. from dbplyr or dtplyr). See *Methods*, below, for #' more details. #' @param ... <[`data-masking`][rlang::args_data_masking]> Variables, or #' functions of variables. Use [desc()] to sort a variable in descending #' order. arrange <- function(.data, ...) {} ``` Then `mutate()` and `summarise()` don't need to provide `@param .data` but can instead inherit the documentation from `arrange()`: ```{r} #' @inheritParams arrange mutate <- function(.data, ...) {} #' @inheritParams arrange summarise <- function(.data, ...) {} ``` If this was all you wrote it wouldn't be quite right because `mutate()` and `summarise()` would also inherit the documentation for `...`, which has a different interpretation in these functions. So, for example, `mutate()` provides its own definition for `...`: ```{r} #' @inheritParams arrange #' @param ... <[`data-masking`][rlang::args_data_masking]> Name-value pairs. #' The name gives the name of the column in the output. #' #' The value can be: #' #' * A vector of length 1, which will be recycled to the correct length. #' * A vector the same length as the current group (or the whole data frame #' if ungrouped). #' * `NULL`, to remove the column. #' * A data frame or tibble, to create multiple columns in the output. mutate <- function(.data, ...) {} ``` Note that only the documentation for arguments with the same names are inherited. For example, `arrange()` also has a `.by_group` argument. Since no other function in dplyr has an argument with this name, its documentation will never be inherited. ### Recursive inheritance `@inheritParams` (like all `@inherits` functions) works recursively, so if `g` inherits parameters from `h`, then `f` can also inherit those parameters from `g`. However, this technique is best used sparingly: it's very easy to create complex dependency webs that are hard to reason about where making changing the documentation in one function cascades out in unexpected ways across your package. You can avoid this problem by being more explicit about which parameters are inherited: - `@inheritParams foo x y` inherits only `x` and `y`. - `@inheritParams foo -z` inherits all parameters except `z`. - `@inheritParams foo first:third` inherits parameters `first` through `third` (in argument order). ### Multiple parameters Sometimes you document two (or more) tightly coupled parameters together. For example, `dplyr::left_join()` has: ```{r} #' @param x,y A pair of data frames, data frame extensions (e.g. a tibble), or #' lazy data frames (e.g. from dbplyr or dtplyr). See *Methods*, below, for #' more details. ``` When joint parameter documentation is inherited, it's all or nothing, i.e. if a function has `@inheritParams left_join` it will only inherit the documentation for `x` and `y` if it has both `x` and `y` arguments and neither is documented by the inheriting function. ### The dot prefix Many tidyverse functions that accept named arguments in `...` also use a `.` prefix for their own arguments. This reduces the risk of an argument going to the wrong place. For example, `dplyr::mutate()` has `.by`, `.keep`, `.before`, and `.after` arguments, because if they didn't have that prefix, you wouldn't be able to create new variables called `by`, `keep`, `before`, or `after`. We call this pattern the [dot prefix](https://design.tidyverse.org/dots-prefix.html). This means that an argument with the same meaning can come in one of two forms: with and without the `.`. `@inheritParams` knows about this common pattern so ignores a `.` prefix when matching argument name. In other words, `.x` will inherit documentation for `x`, and `x` will inherit documentation from `.x`. ### Inheriting other components You can use `@inherit foo` to inherit the documentation for every supported tag from another topic. Currently, `@inherit` supports inheriting the following tags: `r paste0("\x60", roxygen2:::inherit_components, "\x60", collapse = ", ")`. By supplying a space separated list of components after the function name, you can also choose to inherit only selected components. For example, `@inherit foo returns` would just inherit the `@returns` tag, and `@inherit foo seealso source` would inherit the `@seealso` and `@source` tags. `@inherit foo sections` will inherit *every* `@section` tag (which can also be specified in markdown by using the top-level heading spec, `#`). To inherit a *specific* section from another function, use `@inheritSection foo Section title`. For example, all the "adverbs" in purrr use `#' @inheritSection safely Adverbs` to inherit a standard section that provides advice on using an adverb in package code. ### Documenting `...` When your function passes `...` on to another function, it can be useful to inline the documentation for some of the most common arguments. This technique is inspired by the documentation for `plot()`, where `...` can take any graphical parameter; `?plot` describes some of the most common arguments so that you don't have to look them up in `?par`. `@inheritDotParams` has two components: the function to inherit from and the arguments to inherit. Since you'll typically only want to document the most important arguments, `@inheritDotParams` comes with a flexible specification for argument selection inspired by `dplyr::select()`: - `@inheritDotParams foo` takes all parameters from `foo()`. - `@inheritDotParams foo a b e:h` takes parameters `a`, `b`, and all parameters between `e` and `h`. - `@inheritDotParams foo -x -y` takes all parameters except for `x` and `y`. ### Inheriting from other packages It's most common to inherit from other documentation topics within the current package, but roxygen2 also supports inheriting documentation from other packages by using `package::function` syntax, e.g.: - `@inheritParams package::function` - `@inherit package::function` - `@inheritSection package::function Section title` - `@inheritDotParams package::function` When inheriting documentation from another package bear in mind that you're now taking a fairly strong dependency on an external package, and to ensure every developer produces the same documentation you'll need to make sure that they all have the same version of the package installed. And if the package changes the name of the topic or section, your documentation will require an update. For those reasons, this technique is best used sparingly. ## Custom tags Creating a custom tag might be a strategy for repeating yourself less. For instance you could create a tag that allows ```r #' @git rebase ``` to create this Rd section: ```latex \section{Related Git documentation} {\href{https://git-scm.com/docs/git-rebase#_interactive_mode}{\code{git rebase -i}}.} ``` See `vignette("extending")` for more details. ## Inline code To insert code inline, enclose it in `` `r ` ``. Roxygen will interpret the rest of the text within backticks as R code and evaluate it, and replace the backtick expression with its value. Here's a simple example: ```{r} #| include: false roxygen2:::markdown_on() simple_inline <- "#' Title `r 1 + 1` #' #' Description `r 2 + 2` foo <- function() NULL " ``` ```{r, code=simple_inline} ``` This is equivalent to writing: ```{r, code=roxygen2:::markdown(simple_inline)} ``` The resulting text, together with the whole tag is interpreted as markdown, as usual. This means that you can use R to dynamically write markdown. For example if you defined this function in your package: ```{r} alphabet <- function(n) { paste0("`", letters[1:n], "`", collapse = ", ") } ``` You could then write: ```{r} #| echo: false env <- new.env() env$alphabet <- alphabet roxygen2:::roxy_meta_set("evalenv", env) backtick <- "#' Title #' #' @param x A string. Must be one of `r alphabet(5)` foo <- function(x) NULL " ``` ```{r, code=backtick} ``` The result is equivalent to writing the following by hand: ```{r, code=roxygen2:::markdown_evaluate(backtick)} ``` This is a powerful technique for reducing duplication because you can flexibly parameterise the function however best meets your needs. Note that the evaluation environment is deliberately a child of the package that you're documenting so you can call internal functions. ## Inline Rd code You can use `` `Rd ` `` to run code that is evaluated when a user views the help page, rather than when roxygen2 builds the documentation. This is useful for documentation that needs to be dynamic, reflecting the current state of R, of the user's library and system (e.g. what dependencies are installed), not the state when the package was built. It works by using the `\Sexpr` tag, so that `` `Rd myFun()` `` generates`\Sexpr[stage=render,results=rd]{myFun()}`. Here's a simple example, which will greet the user appropriately, depending on the time of day: ```{r} #| eval: false greeting <- function() { hour <- as.POSIXlt(Sys.time(), tz = "UTC")$hour if (hour < 12) { "Good morning!" } else if (hour < 18) { "Good afternoon!" } else { "Good evening!" } } #' Title #' #' `Rd roxygen2:::greeting()` foo <- function() NULL ``` Note that compared to inline R code, the function is run in the global environment by R's documentation system, not inside the package namespace by roxygen2. This generally means that you'll want to use `:::` to explicitly call an internal function. ## Child documents You can use the same `.Rmd` or `.md` document in the documentation, `README.Rmd`, and vignettes by using child documents: ```` ```{r child = "common.Rmd"}`r ''` ``` ```` The included Rmd file can have roxygen Markdown-style links to other help topics. E.g. `[roxygen2::roxygenize()]` will link to the manual page of the `roxygenize` function in roxygen2. See `vignette("rd-formatting")` for details. If the Rmd file contains roxygen (Markdown-style) links to other help topics, then some care is needed, as those links will not work in Rmd files by default. A workaround is to specify external HTML links for them. These external locations will *not* be used for the manual which instead always links to the help topics in the manual. Example: ``` See also the [roxygen2::roxygenize()] function. [roxygen2::roxygenize()]: https://roxygen2.r-lib.org/reference/roxygenize.html ``` This example will link to the supplied URLs in HTML / Markdown files and it will link to the `roxygenize` help topic in the manual. Note that if you add external link targets like these, then roxygen will emit a warning about these link references being defined multiple times (once externally, and once to the help topic). This warning originates in Pandoc, and it is harmless. ## Superseded Over the years, we have experimented with a number of other ways to reduce duplication across documentation files. A number of these are now superseded and we recommend changing them to use the techniques described above: - Instead of `@includeRmd man/rmd/example.Rmd`, use a child document. - Instead of `@eval` or `@evalRd`, use inline R code. - Instead of `@template` and `@templateVars` write your own function and call it from inline R code. Inline R markdown can only generate markdown text within a tag so in principle it is less flexible than `@eval`/`@evalRd`/`@template`. However, our experience has revealed that generating multiple tags at once tends to be rather inflexible, and you often end up refactoring into smaller pieces so we don't believe this reflects a real loss of functionality. roxygen2/inst/doc/rd-S3.R0000644000176200001440000000351015174650104014566 0ustar liggesusers## ----------------------------------------------------------------------------- knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ## ----------------------------------------------------------------------------- # #' Frobnpolicate an object # #' # #' @description # #' `frobnpolicate()` is an S3 generic that ..., with methods available for # #' the following classes: # #' # #' `r doclisting::methods_list("frobnpolicate")` ## ----------------------------------------------------------------------------- # #' Frobnpolicate an object # #' # #' @description # #' `frobnpolicate()` does ... # #' # #' # Methods # #' `frobnpolicate()` is an S3 generic with methods available for the following # #' classes: # #' # #' `r doclisting::methods_list("frobnpolicate")` ## ----------------------------------------------------------------------------- # #' `Rd doclisting::methods_list("frobnpolicate")` ## ----------------------------------------------------------------------------- # ignore_unused_imports <- function() { # doclisting::methods_list # } ## ----------------------------------------------------------------------------- # #' @export # bizarro.character <- function(x, ...) { # letters <- strsplit(x, "") # letters_rev <- lapply(letters, rev) # vapply(letters_rev, paste, collapse = "", FUN.VALUE = character(1)) # } ## ----------------------------------------------------------------------------- # #' @importFrom pkg generic # #' @export # generic.foo <- function(x, ...) {} ## ----------------------------------------------------------------------------- # #' @exportS3Method pkg::generic # generic.foo <- function(x, ...) {} ## ----------------------------------------------------------------------------- # #' @method all.equal data.frame # #' @export # all.equal.data.frame <- function(target, current, ...) { # # ... # } roxygen2/inst/doc/rd-datasets.R0000644000176200001440000000230115174650105016107 0ustar liggesusers## ----------------------------------------------------------------------------- knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ## ----------------------------------------------------------------------------- # #' Prices of over 50,000 round cut diamonds # #' # #' A dataset containing the prices and other attributes of almost 54,000 # #' diamonds. The variables are as follows: # #' # #' @format A data frame with 53940 rows and 10 variables: # #' \describe{ # #' \item{price}{price in US dollars ($326--$18,823)} # #' \item{carat}{weight of the diamond (0.2--5.01)} # #' \item{cut}{quality of the cut (Fair, Good, Very Good, Premium, Ideal)} # #' \item{color}{diamond colour, from D (best) to J (worst)} # #' \item{clarity}{a measurement of how clear the diamond is (I1 (worst), SI2, # #' SI1, VS2, VS1, VVS2, VVS1, IF (best))} # #' \item{x}{length in mm (0--10.74)} # #' \item{y}{width in mm (0--58.9)} # #' \item{z}{depth in mm (0--31.8)} # #' \item{depth}{total depth percentage = z / mean(x, y) = 2 * z / (x + y) (43--79)} # #' \item{table}{width of top of diamond relative to widest point (43--95)} # #' } # #' # #' @source {ggplot2} tidyverse R package. # "diamonds" roxygen2/inst/doc/rd-R6.R0000644000176200001440000000355515174650103014600 0ustar liggesusers## ----------------------------------------------------------------------------- knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ## ----------------------------------------------------------------------------- # #' R6 Class Representing a Person # #' # #' A person has a name and a hair color. # #' @export # Person <- R6::R6Class( # "Person", # public = list( # # ... fields and methods ... # ) # ) ## ----------------------------------------------------------------------------- # Person <- R6::R6Class( # "Person", # public = list( # #' @field name First or full name of the person. # name = NULL, # # #' @field birthdate Date of birth, as a [Date]. # birthdate = NULL # ), # active = list( # #' @field age Age in years, computed from `birthdate` (read-only). # age = function() { # as.numeric(difftime(Sys.Date(), self$birthdate, units = "days")) / 365.25 # } # ) # ) ## ----------------------------------------------------------------------------- # Person <- R6::R6Class( # "Person", # public = list( # #' @description # #' Create a new person object. # #' @param name Name. # #' @param hair Hair color. # #' @returns A new `Person` object. # initialize = function(name = NA, hair = NA) { # self$name <- name # self$hair <- hair # self$greet() # }, # # #' @description # #' Change hair color. # #' @param val New hair color. # set_hair = function(val) { # self$hair <- val # }, # # #' @description # #' Say hi. # greet = function() { # cat(paste0("Hello, my name is ", self$name, ".\n")) # } # ) # ) ## ----------------------------------------------------------------------------- # #' @description # #' Say goodbye. # Person$set("public", "goodbye", function() { # cat(paste0("Goodbye from ", self$name, ".\n")) # }) roxygen2/inst/doc/rd-formatting.Rmd0000644000176200001440000002737415172221402017003 0ustar liggesusers--- title: "(R)Markdown support" description: > The details of the (R)Markdown support provided by roxygen2. output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{(R)Markdown support} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r} #| include: false knitr::opts_chunk$set(comment = "#>", collapse = TRUE) ``` We expect most roxygen2 users will write documentation using markdown rather than Rd syntax, since it's familiar, and doesn't require learning any new syntax. In most cases, you can just use your existing RMarkdown knowledge and it'll work as you expect. When it doesn't, you can read this vignette to figure out what's going on and how to fix it. ## Enabling markdown support To turn on Markdown support for a package, insert this entry into the `DESCRIPTION` file of the package: ``` Config/roxygen2/markdown: TRUE ``` If you use devtools/usethis, this will be automatically inserted for you when you create a new package. If you're updating an existing package, we recommend `usethis::use_roxygen_md()` which will modify the `DESCRIPTION` and prompt you to use the [roxygen2md](https://roxygen2md.r-lib.org) package to convert your existing docs. If needed, you can also use `@md` or `@noMd` to turn markdown support on or off for a documentation block. Here is an example roxygen chunk that uses Markdown. ```r #' Use roxygen to document a package #' #' This function is a wrapper for the [roxygen2::roxygenize()] function from #' the roxygen2 package. See the documentation and vignettes of #' that package to learn how to use roxygen. #' #' @param pkg package description, can be path or package name. See #' [as.package()] for more information. #' @param clean,reload Deprecated. #' @inheritParams roxygen2::roxygenise #' @seealso [roxygen2::roxygenize()], `browseVignettes("roxygen2")` #' @export ``` ## Basic syntax roxygen uses the [commonmark package](https://github.com/r-lib/commonmark), based on the "CommonMark Reference Implementation". See for more about the parser and the markdown language it supports. The most important details are described below. ### Sections and subsections The usual Markdown heading markup creates sections and subsections. Top level headings (e.g. `# title`) create sections with the `\section{}` Rd tag. This largely supersedes use of the older `@section` tag. Top-level headings can only appear after the `@description` and `@details` tags. Since `@details` can appear multiple times in a block, you can always precede a '`#`' section with `@details`, if you want put it near the end of the block, after `@return` for example: ```r #' @details #' Trim the leading and trailing whitespace from a character vector. #' #' @param x Character vector. #' @return Character vector, with the whitespace trimmed. #' #' @details # This will be a new section #' ... ``` Top level sections are placed at a fixed position in the manual page, after the parameters and the details, but before `\note{}`, `\seealso{}` and the `\examples{}`. Their order will be the same as in the roxygen block. Headings at level two and above may appear inside any roxygen tag that formats lines of text, e.g. `@description`, `@details`, `@return`, and create subsections with the `\subsection{}` Rd tag. ```r #' @details #' ## Subsection within details #' ### Sub-subsection #' ... text ... ``` ### Inline formatting For *emphasis*, put the text between asterisks or underline characters. For **strong** text, use two asterisks at both sides. ```r #' @references #' Robert E Tarjan and Mihalis Yannakakis. (1984). Simple #' linear-time algorithms to test chordality of graphs, test acyclicity #' of hypergraphs, and selectively reduce acyclic hypergraphs. #' *SIAM Journal of Computation* **13**, 566-579. ``` ```r #' See `::is_falsy` for the definition of what is _falsy_ #' and what is _truthy_. ``` ### Code Inline code is supported via backticks. ```r #' @param ns Optionally, a named vector giving prefix-url pairs, as #' produced by `xml_ns`. If provided, all names will be explicitly #' qualified with the ns prefix, i.e. if the element `bar` is defined ... ``` For blocks of code, put your code between triple backticks: ````r #' ``` #' pkg <- make_packages( #' foo1 = { f <- function() print("hello!") ; d <- 1:10 }, #' foo2 = { f <- function() print("hello again!") ; d <- 11:20 } #' ) #' foo1::f() #' foo2::f() #' foo1::d #' foo2::d #' dispose_packages(pkg) #' ``` ```` You can also include executable code chunks using the usual knitr syntax. See below for more details. ### Lists Regular Markdown lists are recognized and converted to `\enumerate{}` or `\itemize{}` lists: ```r #' There are two ways to use this function: #' 1. If its first argument is not named, then it returns a function #' that can be used to color strings. #' 1. If its first argument is named, then it also creates a #' style with the given name. This style can be used in #' `style`. One can still use the return value #' of the function, to create a style function. ``` ```r #' The style (the `...` argument) can be anything of the #' following: #' * An R color name, see `colors()`. #' * A 6- or 8-digit hexa color string, e.g. `#ff0000` means #' red. Transparency (alpha channel) values are ignored. #' * A one-column matrix with three rows for the red, green, #' and blue channels, as returned by [grDevices::col2rgb()]. ``` Note that you do not have to leave an empty line before the list. This is different from some Markdown parsers. ### Tables Use [GFM table formatting](https://github.github.com/gfm/#tables-extension-): ```md | foo | bar | | --- | --- | | baz | bim | ``` By default, columns are left-aligned. Use colons to generate right and center aligned columns: ```md | left | center | right | | :--- | :----: | ----: | | 1 | 2 | 3 | ``` ### Links Markdown hyperlinks work as usual: ```r #' See more about the Markdown markup at the #' [Commonmark web site](http://commonmark.org/help) ``` URLs inside angle brackets are also automatically converted to hyperlinks: ```r #' The main R web site is at . ``` ### Images Markdown syntax for inline images works. The image files must be in the `man/figures` directory: ```r #' Here is an example plot: #' ![](example-plot.jpg "Example Plot Title") ``` ## Function links Markdown notation can also be used to create links to other help topics. There are three basic forms for linking to topics in the current package or its dependencies. | Markdown | Code | Rd | | :-------------- | :--- | :---------------------------- | | `[func()]` | yes | `\code{\link[=func]{func()}}` | | `[topic]` | no | `\link{topic}` | | `` [`topic`] `` | yes | `\code{\link{topic}}` | R 4.5.0 and later requires that links to all topics outside of your package be explicitly qualified with the package name. roxygen2 will attempt to do this for you by looking at the dependencies of your package. If this fails, e.g. if multiple dependencies document the same topic, you'll need to manually resolve using the syntax below. | Markdown | Code | Rd | | :------------------- | :--- | :------------------------------------ | | `[pkg::func()]` | yes | `\code{\link[pkg:func]{pkg::func()}}` | | `[pkg::topic]` | no | `\link[pkg:topic]{pkg::topic}` | | `` [`pkg::topic`] `` | yes | `\code{\link[pkg:topic]{pkg::topic}}` | You can also provide your own link text. In this case there's no advantage to supplying a function call, as you control the formatting of the text. | Markdown | Code | Rd | | :------------------------- | :--- | :------------------------------ | | `[text][topic]` | no | `\link[=topic]{text}` | | `` [`text`][topic] `` | yes | `\code{\link[=topic]{text}}` | | `[text][pkg::topic]` | no | `\link[pkg:topic]{text}` | | `` [`text`][pkg::topic] `` | yes | `\code{\link[pkg:topic]{text}}` | ## Code chunks You can insert executable code with ```` ```{r} ```` just like in knitr documents. For example: ````{r} #| echo: false simple_fenced <- "#' @title Title #' @details Details #' ```{r lorem} #' 1+1 #' ``` #' @md foo <- function() NULL " ```` ```{r, code=simple_fenced} ``` becomes: ```{r} #| echo: false #| results: "asis" cat( "\x60\x60\x60rd\n", format(roxygen2:::roc_proc_text(roxygen2::rd_roclet(), simple_fenced)[[1]]), "\n\x60\x60\x60" ) ``` This code is run every time you call `roxygenize()` (or `devtools::document()`) to generate the Rd files. This potentially makes `roxygenize()` (much) slower. Either avoid expensive computations, or turn on knitr caching with `cache = TRUE`. Make sure to omit the cache from the package with `usethis::use_build_ignore()`. Note that knitr will call the appropriate `print()` or (if available) `knitr::knit_print()` method on the result. This may generate markdown not supported by roxygen2. If needed, override the automatic methods to have your R calls return your own markdown as a character vector, wrapped in `knitr::asis_output()`. You can also use `` `r ` `` to insert the results of running R code. This is particularly useful for reducing duplication in your documentation so is documented in `vignette("reuse")`. ### Chunk options Code blocks support some knitr chunk options, e.g. to keep the output of several expressions together, you can specify `results="hold"`: ````r #' ```{r results="hold"} #' names(mtcars) #' nrow(mtcars) #' ``` ```` Some knitr chunk options are reset at the start of every code block, so if you want to change these, you'll have to specify them for every chunk. These are currently `r paste0("\x60", names(roxygen2:::knitr_chunk_defaults()), "\x60")`. Alternatively, you can set the `knitr_chunk_options` option to override these defaults, or add new chunk options that are used for the whole package. See `?load_options` for specifying roxygen2 options. ### Images Plots will create `.png` files in the `man/figures` directory with file names coming from the chunk name. Be aware that plots can quickly increase the size of your package leading to challenges for CRAN submission. ````r #' ```{r iris-pairs-plot} #' pairs(iris[1:4], main = "Anderson's Iris Data -- 3 species", #' pch = 21, bg = c("red", "green3", "blue")[unclass(iris$Species)]) #' ``` ```` By default roxygen2 only includes PDF images in the PDF manual, and SVG images in the HTML manual. If you want to avoid this restriction, set the `restrict_image_formats` roxygen2 option to `FALSE`, see `?load_options`. ## Possible problems ### Some Rd tags can't contain markdown When mixing `Rd` and Markdown notation, most `Rd` tags may contain Markdown markup, the ones that can *not* are: `r paste0("\x60", roxygen2:::escaped_for_md, "\x60", collapse = ", ")`. ### Mixing Markdown and `Rd` markup Note that turning on Markdown does *not* turn off the standard `Rd` syntax. We suggest that you use the regular `Rd` tags in a Markdown roxygen chunk only if necessary. The two parsers do occasionally interact, and the Markdown parser can pick up and reformat Rd syntax, causing an error, or corrupted manuals. ### Leading white space Leading white space is interpreted by the commonmark parser, but is ignored by the `Rd` parser (except in `\preformatted{}`). Make sure that you only include leading white space intentionally, for example, in nested lists. ### Spurious lists The commonmark parser does not require an empty line before lists, and this might lead to unintended lists if a line starts with a number followed by a dot, or with an asterisk followed by white space: ```r #' You can see more about this topic in the book cited below, on page #' 42. Clearly, the numbered list that starts here is not intentional. ``` roxygen2/inst/doc/extending.R0000644000176200001440000001203115174650101015656 0ustar liggesusers## ----------------------------------------------------------------------------- knitr::opts_chunk$set(comment = "#>", collapse = TRUE) ## ----------------------------------------------------------------------------- library(roxygen2) ## ----------------------------------------------------------------------------- roxy_tag("name", "Hadley") str(roxy_tag("name", "Hadley")) ## ----------------------------------------------------------------------------- text <- " #' This is a title #' #' This is the description. #' #' @param x,y A number #' @export f <- function(x, y) x + y " # parse_text() returns a list of blocks, so I extract the first block <- parse_text(text)[[1]] block ## ----------------------------------------------------------------------------- #' @tip The mean of a logical vector is the proportion of `TRUE` values. #' @tip You can compute means of dates and date-times! ## ----------------------------------------------------------------------------- roxy_tag_parse.roxy_tag_tip <- function(x) { tag_markdown(x) } ## ----------------------------------------------------------------------------- # Needed for vignette registerS3method("roxy_tag_parse", "roxy_tag_tip", roxy_tag_parse.roxy_tag_tip) ## ----------------------------------------------------------------------------- text <- " #' Title #' #' @tip The mean of a logical vector is the proportion of `TRUE` values. #' @tip You can compute means of dates and date-times! #' @md f <- function(x, y) { # ... } " block <- parse_text(text)[[1]] block str(block$tags[[2]]) ## ----------------------------------------------------------------------------- roxy_tag_rd.roxy_tag_tip <- function(x, base_path, env) { rd_section("tip", x$val) } ## ----------------------------------------------------------------------------- # Needed for vignette registerS3method("roxy_tag_rd", "roxy_tag_tip", roxy_tag_rd.roxy_tag_tip) ## ----------------------------------------------------------------------------- format.rd_section_tip <- function(x, ...) { paste0( "\\section{Tips and tricks}{\n", "\\itemize{\n", paste0(" \\item ", x$value, "\n", collapse = ""), "}\n", "}\n" ) } ## ----------------------------------------------------------------------------- # Needed for vignette registerS3method("format", "rd_section_tip", format.rd_section_tip) ## ----------------------------------------------------------------------------- topic <- roc_proc_text(rd_roclet(), text)[[1]] topic$get_section("tip") ## ----------------------------------------------------------------------------- #' @memo [Headline] Description ## ----------------------------------------------------------------------------- #' @memo [EFFICIENCY] Currently brute-force; find better algorithm. ## ----------------------------------------------------------------------------- roxy_tag_parse.roxy_tag_memo <- function(x) { if (!grepl("^\\[.*\\].*$", x$raw)) { roxy_tag_warning(x, "Invalid memo format") return() } parsed <- regmatches(x$raw, regexec("\\[(.*)\\](.*)", x$raw))[[1]] x$val <- list( header = parsed[[2]], message = parsed[[3]] ) x } ## ----------------------------------------------------------------------------- # Needed for vignette registerS3method( "roxy_tag_parse", "roxy_tag_memo", roxy_tag_parse.roxy_tag_memo ) ## ----------------------------------------------------------------------------- text <- " #' @memo [TBI] Remember to implement this! #' @memo [API] Check best API f <- function(x, y) { # ... } " block <- parse_text(text)[[1]] block str(block$tags[[1]]) ## ----------------------------------------------------------------------------- memo_roclet <- function() { roclet("memo") } ## ----------------------------------------------------------------------------- roclet_process.roclet_memo <- function(x, blocks, env, base_path) { results <- list() for (block in blocks) { tags <- block_get_tags(block, "memo") for (tag in tags) { msg <- paste0("[", tag$file, ":", tag$line, "] ", tag$val$message) results[[tag$val$header]] <- c(results[[tag$val$header]], msg) } } results } ## ----------------------------------------------------------------------------- roclet_output.roclet_memo <- function(x, results, base_path, ...) { for (header in names(results)) { messages <- results[[header]] cat(paste0(header, ": ", "\n")) cat(paste0(" * ", messages, "\n", collapse = "")) } invisible(NULL) } ## ----------------------------------------------------------------------------- # Needed for vignette registerS3method("roclet_process", "roclet_memo", roclet_process.roclet_memo) registerS3method("roclet_output", "roclet_memo", roclet_output.roclet_memo) ## ----------------------------------------------------------------------------- results <- roc_proc_text( memo_roclet(), " #' @memo [TBI] Remember to implement this! #' @memo [API] Check best API f <- function(x, y) { # ... } #' @memo [API] Consider passing z option g <- function(x, y) { # ... } " ) roclet_output(memo_roclet(), results) roxygen2/inst/doc/rd-S3.html0000644000176200001440000004631215174650104015340 0ustar liggesusers Documenting S3

    Documenting S3

    There are three things that you might document for S3:

    • Generics: mention that the function is a generic and list the available methods.
    • Methods: link back to the generic; only document individually when the method has unique behavior or arguments.
    • Classes: document the constructor.

    Generics

    S3 generics are regular functions, so document them as such. @export a generic if you want users to call it or other developers to write methods for it. If the generic is internal, you don’t need to export or document it.

    The documentation should mention that the function is a generic, because this tells the reader that the behavior may vary depending on the input and that they can write their own methods. For simple generics, you can do this in the description:

    #' Frobnpolicate an object
    #'
    #' @description
    #' `frobnpolicate()` is an S3 generic that ..., with methods available for
    #' the following classes:
    #'
    #' `r doclisting::methods_list("frobnpolicate")`

    For more complicated generics, you can use a # Methods section to provide more detail:

    #' Frobnpolicate an object
    #'
    #' @description
    #' `frobnpolicate()` does ...
    #'
    #' # Methods
    #' `frobnpolicate()` is an S3 generic with methods available for the following
    #' classes:
    #'
    #' `r doclisting::methods_list("frobnpolicate")`

    You might also want to include a section that provides additional details for developers implementing their own methods.

    Both examples above use the doclisting package to automatically generate a list of methods, with links to their help topics. These examples use inline R code (`r `), which generates the list at documentation time (i.e. when you run devtools::document()). This only requires including doclisting in Suggests.

    If you want the list to dynamically reflect all methods that are currently registered (including methods registered by other packages), use an inline Rd code block (`Rd `) instead:

    #' `Rd doclisting::methods_list("frobnpolicate")`

    Using `Rd ` requires doclisting in Imports, and you’ll need a dummy call to eliminate the R CMD check NOTE about unused imports:

    ignore_unused_imports <- function() {
      doclisting::methods_list
    }

    Classes

    S3 classes have no formal definition, so document the constructor. @export the constructor if you want users to create instances of your class or other developers to extend it (e.g. by creating subclasses). Internal constructors don’t need documentation.

    Note that you don’t need to list methods for a class: in S3, methods belong to generics, not to classes. Users should look at the generic’s help page to learn about available methods.

    Methods

    It is your choice whether or not to document S3 methods. Generally, it’s not necessary to document straightforward methods for common generics like print().

    You must, however, always @export S3 methods, even for internal generics. This registers the method so that the generic can find it; a user can’t directly access the method definition by typing its name. roxygen2 will warn you if you have forgotten.

    #' @export
    bizarro.character <- function(x, ...) {
      letters <- strsplit(x, "")
      letters_rev <- lapply(letters, rev)
      vapply(letters_rev, paste, collapse = "", FUN.VALUE = character(1))
    }

    If you are exporting a method in some other way, you can use @exportS3Method NULL to suppress the warning.

    It’s good practice to document methods that have unique behavior or arguments. For example, the clock package documents the methods for date_group() on their own pages (Date, POSIXt) because the methods accept different precision values and have type-specific return value semantics and examples. The generic page serves only as a signpost linking to the individual method pages.

    When documenting a method, always include a link back to the generic using [generic_name()] so the reader can easily find the full documentation and other methods.

    Methods for generics in other packages

    It’s common to write methods for generics defined in other packages. There are three cases you need to be aware of:

    • Base packages: you don’t need to do anything special: just @export the method and roxygen2 will generate the correct S3method() directive.

    • Imported packages: You have two options. Firstly, import the generic with @importFrom and @export the method:.

      #' @importFrom pkg generic
      #' @export
      generic.foo <- function(x, ...) {}

      Alternatively, use @exportS3Method pkg::generic:

      #' @exportS3Method pkg::generic
      generic.foo <- function(x, ...) {}
    • Suggested packages: you can’t import the generic for a suggested package, so you must use @exportS3Method pkg::generic. This uses delayed registration, so the method is only be registered when the suggested package is loaded.

    Generally, roxygen2 can automatically figure out which generic the method belongs to. But there is occasionally ambiguity if the generic name contains .. You can avoid this problem in your own packages by not using . in generic names. But you might encounter it when writing methods for generics in other packages. For example, is all.equal.data.frame() the equal.data.frame method for all(), or the data.frame method for all.equal()? If this happens to you, disambiguate with @method:

    #' @method all.equal data.frame
    #' @export
    all.equal.data.frame <- function(target, current, ...) {
      # ...
    }

    roxygen2 does now automatically handle the all.equal case for you, so this should happen very very rarely. And, again it can be avoided by not using . in generic or class names.

    roxygen2/inst/doc/rd-packages.Rmd0000644000176200001440000000250215172221402016371 0ustar liggesusers--- title: "Documenting packages" description: > How to document a package as a whole using the `"_PACKAGE"` sentinel. output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Documenting packages} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r} #| include: false knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ``` As well as documenting every object inside the package, you can also document the package itself by documenting the special sentinel `"_PACKAGE"`. This automatically includes information parsed from the `DESCRIPTION`, including title, description, list of authors, and useful URLs. We recommend placing package documentation in `{pkgname}-package.R`, and have `@keywords internal`. Use `usethis::use_package_doc()` to set up automatically. Here's an example: ```{r} #| eval: false #' @keywords internal "_PACKAGE" ``` Package documentation is a good place to put `# Package options` that documents options used by the package. Some notes: - By default, aliases will be added so that both `?pkgname` and `package?pkgname` will find the package help. If there's an existing function called `pkgname`, use `@aliases {pkgname}-package NULL` to override the default. - Use `@references` to point to published material about the package that users might find helpful. roxygen2/inst/doc/roxygen2.Rmd0000644000176200001440000001407515172221402015775 0ustar liggesusers--- title: "Get started with roxygen2" description: > Learn the big picture of roxygen2 and the basic workflow you'll use with it. output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Get started with roxygen2} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r} #| include: false knitr::opts_chunk$set(comment = "#>", collapse = TRUE) ``` Documentation is one of the most important aspects of good code. Without it, users won't know how to use your package, and are unlikely to do so. Documentation is also useful for you in the future (so you remember what the heck you were thinking!), and for other developers working on your package. The goal of roxygen2 is to make documenting your code as easy as possible. R provides a standard way of documenting packages: you write `.Rd` files in the `man/` directory. These files use a custom syntax, loosely based on LaTeX. roxygen2 provides a number of advantages over writing `.Rd` files by hand: - Code and documentation are adjacent so when you modify your code, it's easy to remember that you need to update the documentation. - roxygen2 dynamically inspects the objects that it's documenting, so it can automatically add data that you'd otherwise have to write by hand. - It abstracts over the differences in documenting S3 and S4 methods, generics and classes, so you need to learn fewer details. As well as generating `.Rd` files, roxygen will also create a `NAMESPACE` for you, and will manage the `Collate` field in `DESCRIPTION`. ## Basics A roxygen **block** is a sequence of lines starting with `#'` (optionally preceded by white space). The first lines of the block is called the **introduction** and forms the title, description, and details, as described below. The introduction continues until the first **tag**. Tags start with `@`, like `@details` or `@param`. Tags must appear at the beginning of a line and their content extends to the start of the next tag or the end of the block. Text within the description or tags can be formatted using Markdown or `Rd` commands; see `vignette("rd-formatting")` for details. A block ends when it hits R code, usually a function or object assignment. Blocks ignore empty lines, including lines made up of non-roxygen comments. If you need to separate two blocks, use `NULL`. If you want to use roxygen2 documentation tags without generating an `.Rd` file, you can use `@noRd` to suppress file generation for a given topic. This is useful if you want to use roxygen2 conventions for documenting an internal function; only people reading the source doc will be able to read the docs. ## Running roxygen There are three main ways to run roxygen: - `roxygen2::roxygenise()`. - `devtools::document()`. - `Ctrl + Shift + D`, if you're using RStudio or Positron (with [RStudio keybindings](https://positron.posit.co/migrate-rstudio-keybindings.html) enabled). You can mix handwritten Rd and roxygen2; roxygen2 will never overwrite a file it didn't create. ## Basic process: human readable documentation There are three steps in the transformation from roxygen comments in your source file to human readable documentation: 1. You add roxygen comments to your source file. 2. `roxygen2::roxygenise()` converts roxygen comments to `.Rd` files. 3. R converts `.Rd` files to human readable documentation. The process starts when you add specially formatted roxygen comments to your source file. Roxygen comments start with `#'` so you can continue to use regular comments for other purposes. ```{r} #| echo: false example <- "#' Add together two numbers #' #' @param x A number. #' @param y A number. #' @return A number. #' @export #' @examples #' add(1, 1) #' add(10, 1) add <- function(x, y) { x + y } " ``` ```{r, code=example} ``` For the example above, this will generate `man/add.Rd` that looks like: ```{r} #| echo: false #| results: "asis" cat( "\x60\x60\x60rd\n", format(roxygen2::roc_proc_text(roxygen2::rd_roclet(), example)[[1]]), "\n\x60\x60\x60", sep = "" ) ``` Rd files are a special file format loosely based on LaTeX. You can read more about the Rd format in the [R extensions](https://cran.r-project.org/doc/manuals/R-exts.html#Rd-format) manual. With roxygen2, there are few reasons to know about Rd files, so here I'll avoid discussing them as much as possible, focusing instead on what you need to know about roxygen2. When you use `?x`, `help("x")` or `example("x")` R looks for an Rd file containing `\alias{x}`. It then parses the file, converts it into HTML and displays it. These functions look for an Rd file in *installed* packages. This isn't very useful for package development, because you want to use the `.Rd` files in the *source* package. For this reason, we recommend that you use roxygen2 in conjunction with devtools: `devtools::load_all()` automatically adds shims so that `?` and friends will look in the development package. ## Basic process: making functions available to users The example above, that includes an `@export` tag to indicate the `add()` function should be part of the interface of the package available to users, would also be converted by `roxygenize()` into a line in the `NAMESPACE` file: ```{r} #| echo: false #| results: "asis" cat( "\x60\x60\x60txt\n", roxygen2::roc_proc_text(roxygen2::namespace_roclet(), example), "\n\x60\x60\x60", sep = "" ) ``` ## Learn more The other vignettes provide more detail on the most important individual components: - `vignette("rd-functions")` describes how to document your functions with roxygen2. - `vignette("rd-datasets")` and `vignette("rd-packages")` cover documenting datasets and the package itself. - `vignette("rd-S3")`, `vignette("rd-S4")`, `vignette("rd-R6")`, and `vignette("rd-S7")` discuss documenting the various OOP systems. - `vignette("rd-formatting")` gives the details of roxygen2's rmarkdown support. - `vignette("reuse")` demonstrates the tools available to reuse documentation in multiple places. - `vignette("namespace")` describes how to generate a `NAMESPACE` file, how namespacing works in R, and how you can use roxygen2 to be specific about what your package needs and supplies. roxygen2/inst/doc/rd-formatting.R0000644000176200001440000000145115174650106016457 0ustar liggesusers## ----------------------------------------------------------------------------- knitr::opts_chunk$set(comment = "#>", collapse = TRUE) ## ----------------------------------------------------------------------------- simple_fenced <- "#' @title Title #' @details Details #' ```{r lorem} #' 1+1 #' ``` #' @md foo <- function() NULL " ## ----code=simple_fenced------------------------------------------------------- #' @title Title #' @details Details #' ```{r lorem} #' 1+1 #' ``` #' @md foo <- function() NULL ## ----lorem-------------------------------------------------------------------- 1+1 ## ----------------------------------------------------------------------------- cat( "\x60\x60\x60rd\n", format(roxygen2:::roc_proc_text(roxygen2::rd_roclet(), simple_fenced)[[1]]), "\n\x60\x60\x60" ) roxygen2/inst/doc/reuse.html0000644000176200001440000011711715174650111015573 0ustar liggesusers Reusing documentation

    Reusing documentation

    roxygen2 provides several ways to avoid repeating yourself in code documentation, while assembling information from multiple places in one documentation file:

    • Combine documentation for closely related functions into a single file with @describeIn or @rdname.

    • Automatically copy tags with @inheritParams, @inheritSection, or @inherit.

    • Use functions to generate repeated text with inline R code.

    • Share text between documentation and vignettes with child documents.

    The chapter concludes by showing you how to update superseded reuse mechanisms that we no longer recommend: @includeRmd, @eval/@evalRd, and @template.

    Multiple functions in the same topic

    You can document multiple functions in the same file by using either @rdname or @describeIn tag. It’s a technique best used with care: documenting too many functions in one place leads to confusion. Use it when all functions have the same (or very similar) arguments.

    @rdname

    Use @rdname <destination>1 to include multiple functions in the same page. Tags (e.g. @title, @description, @examples) will be combined, across blocks but often this yields text that is hard to understand. So we recommend that you make one block that contains the title, description, common parameters, examples and so on, and then only document individual parameters in the other blocks.

    There are two basic ways to do that. You can create a standalone documentation block and then add the functions to it. This typically works best when you have a family of related functions and you want to link to that family from other functions (i.e. trig in the examples below).

    #' Trigonometric approximations
    #' @param x Input, in radians.
    #' @name trig
    NULL
    #> NULL
    
    #' @rdname trig
    #' @export
    sin_ish <- function(x) x - x^3 / 6
    
    #' @rdname trig
    #' @export
    cos_ish <- function(x) 1 - x^2 / 2
    
    #' @rdname trig
    #' @export
    tan_ish <- function(x) x + x^3 / 3

    Alternatively, you can add docs to an existing function. This tends to work better if you have a “primary” function with some variants or some helpers.

    #' Logarithms
    #'
    #' @param x A numeric vector
    #' @export
    log <- function(x, base) ...
    
    #' @rdname log
    #' @export
    log2 <- function(x) log(x, 2)
    
    #' @rdname log
    #' @export
    ln <- function(x) log(x, exp(1))

    @describeIn

    An alternative to @rdname is @describeIn. It has a slightly different syntax because as well as a topic name, you also provide a brief description for the function, like @describein topic one sentence description. The primary difference between @rdname and @describeIn, is that @describeIn creates a new section containing a bulleted list of each function, along with its description. It uses a number of heuristics to determine the heading of this section, depending on if you’re documenting related functions, methods for a generic, or methods for a class.

    In general, I no longer recommend @describeIn because I don’t think the heuristics it uses are as good as a thoughtful hand-crafted summary. If you’re currently using @describeIn, you can generally replace it with @rdname, as long as you give some thought to the multiple-function @description.

    Order of includes

    By default, roxygen blocks are processed in the order in which they appear in the file. When you’re combining multiple files, this can sometimes cause the function usage to appear in a suboptimal order. You can override the default ordering with @order. For example, the following the block would place times first in arith.Rd because 1 comes before 2.

    #' @rdname arith
    #' @order 2
    add <- function(x, y) x + y
    
    #' @rdname arith
    #' @order 1
    times <- function(x, y) x * y

    Automatically copy tags

    If two or more functions share have similarities but are different or complex enough that you don’t want to document them in a single file, you can use one of the four @inherit tags to automatically copy various components from another topic:

    • @inheritParams foo will copy @param contents from foo.
    • @inherit foo will copy all supported components from foo.
    • @inheritSection foo {Section title} will copy the @section {Section title} section from foo.
    • @inheritDotParams foo will generate documentation for ... by copying the documentation for foo()’s arguments.

    We think of this as “inheritance” rather than just copying, because anything you inherit can be overridden by a more specific definition in the documentation. This applies particularly to @inheritParams which allows you to copy the documentation for some parameters while documenting others directly. We’ll focus on this first.

    Parameters

    The oldest, and most frequently used, inherits tag is @inheritParams. It’s particularly useful when multiple functions use the same argument name for the same task, as you can document the argument once, then inherit those docs elsewhere. For example, take the dplyr functions arrange(), mutate(), and summarise() which all have an argument called .data. arrange() is documented like so:

    #' @param .data A data frame, data frame extension (e.g. a tibble), or a
    #'   lazy data frame (e.g. from dbplyr or dtplyr). See *Methods*, below, for
    #'   more details.
    #' @param ... <[`data-masking`][rlang::args_data_masking]> Variables, or
    #'   functions of variables. Use [desc()] to sort a variable in descending
    #'   order.
    arrange <- function(.data, ...) {}

    Then mutate() and summarise() don’t need to provide @param .data but can instead inherit the documentation from arrange():

    #' @inheritParams arrange
    mutate <- function(.data, ...) {}
    
    #' @inheritParams arrange
    summarise <- function(.data, ...) {}

    If this was all you wrote it wouldn’t be quite right because mutate() and summarise() would also inherit the documentation for ..., which has a different interpretation in these functions. So, for example, mutate() provides its own definition for ...:

    #' @inheritParams arrange
    #' @param ... <[`data-masking`][rlang::args_data_masking]> Name-value pairs.
    #'   The name gives the name of the column in the output.
    #'
    #'   The value can be:
    #'
    #'   * A vector of length 1, which will be recycled to the correct length.
    #'   * A vector the same length as the current group (or the whole data frame
    #'     if ungrouped).
    #'   * `NULL`, to remove the column.
    #'   * A data frame or tibble, to create multiple columns in the output.
    mutate <- function(.data, ...) {}

    Note that only the documentation for arguments with the same names are inherited. For example, arrange() also has a .by_group argument. Since no other function in dplyr has an argument with this name, its documentation will never be inherited.

    Recursive inheritance

    @inheritParams (like all @inherits functions) works recursively, so if g inherits parameters from h, then f can also inherit those parameters from g. However, this technique is best used sparingly: it’s very easy to create complex dependency webs that are hard to reason about where making changing the documentation in one function cascades out in unexpected ways across your package. You can avoid this problem by being more explicit about which parameters are inherited:

    • @inheritParams foo x y inherits only x and y.
    • @inheritParams foo -z inherits all parameters except z.
    • @inheritParams foo first:third inherits parameters first through third (in argument order).

    Multiple parameters

    Sometimes you document two (or more) tightly coupled parameters together. For example, dplyr::left_join() has:

    #' @param x,y A pair of data frames, data frame extensions (e.g. a tibble), or
    #'   lazy data frames (e.g. from dbplyr or dtplyr). See *Methods*, below, for
    #'   more details.

    When joint parameter documentation is inherited, it’s all or nothing, i.e. if a function has @inheritParams left_join it will only inherit the documentation for x and y if it has both x and y arguments and neither is documented by the inheriting function.

    The dot prefix

    Many tidyverse functions that accept named arguments in ... also use a . prefix for their own arguments. This reduces the risk of an argument going to the wrong place. For example, dplyr::mutate() has .by, .keep, .before, and .after arguments, because if they didn’t have that prefix, you wouldn’t be able to create new variables called by, keep, before, or after. We call this pattern the dot prefix.

    This means that an argument with the same meaning can come in one of two forms: with and without the .. @inheritParams knows about this common pattern so ignores a . prefix when matching argument name. In other words, .x will inherit documentation for x, and x will inherit documentation from .x.

    Inheriting other components

    You can use @inherit foo to inherit the documentation for every supported tag from another topic. Currently, @inherit supports inheriting the following tags: params, return, title, description, details, seealso, sections, references, examples, author, source, note, format.

    By supplying a space separated list of components after the function name, you can also choose to inherit only selected components. For example, @inherit foo returns would just inherit the @returns tag, and @inherit foo seealso source would inherit the @seealso and @source tags.

    @inherit foo sections will inherit every @section tag (which can also be specified in markdown by using the top-level heading spec, #). To inherit a specific section from another function, use @inheritSection foo Section title. For example, all the “adverbs” in purrr use #' @inheritSection safely Adverbs to inherit a standard section that provides advice on using an adverb in package code.

    Documenting ...

    When your function passes ... on to another function, it can be useful to inline the documentation for some of the most common arguments. This technique is inspired by the documentation for plot(), where ... can take any graphical parameter; ?plot describes some of the most common arguments so that you don’t have to look them up in ?par.

    @inheritDotParams has two components: the function to inherit from and the arguments to inherit. Since you’ll typically only want to document the most important arguments, @inheritDotParams comes with a flexible specification for argument selection inspired by dplyr::select():

    • @inheritDotParams foo takes all parameters from foo().
    • @inheritDotParams foo a b e:h takes parameters a, b, and all parameters between e and h.
    • @inheritDotParams foo -x -y takes all parameters except for x and y.

    Inheriting from other packages

    It’s most common to inherit from other documentation topics within the current package, but roxygen2 also supports inheriting documentation from other packages by using package::function syntax, e.g.:

    • @inheritParams package::function

    • @inherit package::function

    • @inheritSection package::function Section title

    • @inheritDotParams package::function

    When inheriting documentation from another package bear in mind that you’re now taking a fairly strong dependency on an external package, and to ensure every developer produces the same documentation you’ll need to make sure that they all have the same version of the package installed. And if the package changes the name of the topic or section, your documentation will require an update. For those reasons, this technique is best used sparingly.

    Custom tags

    Creating a custom tag might be a strategy for repeating yourself less.

    For instance you could create a tag that allows

    #' @git rebase

    to create this Rd section:

    \section{Related Git documentation}
    {\href{https://git-scm.com/docs/git-rebase#_interactive_mode}{\code{git rebase -i}}.}

    See vignette("extending") for more details.

    Inline code

    To insert code inline, enclose it in `r `. Roxygen will interpret the rest of the text within backticks as R code and evaluate it, and replace the backtick expression with its value. Here’s a simple example:

    #' Title `r 1 + 1`
    #'
    #' Description `r 2 + 2`
    foo <- function() NULL

    This is equivalent to writing:

    #' Title 2
    #'
    #' Description 4
    foo <- function() NULL

    The resulting text, together with the whole tag is interpreted as markdown, as usual. This means that you can use R to dynamically write markdown. For example if you defined this function in your package:

    alphabet <- function(n) {
      paste0("`", letters[1:n], "`", collapse = ", ")
    }

    You could then write:

    #' Title
    #' 
    #' @param x A string. Must be one of `r alphabet(5)`
    foo <- function(x) NULL

    The result is equivalent to writing the following by hand:

    #' Title
    #' 
    #' @param x A string. Must be one of `a`, `b`, `c`, `d`, `e`
    foo <- function(x) NULL

    This is a powerful technique for reducing duplication because you can flexibly parameterise the function however best meets your needs. Note that the evaluation environment is deliberately a child of the package that you’re documenting so you can call internal functions.

    Inline Rd code

    You can use `Rd ` to run code that is evaluated when a user views the help page, rather than when roxygen2 builds the documentation. This is useful for documentation that needs to be dynamic, reflecting the current state of R, of the user’s library and system (e.g. what dependencies are installed), not the state when the package was built. It works by using the \Sexpr tag, so that `Rd myFun()` generates\Sexpr[stage=render,results=rd]{myFun()}.

    Here’s a simple example, which will greet the user appropriately, depending on the time of day:

    greeting <- function() {
      hour <- as.POSIXlt(Sys.time(), tz = "UTC")$hour
      if (hour < 12) {
        "Good morning!"
      } else if (hour < 18) {
        "Good afternoon!"
      } else {
        "Good evening!"
      }
    }
    #' Title
    #'
    #' `Rd roxygen2:::greeting()`
    foo <- function() NULL

    Note that compared to inline R code, the function is run in the global environment by R’s documentation system, not inside the package namespace by roxygen2. This generally means that you’ll want to use ::: to explicitly call an internal function.

    Child documents

    You can use the same .Rmd or .md document in the documentation, README.Rmd, and vignettes by using child documents:

    ```{r child = "common.Rmd"}
    ```

    The included Rmd file can have roxygen Markdown-style links to other help topics. E.g. [roxygen2::roxygenize()] will link to the manual page of the roxygenize function in roxygen2. See vignette("rd-formatting") for details.

    If the Rmd file contains roxygen (Markdown-style) links to other help topics, then some care is needed, as those links will not work in Rmd files by default. A workaround is to specify external HTML links for them. These external locations will not be used for the manual which instead always links to the help topics in the manual. Example:

    See also the [roxygen2::roxygenize()] function.
    
    [roxygen2::roxygenize()]: https://roxygen2.r-lib.org/reference/roxygenize.html

    This example will link to the supplied URLs in HTML / Markdown files and it will link to the roxygenize help topic in the manual.

    Note that if you add external link targets like these, then roxygen will emit a warning about these link references being defined multiple times (once externally, and once to the help topic). This warning originates in Pandoc, and it is harmless.

    Superseded

    Over the years, we have experimented with a number of other ways to reduce duplication across documentation files. A number of these are now superseded and we recommend changing them to use the techniques described above:

    • Instead of @includeRmd man/rmd/example.Rmd, use a child document.

    • Instead of @eval or @evalRd, use inline R code.

    • Instead of @template and @templateVars write your own function and call it from inline R code.

    Inline R markdown can only generate markdown text within a tag so in principle it is less flexible than @eval/@evalRd/@template. However, our experience has revealed that generating multiple tags at once tends to be rather inflexible, and you often end up refactoring into smaller pieces so we don’t believe this reflects a real loss of functionality.


    1. The destination is a topic name. There’s a one-to-one correspondence between topic names and .Rd files where a topic called foo will produce a file called man/foo.Rd.↩︎

    roxygen2/inst/doc/rd-R6.Rmd0000644000176200001440000001172615172221402015112 0ustar liggesusers--- title: "Documenting R6" description: > How to document R6 classes, methods, and fields. output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Documenting R6} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r} #| include: false knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ``` There are three things that you might document for [R6](https://adv-r.hadley.nz/r6.html): - **Classes**: document the class as a whole with a roxygen block before `R6Class()`. - **Fields**: document fields and active bindings in-line with `@field`. - **Methods**: document methods in-line; every method doc must start with a tag. R6 documentation works differently from regular function documentation because much of it is written **in-line**, inside the class definition. Additionally, because `R CMD check` doesn't know about R6, roxygen2 is in charge of checking that all public methods, fields, active bindings, and method arguments are documented. It'll issue warnings for anything that's missing, which you can suppress using the techniques described below. ## Classes Document an R6 class by placing a roxygen block before the `R6Class()` call. Use `@export` to make the class available to users. ```{r} #| eval: false #' R6 Class Representing a Person #' #' A person has a name and a hair color. #' @export Person <- R6::R6Class( "Person", public = list( # ... fields and methods ... ) ) ``` roxygen2 automatically generates additional sections: - A section with information about the superclass(es) of the class, with links. In HTML this includes a list of all inherited methods, with links. - An 'Examples' section that contains all class and method examples. This section is run by `R CMD check`, so method examples must work without errors. `@param` tags that appear at the class level (i.e. before the `R6Class()` call) are automatically inherited by all methods, if needed. ## Fields and active bindings Fields and active bindings are documented **in-line** using the `@field` tag, right before the field definition: ```{r} #| eval: false Person <- R6::R6Class( "Person", public = list( #' @field name First or full name of the person. name = NULL, #' @field birthdate Date of birth, as a [Date]. birthdate = NULL ), active = list( #' @field age Age in years, computed from `birthdate` (read-only). age = function() { as.numeric(difftime(Sys.Date(), self$birthdate, units = "days")) / 365.25 } ) ) ``` If a field or active binding is inherited from a superclass and documented there, the child class will automatically inherit the documentation. To suppress documentation of a field or active binding, use `@field name NULL`. ## Methods Methods are also documented **in-line**, right before the method definition. Unlike regular function documentation, all roxygen comment lines for a method must appear **after a tag** --- you can't start with a plain text introduction. Use `@description` to provide the method's description: ```{r} #| eval: false Person <- R6::R6Class( "Person", public = list( #' @description #' Create a new person object. #' @param name Name. #' @param hair Hair color. #' @returns A new `Person` object. initialize = function(name = NA, hair = NA) { self$name <- name self$hair <- hair self$greet() }, #' @description #' Change hair color. #' @param val New hair color. set_hair = function(val) { self$hair <- val }, #' @description #' Say hi. greet = function() { cat(paste0("Hello, my name is ", self$name, ".\n")) } ) ) ``` Like functions, methods can use the `@description`, `@details`, `@param`, `@returns`, and `@examples` tags. These are used to create a subsection for the method, within a separate 'Methods' section in the rendered help. If a method parameter is not documented with `@param`, roxygen2 will look for documentation in the following order: class-level `@param` tags, `@field` tags (for `initialize()` only), and then the same method in parent classes. If you want to leave a method without documentation, use `@noRd` to suppress the warning. ### Dynamic methods If a method is added dynamically with `$set()`, you can document it by placing a roxygen block directly above the `$set()` call: ```{r} #| eval: false #' @description #' Say goodbye. Person$set("public", "goodbye", function() { cat(paste0("Goodbye from ", self$name, ".\n")) }) ``` roxygen2 will automatically associate the block with the class. If roxygen2 can't automatically discover a method, you can use `@R6method Class$method` to explicitly associate a documentation block with a method. Place it in a standalone roxygen block above `NULL`: ```r #' @R6method Person$set_hair #' @description Change hair color. #' @param val New hair color. NULL ``` ## Opting out To turn off the special handling of R6 classes and go back to the roxygen2 6.x.x behavior, add `Config/roxygen2/r6: false` to your `DESCRIPTION` file. roxygen2/inst/doc/rd-datasets.html0000644000176200001440000003072515174650105016665 0ustar liggesusers Documenting datasets

    Documenting datasets

    Datasets are stored in data/, not as regular R objects in the package. This means you need to document them in a slightly different way: instead of documenting the data directly, you quote the dataset’s name. For example, this is the roxygen2 block used for ggplot2::diamonds:

    #' Prices of over 50,000 round cut diamonds
    #'
    #' A dataset containing the prices and other attributes of almost 54,000
    #'  diamonds. The variables are as follows:
    #'
    #' @format A data frame with 53940 rows and 10 variables:
    #' \describe{
    #'   \item{price}{price in US dollars ($326--$18,823)}
    #'   \item{carat}{weight of the diamond (0.2--5.01)}
    #'   \item{cut}{quality of the cut (Fair, Good, Very Good, Premium, Ideal)}
    #'   \item{color}{diamond colour, from D (best) to J (worst)}
    #'   \item{clarity}{a measurement of how clear the diamond is (I1 (worst), SI2,
    #'     SI1, VS2, VS1, VVS2, VVS1, IF (best))}
    #'   \item{x}{length in mm (0--10.74)}
    #'   \item{y}{width in mm (0--58.9)}
    #'   \item{z}{depth in mm (0--31.8)}
    #'   \item{depth}{total depth percentage = z / mean(x, y) = 2 * z / (x + y) (43--79)}
    #'   \item{table}{width of top of diamond relative to widest point (43--95)}
    #' }
    #'
    #' @source {ggplot2} tidyverse R package.
    "diamonds"

    Datasets should never be exported with @export because they are not found in the NAMESPACE. Instead, datasets will either be automatically available if you set LazyData: true in your DESCRIPTION, or available after calling data() if not. This field also affects the default usage. If you have LazyData: true, the usage will be just the dataset name (e.g. diamonds). Otherwise, the usage will be wrapped in data() (e.g. data(diamonds)).

    Note the use of two additional tags that are particularly useful for documenting data:

    • @format, which gives an overview of the structure of the dataset. This should include a definition list that describes each variable. There’s currently no way to generate this with Markdown, so this is one of the few places you’ll need to Rd markup directly.

    • @source where you got the data form, often a URL.

    roxygen2/inst/doc/roxygen2.html0000644000176200001440000004104015174650112016215 0ustar liggesusers Get started with roxygen2

    Get started with roxygen2

    Documentation is one of the most important aspects of good code. Without it, users won’t know how to use your package, and are unlikely to do so. Documentation is also useful for you in the future (so you remember what the heck you were thinking!), and for other developers working on your package. The goal of roxygen2 is to make documenting your code as easy as possible.

    R provides a standard way of documenting packages: you write .Rd files in the man/ directory. These files use a custom syntax, loosely based on LaTeX. roxygen2 provides a number of advantages over writing .Rd files by hand:

    • Code and documentation are adjacent so when you modify your code, it’s easy to remember that you need to update the documentation.

    • roxygen2 dynamically inspects the objects that it’s documenting, so it can automatically add data that you’d otherwise have to write by hand.

    • It abstracts over the differences in documenting S3 and S4 methods, generics and classes, so you need to learn fewer details.

    As well as generating .Rd files, roxygen will also create a NAMESPACE for you, and will manage the Collate field in DESCRIPTION.

    Basics

    A roxygen block is a sequence of lines starting with #' (optionally preceded by white space).

    The first lines of the block is called the introduction and forms the title, description, and details, as described below. The introduction continues until the first tag.

    Tags start with @, like @details or @param. Tags must appear at the beginning of a line and their content extends to the start of the next tag or the end of the block. Text within the description or tags can be formatted using Markdown or Rd commands; see vignette("rd-formatting") for details.

    A block ends when it hits R code, usually a function or object assignment. Blocks ignore empty lines, including lines made up of non-roxygen comments. If you need to separate two blocks, use NULL.

    If you want to use roxygen2 documentation tags without generating an .Rd file, you can use @noRd to suppress file generation for a given topic. This is useful if you want to use roxygen2 conventions for documenting an internal function; only people reading the source doc will be able to read the docs.

    Running roxygen

    There are three main ways to run roxygen:

    • roxygen2::roxygenise().

    • devtools::document().

    • Ctrl + Shift + D, if you’re using RStudio or Positron (with RStudio keybindings enabled).

    You can mix handwritten Rd and roxygen2; roxygen2 will never overwrite a file it didn’t create.

    Basic process: human readable documentation

    There are three steps in the transformation from roxygen comments in your source file to human readable documentation:

    1. You add roxygen comments to your source file.
    2. roxygen2::roxygenise() converts roxygen comments to .Rd files.
    3. R converts .Rd files to human readable documentation.

    The process starts when you add specially formatted roxygen comments to your source file. Roxygen comments start with #' so you can continue to use regular comments for other purposes.

    #' Add together two numbers
    #'
    #' @param x A number.
    #' @param y A number.
    #' @return A number.
    #' @export
    #' @examples
    #' add(1, 1)
    #' add(10, 1)
    add <- function(x, y) {
      x + y
    }

    For the example above, this will generate man/add.Rd that looks like:

    % Generated by roxygen2: do not edit by hand
    % Please edit documentation in ./<text>
    \name{add}
    \alias{add}
    \title{Add together two numbers}
    \usage{
    add(x, y)
    }
    \arguments{
    \item{x}{A number.}
    
    \item{y}{A number.}
    }
    \value{
    A number.
    }
    \description{
    Add together two numbers
    }
    \examples{
    add(1, 1)
    add(10, 1)
    }

    Rd files are a special file format loosely based on LaTeX. You can read more about the Rd format in the R extensions manual. With roxygen2, there are few reasons to know about Rd files, so here I’ll avoid discussing them as much as possible, focusing instead on what you need to know about roxygen2.

    When you use ?x, help("x") or example("x") R looks for an Rd file containing \alias{x}. It then parses the file, converts it into HTML and displays it. These functions look for an Rd file in installed packages. This isn’t very useful for package development, because you want to use the .Rd files in the source package. For this reason, we recommend that you use roxygen2 in conjunction with devtools: devtools::load_all() automatically adds shims so that ? and friends will look in the development package.

    Basic process: making functions available to users

    The example above, that includes an @export tag to indicate the add() function should be part of the interface of the package available to users, would also be converted by roxygenize() into a line in the NAMESPACE file:

    export(add)

    Learn more

    The other vignettes provide more detail on the most important individual components:

    • vignette("rd-functions") describes how to document your functions with roxygen2.

    • vignette("rd-datasets") and vignette("rd-packages") cover documenting datasets and the package itself.

    • vignette("rd-S3"), vignette("rd-S4"), vignette("rd-R6"), and vignette("rd-S7") discuss documenting the various OOP systems.

    • vignette("rd-formatting") gives the details of roxygen2’s rmarkdown support.

    • vignette("reuse") demonstrates the tools available to reuse documentation in multiple places.

    • vignette("namespace") describes how to generate a NAMESPACE file, how namespacing works in R, and how you can use roxygen2 to be specific about what your package needs and supplies.

    roxygen2/inst/doc/reuse.R0000644000176200001440000001007115174650111015017 0ustar liggesusers## ----------------------------------------------------------------------------- knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ## ----------------------------------------------------------------------------- #' Trigonometric approximations #' @param x Input, in radians. #' @name trig NULL #' @rdname trig #' @export sin_ish <- function(x) x - x^3 / 6 #' @rdname trig #' @export cos_ish <- function(x) 1 - x^2 / 2 #' @rdname trig #' @export tan_ish <- function(x) x + x^3 / 3 ## ----------------------------------------------------------------------------- # #' Logarithms # #' # #' @param x A numeric vector # #' @export # log <- function(x, base) ... # # #' @rdname log # #' @export # log2 <- function(x) log(x, 2) # # #' @rdname log # #' @export # ln <- function(x) log(x, exp(1)) ## ----------------------------------------------------------------------------- #' @rdname arith #' @order 2 add <- function(x, y) x + y #' @rdname arith #' @order 1 times <- function(x, y) x * y ## ----------------------------------------------------------------------------- #' @param .data A data frame, data frame extension (e.g. a tibble), or a #' lazy data frame (e.g. from dbplyr or dtplyr). See *Methods*, below, for #' more details. #' @param ... <[`data-masking`][rlang::args_data_masking]> Variables, or #' functions of variables. Use [desc()] to sort a variable in descending #' order. arrange <- function(.data, ...) {} ## ----------------------------------------------------------------------------- #' @inheritParams arrange mutate <- function(.data, ...) {} #' @inheritParams arrange summarise <- function(.data, ...) {} ## ----------------------------------------------------------------------------- #' @inheritParams arrange #' @param ... <[`data-masking`][rlang::args_data_masking]> Name-value pairs. #' The name gives the name of the column in the output. #' #' The value can be: #' #' * A vector of length 1, which will be recycled to the correct length. #' * A vector the same length as the current group (or the whole data frame #' if ungrouped). #' * `NULL`, to remove the column. #' * A data frame or tibble, to create multiple columns in the output. mutate <- function(.data, ...) {} ## ----------------------------------------------------------------------------- #' @param x,y A pair of data frames, data frame extensions (e.g. a tibble), or #' lazy data frames (e.g. from dbplyr or dtplyr). See *Methods*, below, for #' more details. ## ----------------------------------------------------------------------------- roxygen2:::markdown_on() simple_inline <- "#' Title `r 1 + 1` #' #' Description `r 2 + 2` foo <- function() NULL " ## ----code=simple_inline------------------------------------------------------- #' Title `r 1 + 1` #' #' Description `r 2 + 2` foo <- function() NULL ## ----code=roxygen2:::markdown(simple_inline)---------------------------------- #' Title 2 #' #' Description 4 foo <- function() NULL ## ----------------------------------------------------------------------------- alphabet <- function(n) { paste0("`", letters[1:n], "`", collapse = ", ") } ## ----------------------------------------------------------------------------- env <- new.env() env$alphabet <- alphabet roxygen2:::roxy_meta_set("evalenv", env) backtick <- "#' Title #' #' @param x A string. Must be one of `r alphabet(5)` foo <- function(x) NULL " ## ----code=backtick------------------------------------------------------------ #' Title #' #' @param x A string. Must be one of `r alphabet(5)` foo <- function(x) NULL ## ----code=roxygen2:::markdown_evaluate(backtick)------------------------------ #' Title #' #' @param x A string. Must be one of `a`, `b`, `c`, `d`, `e` foo <- function(x) NULL ## ----------------------------------------------------------------------------- # greeting <- function() { # hour <- as.POSIXlt(Sys.time(), tz = "UTC")$hour # if (hour < 12) { # "Good morning!" # } else if (hour < 18) { # "Good afternoon!" # } else { # "Good evening!" # } # } # #' Title # #' # #' `Rd roxygen2:::greeting()` # foo <- function() NULL roxygen2/inst/doc/rd-functions.html0000644000176200001440000006573215174650107017075 0ustar liggesusers Documenting functions

    Documenting functions

    This vignette describes the most important roxygen2 tags for documenting functions. See vignette("reuse") for reusing documentation across topics.

    Exporting

    For a user to use a function in your package, you must export it with @export. If you export a function, you must also document it, and since other people will use it, you need to be careful if you later change the function interface. We usually put @export in between @returns and @examples:

    #' Add two numbers together
    #'
    #' @param x,y A pair of numbers.
    #' @returns A number.
    #' @export
    #' @examples
    #' 1 + 2
    add <- function(x, y) {
      x + y
    }

    You’ll learn what all these are pieces are next!

    The introduction

    Each documentation block starts with some text which defines the title, the description, and the details. Here’s an example showing what the documentation for sum() might look like if it had been written with roxygen:

    #' Sum of vector elements
    #'
    #' `sum` returns the sum of all the values present in its arguments.
    #'
    #' This is a generic function: methods can be defined for it directly
    #' or via the [Summary()] group generic. For this to work properly,
    #' the arguments `...` should be unnamed, and dispatch is on the
    #' first argument.
    sum <- function(..., na.rm = TRUE) {}

    This introductory block is broken up as follows:

    • The first sentence is the title: that’s what you see when you look at help(package = mypackage) and is shown at the top of each help file. It should generally fit on one line, be written in sentence case, and not end in a full stop.

    • The second paragraph is the description: this comes first in the documentation and should briefly describe what the function does.

    • The third and subsequent paragraphs go into the details: this is a (often long) section that comes after the argument description and should provide any other important details of how the function operates. The details are optional.

    Title

    The title appears in package indexes and search results, so think about how users will find your function. Use synonyms to describe what the function does. For example, dplyr uses titles like:

    • “Keep rows that match a condition” (filter())
    • “Create, modify, and delete columns” (mutate())
    • “Summarise each group down to one row” (summarise())

    Description

    The description should briefly summarize the goal of the function, usually in one paragraph. This can be challenging for simple functions, because it can feel like you’re just repeating the title of the function. Try to find a slightly different wording, if you can. It’s okay if this feels a little repetitive; it’s often useful for users to see the same thing expressed in two different ways. It’s a little extra work, but the extra effort is often worth it.

    #' Detect the presence/absence of a match
    #'
    #' `str_detect()` returns a logical vector with `TRUE` for each element of
    #' `string` that matches `pattern` and `FALSE` otherwise. It's equivalent to
    #' `grepl(pattern, string)`.

    If you need a multi-paragraph description, use an explicit @description tag:

    #' View strings and matches
    #'
    #' @description
    #' `str_view()` is used to print the underlying representation of a string and
    #' to see how a `pattern` matches.
    #'
    #' Matches are surrounded by `<>` and unusual whitespace (i.e. all whitespace
    #' apart from `" "` and `"\n"`) are surrounded by `{}` and escaped.

    Basically, if you’re going to include an empty line in your description, you’ll need to use an explicit @description tag.

    Details

    The details section is optional and comes after the argument description in the rendered help. Use informative markdown headings to break up long details.

    #' Sum of vector elements
    #'
    #' @description
    #' `sum` returns the sum of all the values present in its arguments.
    #'
    #' @details
    #' This is a generic function: methods can be defined for it directly
    #' or via the [Summary()] group generic. For this to work properly,
    #' the arguments `...` should be unnamed, and dispatch is on the
    #' first argument.

    Required tags

    Functions are the most commonly documented objects. Functions require three tags: @param, @returns, and @examples.

    Inputs

    Use @param name description to describe each input to the function. The description should provide a succinct summary of the parameter type (e.g. a string, a numeric vector), and if not obvious from the name, what the parameter does. The description is a sentence so should start with a capital letter and end with a full stop. It can span multiple lines (or even paragraphs) if necessary. All parameters must be documented.

    #' @param pattern Pattern to look for.
    #'
    #'   The default interpretation is a regular expression, as described in
    #'   `vignette("regular-expressions")`. Use [regex()] for finer control of the
    #'   matching behaviour.
    #'
    #' @param string Input vector. Either a character vector, or something
    #'   coercible to one.

    It’s a good idea to describe the default value in the @param description, particularly when the default is non-obvious or the signature and the rendered argument documentation are far apart.

    #' @param na.rm Remove missing values? If `FALSE` (the default), the result
    #'   will be `NA` if any element of `string` is `NA`.

    For arguments with a small fixed set of possible values, list them in the description:

    #' @param side Side on which to remove whitespace: `"left"`, `"right"`, or
    #'   `"both"` (the default).

    If each value needs more explanation, use a bulleted list:

    #' @param whitespace_only A boolean.
    #'   * `TRUE` (the default): wrapping will only occur at whitespace.
    #'   * `FALSE`: can break on any non-word character (e.g. `/`, `-`).

    If two or more arguments are tightly coupled, you can document them in one place by separating the names with commas (no spaces). For example, to document both x and y, you can say @param x,y Numeric vectors.

    Outputs

    @returns description describes the output from the function. Briefly describe the type/shape of the output, not the details.

    All functions must have a documented return value for initial CRAN submission.

    For simple cases, a single sentence suffices:

    #' @returns A logical vector the same length as `string`.

    For functions returning data frames, describe how the output relates to the input:

    #' @returns
    #' An object of the same type as `.data`. The output has the following
    #' properties:
    #'
    #' * Rows are a subset of the input, but appear in the same order.
    #' * Columns are not modified.
    #' * The number of groups may be reduced (if `.preserve` is not `TRUE`).
    #' * Data frame attributes are preserved.

    Examples

    @examples provides executable R code showing how to use the function in practice. This is a very important part of the documentation because many people look at the examples before reading anything. Example code must work without errors as it is run automatically as part of R CMD check.

    Focus on demonstrating the most important features with realistic, typical usage. Use comments to break examples into sections:

    #' @examples
    #' fruit <- c("apple", "banana", "pear", "pineapple")
    #' str_detect(fruit, "a")
    #' str_detect(fruit, "^a")
    #' str_detect(fruit, "a$")
    #'
    #' # Also vectorised over pattern
    #' str_detect("aecfg", letters)

    Examples must be self-contained and not change the user’s state: if you modify options(), reset them; create files in tempdir() and delete them; don’t change the working directory or write to the clipboard.

    For the purpose of illustration, it’s often useful to include code that causes an error. try() is the best way to do this, because users will see the error message:

    #' @examples
    #' # Row sizes must be compatible when column-binding
    #' try(bind_cols(tibble(x = 1:3), tibble(y = 1:2)))

    You can also use \dontrun{} to prevent code from executing, but try() is generally preferred.

    For code that only works in certain environments, use @examplesIf:

    #' @examplesIf interactive()
    #' browseURL("https://roxygen2.r-lib.org")

    This generates

    \examples{
    \dontshow{if (interactive() (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf}
    gh_organizations(since = 42)
    \dontshow{\}) # examplesIf}
    }

    This way, the code evaluating whether the example can be run is not shown to users reading the help, but it still prevents R CMD check failures. @examplesIf is preferred over wrapping code in if() because users don’t see the machinery, and unlike \dontrun{}, the code still runs when the condition is met.

    Instead of including examples directly in the documentation, you can put them in separate files and use @example path/relative/to/package/root to insert them into the documentation.

    All functions must have examples for initial CRAN submission.

    Usage

    In most cases, the function usage (which appears beneath the description in the generated docs) will be automatically derived from the function specification. For the cases where it is not, please file an issue and use @usage to override the default with what you want. If you want to suppress the usage altogether (which is sometimes useful for internal or deprecated functions), you can use @usage NULL.

    roxygen2/inst/doc/rd-packages.html0000644000176200001440000002312615174650110016624 0ustar liggesusers Documenting packages

    Documenting packages

    As well as documenting every object inside the package, you can also document the package itself by documenting the special sentinel "_PACKAGE". This automatically includes information parsed from the DESCRIPTION, including title, description, list of authors, and useful URLs.

    We recommend placing package documentation in {pkgname}-package.R, and have @keywords internal. Use usethis::use_package_doc() to set up automatically. Here’s an example:

    #' @keywords internal
    "_PACKAGE"

    Package documentation is a good place to put # Package options that documents options used by the package.

    Some notes:

    • By default, aliases will be added so that both ?pkgname and package?pkgname will find the package help. If there’s an existing function called pkgname, use @aliases {pkgname}-package NULL to override the default.

    • Use @references to point to published material about the package that users might find helpful.

    roxygen2/inst/doc/rd-S4.html0000644000176200001440000002577715174650104015355 0ustar liggesusers Documenting S4

    Documenting S4

    There are three things that you might document for S4:

    • Generics: document as functions; mention that they are generics.
    • Methods: unlike S3/S7, all S4 methods must be documented.
    • Classes: document the class definition and use @slot for slots.

    Generics

    S4 generics are functions, so document them as such. @export a generic if you want users to call it or other developers to write methods for it. If the generic is internal, you don’t need to export or document it.

    Classes

    Document S4 classes by adding a roxygen block before setClass(). @export a class if you want users to create instances or other developers to extend it (e.g. by creating subclasses). Internal classes don’t need documentation. Use @slot to document the slots of the class. Here’s a simple example:

    #' An S4 class to represent a bank account
    #'
    #' @slot balance A length-one numeric vector
    #' @export
    Account <- setClass("Account", slots = list(balance = "numeric"))

    Methods

    S4 methods are a little more complicated. Unlike S3 and S7 methods, all S4 methods must be documented. You only need to @export a method if the generic lives in another package.

    You can document methods in three places:

    • In the class. Most appropriate if the corresponding generic uses single dispatch and you created the class.

    • In the generic. Most appropriate if the generic uses multiple dispatches and you control it.

    • In its own file. Most appropriate if the method is complex or the either two options don’t apply.

    Use either @rdname or @describeIn to control where method documentation goes. See vignette("reuse") for more details.

    roxygen2/inst/doc/rd-R6.html0000644000176200001440000005025315174650103015340 0ustar liggesusers Documenting R6

    Documenting R6

    There are three things that you might document for R6:

    • Classes: document the class as a whole with a roxygen block before R6Class().
    • Fields: document fields and active bindings in-line with @field.
    • Methods: document methods in-line; every method doc must start with a tag.

    R6 documentation works differently from regular function documentation because much of it is written in-line, inside the class definition. Additionally, because R CMD check doesn’t know about R6, roxygen2 is in charge of checking that all public methods, fields, active bindings, and method arguments are documented. It’ll issue warnings for anything that’s missing, which you can suppress using the techniques described below.

    Classes

    Document an R6 class by placing a roxygen block before the R6Class() call. Use @export to make the class available to users.

    #' R6 Class Representing a Person
    #'
    #' A person has a name and a hair color.
    #' @export
    Person <- R6::R6Class(
      "Person",
      public = list(
        # ... fields and methods ...
      )
    )

    roxygen2 automatically generates additional sections:

    • A section with information about the superclass(es) of the class, with links. In HTML this includes a list of all inherited methods, with links.

    • An ‘Examples’ section that contains all class and method examples. This section is run by R CMD check, so method examples must work without errors.

    @param tags that appear at the class level (i.e. before the R6Class() call) are automatically inherited by all methods, if needed.

    Fields and active bindings

    Fields and active bindings are documented in-line using the @field tag, right before the field definition:

    Person <- R6::R6Class(
      "Person",
      public = list(
        #' @field name First or full name of the person.
        name = NULL,
    
        #' @field birthdate Date of birth, as a [Date].
        birthdate = NULL
      ),
      active = list(
        #' @field age Age in years, computed from `birthdate` (read-only).
        age = function() {
          as.numeric(difftime(Sys.Date(), self$birthdate, units = "days")) / 365.25
        }
      )
    )

    If a field or active binding is inherited from a superclass and documented there, the child class will automatically inherit the documentation. To suppress documentation of a field or active binding, use @field name NULL.

    Methods

    Methods are also documented in-line, right before the method definition. Unlike regular function documentation, all roxygen comment lines for a method must appear after a tag — you can’t start with a plain text introduction. Use @description to provide the method’s description:

    Person <- R6::R6Class(
      "Person",
      public = list(
        #' @description
        #' Create a new person object.
        #' @param name Name.
        #' @param hair Hair color.
        #' @returns A new `Person` object.
        initialize = function(name = NA, hair = NA) {
          self$name <- name
          self$hair <- hair
          self$greet()
        },
    
        #' @description
        #' Change hair color.
        #' @param val New hair color.
        set_hair = function(val) {
          self$hair <- val
        },
    
        #' @description
        #' Say hi.
        greet = function() {
          cat(paste0("Hello, my name is ", self$name, ".\n"))
        }
      )
    )

    Like functions, methods can use the @description, @details, @param, @returns, and @examples tags. These are used to create a subsection for the method, within a separate ‘Methods’ section in the rendered help.

    If a method parameter is not documented with @param, roxygen2 will look for documentation in the following order: class-level @param tags, @field tags (for initialize() only), and then the same method in parent classes.

    If you want to leave a method without documentation, use @noRd to suppress the warning.

    Dynamic methods

    If a method is added dynamically with $set(), you can document it by placing a roxygen block directly above the $set() call:

    #' @description
    #' Say goodbye.
    Person$set("public", "goodbye", function() {
      cat(paste0("Goodbye from ", self$name, ".\n"))
    })

    roxygen2 will automatically associate the block with the class.

    If roxygen2 can’t automatically discover a method, you can use @R6method Class$method to explicitly associate a documentation block with a method. Place it in a standalone roxygen block above NULL:

    #' @R6method Person$set_hair
    #' @description Change hair color.
    #' @param val New hair color.
    NULL

    Opting out

    To turn off the special handling of R6 classes and go back to the roxygen2 6.x.x behavior, add Config/roxygen2/r6: false to your DESCRIPTION file.

    roxygen2/inst/doc/rd-functions.R0000644000176200001440000001007015174650107016313 0ustar liggesusers## ----------------------------------------------------------------------------- knitr::opts_chunk$set(comment = "#>", collapse = TRUE) ## ----------------------------------------------------------------------------- # #' Add two numbers together # #' # #' @param x,y A pair of numbers. # #' @returns A number. # #' @export # #' @examples # #' 1 + 2 # add <- function(x, y) { # x + y # } ## ----------------------------------------------------------------------------- #' Sum of vector elements #' #' `sum` returns the sum of all the values present in its arguments. #' #' This is a generic function: methods can be defined for it directly #' or via the [Summary()] group generic. For this to work properly, #' the arguments `...` should be unnamed, and dispatch is on the #' first argument. sum <- function(..., na.rm = TRUE) {} ## ----------------------------------------------------------------------------- # #' Detect the presence/absence of a match # #' # #' `str_detect()` returns a logical vector with `TRUE` for each element of # #' `string` that matches `pattern` and `FALSE` otherwise. It's equivalent to # #' `grepl(pattern, string)`. ## ----------------------------------------------------------------------------- # #' View strings and matches # #' # #' @description # #' `str_view()` is used to print the underlying representation of a string and # #' to see how a `pattern` matches. # #' # #' Matches are surrounded by `<>` and unusual whitespace (i.e. all whitespace # #' apart from `" "` and `"\n"`) are surrounded by `{}` and escaped. ## ----------------------------------------------------------------------------- #' Sum of vector elements #' #' @description #' `sum` returns the sum of all the values present in its arguments. #' #' @details #' This is a generic function: methods can be defined for it directly #' or via the [Summary()] group generic. For this to work properly, #' the arguments `...` should be unnamed, and dispatch is on the #' first argument. ## ----------------------------------------------------------------------------- # #' @param pattern Pattern to look for. # #' # #' The default interpretation is a regular expression, as described in # #' `vignette("regular-expressions")`. Use [regex()] for finer control of the # #' matching behaviour. # #' # #' @param string Input vector. Either a character vector, or something # #' coercible to one. ## ----------------------------------------------------------------------------- # #' @param na.rm Remove missing values? If `FALSE` (the default), the result # #' will be `NA` if any element of `string` is `NA`. ## ----------------------------------------------------------------------------- # #' @param side Side on which to remove whitespace: `"left"`, `"right"`, or # #' `"both"` (the default). ## ----------------------------------------------------------------------------- # #' @param whitespace_only A boolean. # #' * `TRUE` (the default): wrapping will only occur at whitespace. # #' * `FALSE`: can break on any non-word character (e.g. `/`, `-`). ## ----------------------------------------------------------------------------- # #' @returns A logical vector the same length as `string`. ## ----------------------------------------------------------------------------- # #' @returns # #' An object of the same type as `.data`. The output has the following # #' properties: # #' # #' * Rows are a subset of the input, but appear in the same order. # #' * Columns are not modified. # #' * The number of groups may be reduced (if `.preserve` is not `TRUE`). # #' * Data frame attributes are preserved. ## ----------------------------------------------------------------------------- # #' @examples # #' fruit <- c("apple", "banana", "pear", "pineapple") # #' str_detect(fruit, "a") # #' str_detect(fruit, "^a") # #' str_detect(fruit, "a$") # #' # #' # Also vectorised over pattern # #' str_detect("aecfg", letters) ## ----------------------------------------------------------------------------- # #' @examples # #' # Row sizes must be compatible when column-binding # #' try(bind_cols(tibble(x = 1:3), tibble(y = 1:2))) roxygen2/inst/doc/rd-S3.Rmd0000644000176200001440000001460415172221402015106 0ustar liggesusers--- title: "Documenting S3" description: > How to document S3 generics, methods, and classes. output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Documenting S3} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r} #| include: false knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ``` There are three things that you might document for [S3](https://adv-r.hadley.nz/s3.html): - **Generics**: mention that the function is a generic and list the available methods. - **Methods**: link back to the generic; only document individually when the method has unique behavior or arguments. - **Classes**: document the constructor. ## Generics S3 **generics** are regular functions, so document them as such. `@export` a generic if you want users to call it or other developers to write methods for it. If the generic is internal, you don't need to export or document it. The documentation should mention that the function is a generic, because this tells the reader that the behavior may vary depending on the input and that they can write their own methods. For simple generics, you can do this in the description: ```{r} #| eval: false #' Frobnpolicate an object #' #' @description #' `frobnpolicate()` is an S3 generic that ..., with methods available for #' the following classes: #' #' `r doclisting::methods_list("frobnpolicate")` ``` For more complicated generics, you can use a `# Methods` section to provide more detail: ```{r} #| eval: false #' Frobnpolicate an object #' #' @description #' `frobnpolicate()` does ... #' #' # Methods #' `frobnpolicate()` is an S3 generic with methods available for the following #' classes: #' #' `r doclisting::methods_list("frobnpolicate")` ``` You might also want to include a section that provides additional details for developers implementing their own methods. Both examples above use the [doclisting](https://doclisting.r-lib.org/) package to automatically generate a list of methods, with links to their help topics. These examples use [inline R code](reuse.html#inline-code) (`` `r ` ``), which generates the list at documentation time (i.e. when you run `devtools::document()`). This only requires including doclisting in `Suggests`. If you want the list to dynamically reflect all methods that are currently registered (including methods registered by other packages), use an [inline Rd code](reuse.html#inline-rd-code) block (`` `Rd ` ``) instead: ```{r} #| eval: false #' `Rd doclisting::methods_list("frobnpolicate")` ``` Using `` `Rd ` `` requires doclisting in `Imports`, and you'll need a dummy call to eliminate the `R CMD check` NOTE about unused imports: ```{r} #| eval: false ignore_unused_imports <- function() { doclisting::methods_list } ``` ## Classes S3 **classes** have no formal definition, so document the [constructor](https://adv-r.hadley.nz/s3.html#s3-constructor). `@export` the constructor if you want users to create instances of your class or other developers to extend it (e.g. by creating subclasses). Internal constructors don't need documentation. Note that you don't need to list methods for a class: in S3, methods belong to generics, not to classes. Users should look at the generic's help page to learn about available methods. ## Methods It is your choice whether or not to document S3 **methods**. Generally, it's not necessary to document straightforward methods for common generics like `print()`. You must, however, always `@export` S3 methods, even for internal generics. This **registers** the method so that the generic can find it; a user can't directly access the method definition by typing its name. roxygen2 will warn you if you have forgotten. ```{r} #| eval: false #' @export bizarro.character <- function(x, ...) { letters <- strsplit(x, "") letters_rev <- lapply(letters, rev) vapply(letters_rev, paste, collapse = "", FUN.VALUE = character(1)) } ``` If you are exporting a method in some other way, you can use `@exportS3Method NULL` to suppress the warning. It's good practice to document methods that have unique behavior or arguments. For example, the clock package documents the methods for [`date_group()`](https://clock.r-lib.org/reference/date_group.html) on their own pages ([Date](https://clock.r-lib.org/reference/date-group.html), [POSIXt](https://clock.r-lib.org/reference/posixt-group.html)) because the methods accept different `precision` values and have type-specific return value semantics and examples. The generic page serves only as a signpost linking to the individual method pages. When documenting a method, always include a link back to the generic using `[generic_name()]` so the reader can easily find the full documentation and other methods. ### Methods for generics in other packages It's common to write methods for generics defined in other packages. There are three cases you need to be aware of: - **Base packages**: you don't need to do anything special: just `@export` the method and roxygen2 will generate the correct `S3method()` directive. - **Imported packages**: You have two options. Firstly, import the generic with `@importFrom` and `@export` the method:. ```{r} #| eval: false #' @importFrom pkg generic #' @export generic.foo <- function(x, ...) {} ``` Alternatively, use `@exportS3Method pkg::generic`: ```{r} #| eval: false #' @exportS3Method pkg::generic generic.foo <- function(x, ...) {} ``` - **Suggested packages**: you can't import the generic for a suggested package, so you must use `@exportS3Method pkg::generic`. This uses delayed registration, so the method is only be registered when the suggested package is loaded. Generally, roxygen2 can automatically figure out which generic the method belongs to. But there is occasionally ambiguity if the generic name contains `.`. You can avoid this problem in your own packages by not using `.` in generic names. But you might encounter it when writing methods for generics in other packages. For example, is `all.equal.data.frame()` the `equal.data.frame` method for `all()`, or the `data.frame` method for `all.equal()`? If this happens to you, disambiguate with `@method`: ```{r} #| eval: false #' @method all.equal data.frame #' @export all.equal.data.frame <- function(target, current, ...) { # ... } ``` roxygen2 does now automatically handle the `all.equal` case for you, so this should happen very very rarely. And, again it can be avoided by not using `.` in generic or class names. roxygen2/inst/doc/roxygen2.R0000644000176200001440000000211515174650112015452 0ustar liggesusers## ----------------------------------------------------------------------------- knitr::opts_chunk$set(comment = "#>", collapse = TRUE) ## ----------------------------------------------------------------------------- example <- "#' Add together two numbers #' #' @param x A number. #' @param y A number. #' @return A number. #' @export #' @examples #' add(1, 1) #' add(10, 1) add <- function(x, y) { x + y } " ## ----code=example------------------------------------------------------------- #' Add together two numbers #' #' @param x A number. #' @param y A number. #' @return A number. #' @export #' @examples #' add(1, 1) #' add(10, 1) add <- function(x, y) { x + y } ## ----------------------------------------------------------------------------- cat( "\x60\x60\x60rd\n", format(roxygen2::roc_proc_text(roxygen2::rd_roclet(), example)[[1]]), "\n\x60\x60\x60", sep = "" ) ## ----------------------------------------------------------------------------- cat( "\x60\x60\x60txt\n", roxygen2::roc_proc_text(roxygen2::namespace_roclet(), example), "\n\x60\x60\x60", sep = "" ) roxygen2/inst/roxygen2-tags.yml0000644000176200001440000003013015166171276016251 0ustar liggesusers- name: aliases description: > Add additional aliases to the topic. Use `NULL` to suppress the default alias automatically generated by roxygen2. template: ' ${1:alias}' vignette: index-crossref recommend: true - name: author description: > Topic author(s), if different from the package author(s). template: ' ${1:author}' recommend: true - name: backref description: > Manually override the backreference that points from the `.Rd` file back to the source `.R` file. Only needed when generating code. template: ' ${1:path}' vignette: index-crossref - name: concept description: > Add additional keywords or phrases to be included in the `help.search()` index. Each `@concept` should be a single term or phrase. template: ' ${1:concept}' vignette: index-crossref recommend: true - name: describeIn description: > Document a function or method in the `destination` topic. template: ' ${1:destination} ${2:description}' vignette: reuse recommend: true - name: description description: > A short description of the purpose of the function. Usually around a paragraph, but can be longer if needed. template: "\n${1:A short description...}\n" vignette: rd-functions recommend: true - name: details description: > Additional details about the function. Generally superseded by instead using a level 1 heading. template: "\n${1:Additional details...}\n" vignette: rd-functions - name: docType description: > Set documentation type. One of `data`, `package`, `class`, or `methods`. Usually added automatically; for expert use only. template: ' ${1:data|package|class|methods}' - name: encoding description: > Override encoding of single `.Rd` file. No longer recommended since all documentation should use UTF-8. template: ' ${1:encoding}' - name: eval description: > Evaluate arbitrary code in the package namespace and insert the results back into the block. Should return a character vector of lines. template: ' ${1:r-code}' vignette: reuse - name: evalNamespace description: > Evaluate arbitrary code in the package namespace and insert the results into the `NAMESPACE`. Should return a character vector of directives. template: ' ${1:r-code}' vignette: namespace - name: evalRd description: > Evaluate arbitrary code in the package namespace and insert the results back as into the block. Should return a character vector of lines. template: ' ${1:r-code}' vignette: reuse - name: example description: > Embed examples stored in another file. template: ' ${1:path}.R' vignette: rd-functions recommend: true - name: examples description: > Executable R code that demonstrates how the function works. Code must run without error. template: "\n${1:# example code}\n" vignette: rd-functions recommend: true - name: examplesIf description: > Run examples only when `condition` is `TRUE`. template: " ${1:condition}\n${2:# example code}\n" vignette: rd-functions recommend: true - name: export description: > Export this function, method, generic, or class so it's available outside of the package. vignette: namespace recommend: true - name: exportClass description: > Export an S4 class. For expert use only; in most cases you should use `@export` so roxygen2 can automatically generate the correct directive. template: ' ${1:class}' vignette: namespace - name: exportMethod description: > Export S4 methods. For expert use only; in most cases you should use `@export` so roxygen2 can automatically generate the correct directive. template: ' ${1:generic}' vignette: namespace - name: exportPattern description: > Export all objects matching a regular expression. template: ' ${1:pattern}' vignette: namespace - name: exportS3Method description: > Export an S3 method. Only needed when the method is for a generic from a suggested package. template: ' ${1:package}::${2:generic}' vignette: namespace recommend: true - name: family description: > Generate `@seealso` entries to all other functions in `family name`. template: ' ${1:family name}' vignette: index-crossref recommend: true - name: field description: > Describe a R6 or refClass field. template: ' ${1:name} ${2:description}' vignette: rd-R6 recommend: true - name: format description: > Describe the type/shape of a dataset. If the dataset is a data frame, include a description of each column. If not supplied, will be automatically generated by `object_format()`. template: ' ${1:description}' vignette: rd-datasets recommend: true - name: import description: > Import all functions from a package. Use with extreme care. template: ' ${1:package}' vignette: namespace - name: importClassesFrom description: > Import S4 classes from another package. template: ' ${1:package} ${2:class}' vignette: namespace - name: importFrom description: > Import specific functions from a package. template: ' ${1:package} ${2:function}' vignette: namespace recommend: true - name: importMethodsFrom description: > Import S4 methods from a package. template: ' ${1:package} ${2:generic}' vignette: namespace - name: include description: > Declare that `filename.R` must be loaded before the current file. template: ' ${1:filename}.R' recommend: true - name: includeRmd description: > Insert the contents of an `.Rmd` into the current block. Superseded in favour of using a code chunk with a child document. template: ' man/rmd/${1:filename}.Rmd' vignette: reuse - name: inherit description: > Inherit one or more documentation components from another topic. If `components` is omitted, all supported components will be inherited. Otherwise, specify individual components to inherit by picking one or more of `params`, `return`, `title`, `description`, `details`, `seealso`, `sections`, `references`, `examples`, `author`, `source`, `note`, and `format`. template: ' ${1:source} ${2:components}' vignette: reuse recommend: true - name: inheritDotParams description: > Automatically generate documentation for `...` when you're passing dots along to another function. We recommend supplying explicit argument names for maximum clarity and consistency. template: ' ${1:source} ${2:arg1 arg2 arg3}' vignette: reuse recommend: true - name: inheritParams description: > Inherit argument documentation from another function. Only inherits documentation for arguments that aren't already documented locally. Optionally followed by a list of argument names to include or exclude, using the same syntax as `@inheritDotParams`. template: ' ${1:source} ${2:arg1 arg2 arg3}' vignette: reuse recommend: true - name: inheritSection description: > Inherit a specific named section from another topic. template: ' ${1:source} ${2:section name}' vignette: reuse recommend: true - name: keywords description: > Add a standardised keyword, indexed by `help.search()`. These are generally not useful apart from `@keywords internal` which flags the topic as internal and removes from topic indexes. template: ' ${1:keyword}' vignette: index-crossref recommend: true - name: md description: > Force markdown processing for a block. vignette: rd-formatting - name: method description: > If ambiguous, clarify which generic an S3 method belongs too. Only needed in very rare cases. template: ' ${1:generic} ${2:class}' vignette: rd-S3 recommend: true - name: name description: > Define (or override the topic) name. Typically only needed for synthetic topics where you are documenting `NULL`. template: ' ${1:name}' - name: noMd description: > Suppress markdown processing for a block. vignette: 'rd-formatting' - name: noRd description: > Suppress `.Rd` generation for a block. Use for documentation blocks that should only be visible in the source code. vignette: rd-functions recommend: true - name: note description: > Add an optional note. Now generally superseded by using a level 1 markdown heading. template: "\n" - name: order description: > Override the default (lexigraphic) order in which multiple blocks are combined into a single topic. template: ' ${1:number}' vignette: reuse recommend: true - name: param description: > Describe a function input. Should describe acceptable input types and how it affects the output. `description` is usually one or two sentences but can be as long as needed. Document multiple arguments by separating their names with commas without spaces. template: ' ${1:name} ${2:description}' vignette: rd-functions recommend: true - name: prop description: > Describe an S7 class property that is not a constructor parameter. template: ' ${1:name} ${2:description}' vignette: rd-S7 recommend: true - name: R6method description: > Document an R6 method that can't be discovered by introspection, such as methods added via `$set()`. template: ' ${1:Class}$${2:method}' vignette: rd-R6 recommend: true - name: rawNamespace description: > Insert literal text directly into the `NAMESPACE`. template: ' ${1:namespace directives}' vignette: namespace - name: rawRd description: > Insert literal text directly into the `.Rd` file. template: ' ${1:rd}' vignette: rd-functions - name: rdname description: > Override the file name of generated `.Rd` file. Can be used to combine multiple blocks into a single documentation topic. template: ' ${1:topic-name}' vignette: reuse recommend: true - name: references description: > Pointers to the related literature. Usually formatted like a bibliography. template: ' ${1:reference}' vignette: index-crossref recommend: true - name: return description: > Describe the function's output. Superseded in favour of `@returns`. template: ' ${1:description}' vignette: rd-functions - name: returns description: > Describe the function's output. Typically will be a 1-2 sentence description of the output type, but might also include discussion of important errors or warnings. template: ' ${1:description}' vignette: rd-functions recommend: true - name: section description: > Add an arbitrary section to the documentation. Now generally superseded in favour of using a level 1 heading. template: " ${1:section title}: \n" vignette: rd-formatting - name: seealso description: > Link to other related functions or urls. Usually a sentence or two, or a bulleted list. template: ' [${1:func}()]' vignette: index-crossref recommend: true - name: slot description: > Describe the slot of an S4 class. template: ' ${1:name} ${2:description}' vignette: rd-S4 recommend: true - name: source description: > Describe where the dataset came from. Provide a link to the original source (if possible) and briefly describe any manipulation that you performed when importing the data. template: ' ${1:description}' vignette: rd-datasets recommend: true - name: template description: > Use a roxygen2 template. Now superseded in favour of inline R code. template: ' ${1:path-to-template}' vignette: reuse - name: templateVar description: > Define variables for use in a roxygen2 template. template: ' ${1:name} ${2:value}' vignette: reuse - name: title description: > A one-line description of the function shown in various indexes. An explicit `@title` is not usually needed as by default it is taken from the first paragraph in the roxygen block. template: ' ${1:title}' vignette: rd-functions recommend: true - name: usage description: > Override the default usage generated by roxygen2. Only needed when roxygen2 fails to correctly derive the usage of your function. template: ' ${1:fun}(${2:arg1, arg2 = default, ...})' vignette: rd-functions recommend: true - name: useDynLib description: > Import compiled code from another package. template: ' ${1:package}' vignette: namespace recommend: true roxygen2/README.md0000644000176200001440000000624315166171276013327 0ustar liggesusers# roxygen2 roxygen2 website [![CRAN status](https://www.r-pkg.org/badges/version/roxygen2)](https://CRAN.R-project.org/package=roxygen2) [![R-CMD-check](https://github.com/r-lib/roxygen2/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/r-lib/roxygen2/actions/workflows/R-CMD-check.yaml) [![Codecov test coverage](https://codecov.io/gh/r-lib/roxygen2/branch/main/graph/badge.svg)](https://app.codecov.io/gh/r-lib/roxygen2?branch=main) The premise of roxygen2 is simple: describe your functions in comments next to their definitions and roxygen2 will process your source code and comments to automatically generate `.Rd` files in `man/`, `NAMESPACE`, and, if needed, the `Collate` field in `DESCRIPTION`. ## Installation ```R # Install roxygen2 from CRAN install.packages("roxygen2") # Or the development version from GitHub: # install.packages("pak") pak::pak("r-lib/roxygen2") ``` ## Usage The premise of roxygen2 is simple: describe your functions in comments next to their definitions and roxygen2 will process your source code and comments to produce Rd files in the `man/` directory. Here's a [simple example](https://stringr.tidyverse.org/reference/str_length.html) from the stringr package: ```R #' The length of a string #' #' Technically this returns the number of "code points", in a string. One #' code point usually corresponds to one character, but not always. For example, #' an u with a umlaut might be represented as a single character or as the #' combination a u and an umlaut. #' #' @inheritParams str_detect #' @return A numeric vector giving number of characters (code points) in each #' element of the character vector. Missing string have missing length. #' @seealso [stringi::stri_length()] which this function wraps. #' @export #' @examples #' str_length(letters) #' str_length(NA) #' str_length(factor("abc")) #' str_length(c("i", "like", "programming", NA)) str_length <- function(string) { } ``` When you `roxygenise()` (or `devtools::document()`) your package these comments will be automatically transformed to the `.Rd` that R uses to generate the documentation you see when you type `?str_length`. ## Learn more To get started, first read `vignette("roxygen2")`. Then read more about the specific package component that you want to generate: * `vignette("rd-functions")` describes how to document your functions with roxygen2. * `vignette("rd-S3")`, `vignette("rd-S4")`, `vignette("rd-R6")`, and `vignette("rd-S7")` discuss documenting the various OOP systems. * `vignette("rd-datasets")` and `vignette("rd-packages")` cover documenting datasets and the package itself. * `vignette("rd-formatting")` gives the details of roxygen2's rmarkdown support. * `vignette("reuse")` demonstrates the tools available to reuse documentation in multiple places. * `vignette("namespace")` describes how to generate a `NAMESPACE` file, how namespacing works in R, and how you can use roxygen2 to be specific about what your package needs and supplies. * For the `Collate` field in the `DESCRIPTION`, see `?update_collate()`. roxygen2/build/0000755000176200001440000000000015174650112013130 5ustar liggesusersroxygen2/build/vignette.rds0000644000176200001440000000101415174650112015463 0ustar liggesusersSMo1,Z!!R*=ADUЫ;UXF[s!zތ=k'I&4Ij8W#IIKa- `%gBpLĂ/u<\(8U@I$ \% #' @import rlang NULL # This results in the following lines in `NAMESPACE`: # importFrom(magrittr,"\%>\%") # import(rlang) } \seealso{ \link{tags-namespace} for tags that generate \code{NAMESPACE} directives. } roxygen2/man/tags-rd-S4.Rd0000644000176200001440000000123415173670373014732 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/tag-metadata.R \name{tags-rd-S4} \alias{tags-rd-S4} \alias{@slot} \title{Tags for documenting S4} \usage{ #' @slot ${1:name} ${2:description} } \description{ Learn the full details in \code{vignette('rd-S4')}. Key tags: \itemize{ \item \verb{@slot $\{1:name\} $\{2:description\}}: Describe the slot of an S4 class. } } \seealso{ Other documentation tags: \code{\link{tags-index-crossref}}, \code{\link{tags-rd-R6}}, \code{\link{tags-rd-S3}}, \code{\link{tags-rd-S7}}, \code{\link{tags-rd-datasets}}, \code{\link{tags-rd-functions}}, \code{\link{tags-reuse}} } \concept{documentation tags} roxygen2/man/roc_proc_text.Rd0000644000176200001440000000144615173670373015764 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/roclet.R \name{roc_proc_text} \alias{roc_proc_text} \title{Process roclet on string and capture results} \usage{ roc_proc_text(roclet, input, wd = NULL) } \arguments{ \item{roclet}{Name of roclet to use for processing.} \item{input}{Source string} \item{wd}{Working directory} } \description{ Useful for testing. } \seealso{ Other extending: \code{\link[=load_options]{load_options()}}, \code{\link[=parse_package]{parse_package()}}, \code{\link[=rd_section]{rd_section()}}, \code{\link[=roclet_find]{roclet_find()}}, \code{\link[=roxy_block]{roxy_block()}}, \code{\link[=roxy_tag]{roxy_tag()}}, \code{\link[=roxy_tag_rd]{roxy_tag_rd()}}, \code{\link{tag_parsers}}, \code{\link[=tags_list]{tags_list()}} } \concept{extending} roxygen2/man/roxy_tag_rd.Rd0000644000176200001440000000171415173670373015431 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/rd.R \name{roxy_tag_rd} \alias{roxy_tag_rd} \title{Generate Rd output from a tag} \usage{ roxy_tag_rd(x, base_path, env) } \arguments{ \item{x}{The tag} \item{base_path}{Path to package root directory.} \item{env}{Environment in which to evaluate code (if needed)} } \value{ Methods must return a \link{rd_section}. } \description{ Provide a method for this generic if you want a tag to generate output in \code{.Rd} files. See \code{vignette("extending")} for more details. } \seealso{ Other extending: \code{\link[=load_options]{load_options()}}, \code{\link[=parse_package]{parse_package()}}, \code{\link[=rd_section]{rd_section()}}, \code{\link[=roc_proc_text]{roc_proc_text()}}, \code{\link[=roclet_find]{roclet_find()}}, \code{\link[=roxy_block]{roxy_block()}}, \code{\link[=roxy_tag]{roxy_tag()}}, \code{\link{tag_parsers}}, \code{\link[=tags_list]{tags_list()}} } \concept{extending} roxygen2/man/rd_roclet.Rd0000644000176200001440000000152515173670373015065 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/rd.R \name{rd_roclet} \alias{rd_roclet} \title{Roclet: make Rd files} \usage{ rd_roclet() } \description{ This \link{roclet} automates the production of the \code{.Rd} files that R uses to document functions, datasets, packages, classes, and more. See \code{vignette("rd-functions")} for details. It is run by default by \code{\link[=roxygenize]{roxygenize()}}. } \examples{ #' Add together two numbers #' @param x A number. #' @param y A number. #' @return A number. #' @export #' @examples #' add(1, 1) #' add(10, 1) add <- function(x, y) { x + y } } \seealso{ \link{tags-rd-functions}, \link{tags-rd-datasets}, \link{tags-rd-S3}, \link{tags-rd-S4}, \link{tags-rd-S7}, \link{tags-rd-R6}, \link{tags-reuse}, \link{tags-index-crossref} for tags provided by this roclet. } roxygen2/man/roclet_find.Rd0000644000176200001440000000223615173670373015400 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/roclet.R \name{roclet_find} \alias{roclet_find} \title{Create a roclet from a string} \usage{ roclet_find(x) } \arguments{ \item{x}{Arbitrary R code evaluated in roxygen2 package.} } \description{ This provides a flexible way of specifying a roclet in a string. } \examples{ # rd, namespace, and vignette work for backward compatibility roclet_find("rd") # But generally you should specify the name of a function that # returns a roclet roclet_find("rd_roclet") # If it lives in another package, you'll need to use :: roclet_find("roxygen2::rd_roclet") # If it takes parameters (which no roclet does currently), you'll need # to call the function roclet_find("roxygen2::rd_roclet()") } \seealso{ Other extending: \code{\link[=load_options]{load_options()}}, \code{\link[=parse_package]{parse_package()}}, \code{\link[=rd_section]{rd_section()}}, \code{\link[=roc_proc_text]{roc_proc_text()}}, \code{\link[=roxy_block]{roxy_block()}}, \code{\link[=roxy_tag]{roxy_tag()}}, \code{\link[=roxy_tag_rd]{roxy_tag_rd()}}, \code{\link{tag_parsers}}, \code{\link[=tags_list]{tags_list()}} } \concept{extending} roxygen2/man/escape_examples.Rd0000644000176200001440000000213715173670373016246 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/rd-examples.R \name{escape_examples} \alias{escape_examples} \title{Escape examples} \usage{ escape_examples(x) } \description{ This documentation topic is used primarily for testing and to record our understanding of the \verb{\\example\{\}} escaping rules. See \url{https://developer.r-project.org/parseRd.pdf} for the details provided by R core. } \examples{ # In examples we automatically escape Rd comments (\%): 100 \%\% 30 # even if they are in strings "50\%" # and \\ and \v inside of strings and symbols "\\v" # vertical tab "\\\\" # but not comments: \l \v # other string escapes are left as is "\"" "\n" # Otherwise, backslashes and parentheses are left as is. This # means that you need to escape unbalanced parentheses, which typically only # occur in \dontshow{}: \dontshow{if (FALSE) \{ } print("Hello") \dontshow{ \} } # You also need to escape backslashes in infix operators and comments # (this is generally rare) `\%\\\\\%` <- function(x, y) x + y 10 \%\\\% 20 # \\\\ (renders as two backslashes) } \keyword{internal} roxygen2/man/RoxyTopic.Rd0000644000176200001440000002177515173670373015061 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/topic.R \name{RoxyTopic} \alias{RoxyTopic} \title{A \code{RoxyTopic} is an ordered collection of unique rd_sections} \description{ A \code{RoxyTopic} object corresponds to a generated \code{.Rd} file. } \keyword{internal} \section{Public fields}{ \if{html}{\out{
    }} \describe{ \item{\code{sections}}{Named list of sections. Each item must be an \code{\link[=rd_section]{rd_section()}} object.} \item{\code{filename}}{Path to the \code{.Rd} file to generate.} } \if{html}{\out{
    }} } \section{Methods}{ \subsection{Public methods}{ \itemize{ \item \href{#method-RoxyTopic-format}{\code{RoxyTopic$format()}} \item \href{#method-RoxyTopic-is_valid}{\code{RoxyTopic$is_valid()}} \item \href{#method-RoxyTopic-has_section}{\code{RoxyTopic$has_section()}} \item \href{#method-RoxyTopic-get_section}{\code{RoxyTopic$get_section()}} \item \href{#method-RoxyTopic-get_value}{\code{RoxyTopic$get_value()}} \item \href{#method-RoxyTopic-get_rd}{\code{RoxyTopic$get_rd()}} \item \href{#method-RoxyTopic-get_name}{\code{RoxyTopic$get_name()}} \item \href{#method-RoxyTopic-inherits_from}{\code{RoxyTopic$inherits_from()}} \item \href{#method-RoxyTopic-inherits_section_from}{\code{RoxyTopic$inherits_section_from()}} \item \href{#method-RoxyTopic-add}{\code{RoxyTopic$add()}} \item \href{#method-RoxyTopic-add_section}{\code{RoxyTopic$add_section()}} \item \href{#method-RoxyTopic-clone}{\code{RoxyTopic$clone()}} } } \if{html}{\out{
    }} \if{html}{\out{}} \if{latex}{\out{\hypertarget{method-RoxyTopic-format}{}}} \subsection{\code{RoxyTopic$format()}}{ Format the \code{.Rd} file. It considers the sections in particular order, even though Rd tools will reorder them again. \subsection{Usage}{ \if{html}{\out{
    }} \preformatted{RoxyTopic$format(...)} \if{html}{\out{
    }} } \subsection{Arguments}{ \if{html}{\out{
    }} \describe{ \item{\code{...}}{Passed to the \code{format()} methods of the \code{\link[=rd_section]{rd_section()}} objects, the sections.} } \if{html}{\out{
    }} } \subsection{Returns}{ Character string. } } \if{html}{\out{
    }} \if{html}{\out{}} \if{latex}{\out{\hypertarget{method-RoxyTopic-is_valid}{}}} \subsection{\code{RoxyTopic$is_valid()}}{ Check if an \code{.Rd} file is valid \subsection{Usage}{ \if{html}{\out{
    }} \preformatted{RoxyTopic$is_valid()} \if{html}{\out{
    }} } \subsection{Returns}{ Logical flag, \code{TRUE} for valid \code{.Rd} files } } \if{html}{\out{
    }} \if{html}{\out{}} \if{latex}{\out{\hypertarget{method-RoxyTopic-has_section}{}}} \subsection{\code{RoxyTopic$has_section()}}{ Check if an \code{.Rd} file has a certain section. \subsection{Usage}{ \if{html}{\out{
    }} \preformatted{RoxyTopic$has_section(type)} \if{html}{\out{
    }} } \subsection{Arguments}{ \if{html}{\out{
    }} \describe{ \item{\code{type}}{Section type, a character scalar.} } \if{html}{\out{
    }} } \subsection{Returns}{ Logical flag. } } \if{html}{\out{
    }} \if{html}{\out{}} \if{latex}{\out{\hypertarget{method-RoxyTopic-get_section}{}}} \subsection{\code{RoxyTopic$get_section()}}{ Query a section. \subsection{Usage}{ \if{html}{\out{
    }} \preformatted{RoxyTopic$get_section(type)} \if{html}{\out{
    }} } \subsection{Arguments}{ \if{html}{\out{
    }} \describe{ \item{\code{type}}{Section type, a character scalar.} } \if{html}{\out{
    }} } \subsection{Returns}{ The \link{rd_section} object representing the section, or \code{NULL} if the topic has no such section. } } \if{html}{\out{
    }} \if{html}{\out{}} \if{latex}{\out{\hypertarget{method-RoxyTopic-get_value}{}}} \subsection{\code{RoxyTopic$get_value()}}{ Query the value of a section. This is the value of the \link{rd_section} object. \subsection{Usage}{ \if{html}{\out{
    }} \preformatted{RoxyTopic$get_value(type)} \if{html}{\out{
    }} } \subsection{Arguments}{ \if{html}{\out{
    }} \describe{ \item{\code{type}}{Section type, a character scalar.} } \if{html}{\out{
    }} } \subsection{Returns}{ Value. } } \if{html}{\out{
    }} \if{html}{\out{}} \if{latex}{\out{\hypertarget{method-RoxyTopic-get_rd}{}}} \subsection{\code{RoxyTopic$get_rd()}}{ Get the Rd code of a section. \subsection{Usage}{ \if{html}{\out{
    }} \preformatted{RoxyTopic$get_rd(type)} \if{html}{\out{
    }} } \subsection{Arguments}{ \if{html}{\out{
    }} \describe{ \item{\code{type}}{Section type, a character scalar.} } \if{html}{\out{
    }} } \subsection{Returns}{ Character vector, one element per line. } } \if{html}{\out{
    }} \if{html}{\out{}} \if{latex}{\out{\hypertarget{method-RoxyTopic-get_name}{}}} \subsection{\code{RoxyTopic$get_name()}}{ Get the value of the \code{name} section. This is the name of the Rd topic. \subsection{Usage}{ \if{html}{\out{
    }} \preformatted{RoxyTopic$get_name()} \if{html}{\out{
    }} } \subsection{Returns}{ Character scalar. } } \if{html}{\out{
    }} \if{html}{\out{}} \if{latex}{\out{\hypertarget{method-RoxyTopic-inherits_from}{}}} \subsection{\code{RoxyTopic$inherits_from()}}{ Query the topics this topic inherits \code{type} from. \subsection{Usage}{ \if{html}{\out{
    }} \preformatted{RoxyTopic$inherits_from(type)} \if{html}{\out{
    }} } \subsection{Arguments}{ \if{html}{\out{
    }} \describe{ \item{\code{type}}{Section type, a character scalar.} } \if{html}{\out{
    }} } \subsection{Returns}{ A character vector of topic names. } } \if{html}{\out{
    }} \if{html}{\out{}} \if{latex}{\out{\hypertarget{method-RoxyTopic-inherits_section_from}{}}} \subsection{\code{RoxyTopic$inherits_section_from()}}{ Query the topics this topic inherits sections from. \subsection{Usage}{ \if{html}{\out{
    }} \preformatted{RoxyTopic$inherits_section_from()} \if{html}{\out{
    }} } \subsection{Returns}{ A character vector of topic names. } } \if{html}{\out{
    }} \if{html}{\out{}} \if{latex}{\out{\hypertarget{method-RoxyTopic-add}{}}} \subsection{\code{RoxyTopic$add()}}{ Add one or more sections to the topic. \subsection{Usage}{ \if{html}{\out{
    }} \preformatted{RoxyTopic$add(x, block = "???", overwrite = FALSE)} \if{html}{\out{
    }} } \subsection{Arguments}{ \if{html}{\out{
    }} \describe{ \item{\code{x}}{Section(s) to add. It may be another \code{RoxyTopic} object, all of its sections will be added; or an \link{rd_section} object; or a list of \link{rd_section} objects to add.} \item{\code{block}}{Name of block to use in error messages.} \item{\code{overwrite}}{Whether to overwrite an existing section. If \code{FALSE} then the two sections will be merged.} } \if{html}{\out{
    }} } } \if{html}{\out{
    }} \if{html}{\out{}} \if{latex}{\out{\hypertarget{method-RoxyTopic-add_section}{}}} \subsection{\code{RoxyTopic$add_section()}}{ Add a section. \subsection{Usage}{ \if{html}{\out{
    }} \preformatted{RoxyTopic$add_section(section, block = "???", overwrite = FALSE)} \if{html}{\out{
    }} } \subsection{Arguments}{ \if{html}{\out{
    }} \describe{ \item{\code{section}}{\link{rd_section} object to add.} \item{\code{block}}{Name of block to use in error messages.} \item{\code{overwrite}}{Whether to overwrite an existing section. If \code{FALSE} then the two sections will be merged.} } \if{html}{\out{
    }} } \subsection{Details}{ Ensures that each type of name (as given by its name), only appears once in \code{self$sections}. This method if for internal use only. } } \if{html}{\out{
    }} \if{html}{\out{}} \if{latex}{\out{\hypertarget{method-RoxyTopic-clone}{}}} \subsection{\code{RoxyTopic$clone()}}{ The objects of this class are cloneable with this method. \subsection{Usage}{ \if{html}{\out{
    }} \preformatted{RoxyTopic$clone(deep = FALSE)} \if{html}{\out{
    }} } \subsection{Arguments}{ \if{html}{\out{
    }} \describe{ \item{\code{deep}}{Whether to make a deep clone.} } \if{html}{\out{
    }} } } } roxygen2/man/vignette_roclet.Rd0000644000176200001440000000165515173670373016311 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/vignette.R \name{vignette_roclet} \alias{vignette_roclet} \title{Re-build outdated vignettes} \usage{ vignette_roclet() } \description{ This roclet rebuilds outdated vignettes with \link[tools:buildVignette]{tools::buildVignette}, but we no longer recommend it because we no longer recommend storing built vignettes in a package. By default, it will rebuild all vignettes if the source file is newer than the output pdf or html. (This means it will automatically re-build the vignette if you change the vignette source, but \emph{not} when you change the R code). If you want finer control, add a Makefile to \verb{vignettes/} and roxygen2 will use that instead. To prevent RStudio from re-building the vignettes again when checking your package, add \code{--no-build-vignettes} to the "Build Source Package" field in your project options. } \keyword{internal} roxygen2/man/markdown_pass1.Rd0000644000176200001440000000312515173670373016037 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/markdown.R \name{markdown_pass1} \alias{markdown_pass1} \title{Expand the embedded inline code} \usage{ markdown_pass1(text) } \arguments{ \item{text}{Input text.} } \value{ Text with R code expanded. A character vector of the same length as the input \code{text}. } \description{ Expand the embedded inline code } \details{ For example this becomes two: 2. Variables can be set and then reused, within the same tag: The value of \code{x} is 100. We have access to the internal functions of the package, e.g. since this is \emph{roxygen2}, we can refer to the internal \code{markdown} function, and this is \code{TRUE}: TRUE. To insert the name of the current package: roxygen2. The \code{iris} data set has 5 columns: \code{Sepal.Length}, \code{Sepal.Width}, \code{Petal.Length}, \code{Petal.Width}, \code{Species}. \if{html}{\out{
    }}\preformatted{# Code block demo x + 1 #> [1] 101 }\if{html}{\out{
    }} Chunk options: \if{html}{\out{
    }}\preformatted{names(mtcars) nrow(mtcars) #> [1] "mpg" "cyl" "disp" "hp" "drat" "wt" "qsec" "vs" "am" "gear" #> [11] "carb" #> [1] 32 }\if{html}{\out{
    }} Plots: \if{html}{\out{
    }}\preformatted{plot(1:10) }\if{html}{\out{
    }} \figure{test-figure-1.png} Alternative knitr engines: \if{html}{\out{
    }}\preformatted{```\{r\} # comment this <- 10 is <- this + 10 good <- this + is }\if{html}{\out{
    }} Also see \code{vignette("rd-formatting")}. } \keyword{internal} roxygen2/man/tags-reuse.Rd0000644000176200001440000000705215173670373015170 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/tag-metadata.R \name{tags-reuse} \alias{tags-reuse} \alias{@describeIn} \alias{@eval} \alias{@evalRd} \alias{@includeRmd} \alias{@inherit} \alias{@inheritDotParams} \alias{@inheritParams} \alias{@inheritSection} \alias{@order} \alias{@rdname} \alias{@template} \alias{@templateVar} \title{Tags that help you reuse documentation} \usage{ #' @describeIn ${1:destination} ${2:description} #' @eval ${1:r-code} #' @evalRd ${1:r-code} #' @includeRmd man/rmd/${1:filename}.Rmd #' @inherit ${1:source} ${2:components} #' @inheritDotParams ${1:source} ${2:arg1 arg2 arg3} #' @inheritParams ${1:source} ${2:arg1 arg2 arg3} #' @inheritSection ${1:source} ${2:section name} #' @order ${1:number} #' @rdname ${1:topic-name} #' @template ${1:path-to-template} #' @templateVar ${1:name} ${2:value} } \description{ Learn the full details in \code{vignette('reuse')}. Key tags: \itemize{ \item \verb{@describeIn $\{1:destination\} $\{2:description\}}: Document a function or method in the \code{destination} topic. \item \verb{@inherit $\{1:source\} $\{2:components\}}: Inherit one or more documentation components from another topic. If \code{components} is omitted, all supported components will be inherited. Otherwise, specify individual components to inherit by picking one or more of \code{params}, \code{return}, \code{title}, \code{description}, \code{details}, \code{seealso}, \code{sections}, \code{references}, \code{examples}, \code{author}, \code{source}, \code{note}, and \code{format}. \item \verb{@inheritDotParams $\{1:source\} $\{2:arg1 arg2 arg3\}}: Automatically generate documentation for \code{...} when you're passing dots along to another function. We recommend supplying explicit argument names for maximum clarity and consistency. \item \verb{@inheritParams $\{1:source\} $\{2:arg1 arg2 arg3\}}: Inherit argument documentation from another function. Only inherits documentation for arguments that aren't already documented locally. Optionally followed by a list of argument names to include or exclude, using the same syntax as \verb{@inheritDotParams}. \item \verb{@inheritSection $\{1:source\} $\{2:section name\}}: Inherit a specific named section from another topic. \item \verb{@order $\{1:number\}}: Override the default (lexigraphic) order in which multiple blocks are combined into a single topic. \item \verb{@rdname $\{1:topic-name\}}: Override the file name of generated \code{.Rd} file. Can be used to combine multiple blocks into a single documentation topic. } Other less frequently used tags: \itemize{ \item \verb{@eval $\{1:r-code\}}: Evaluate arbitrary code in the package namespace and insert the results back into the block. Should return a character vector of lines. \item \verb{@evalRd $\{1:r-code\}}: Evaluate arbitrary code in the package namespace and insert the results back as into the block. Should return a character vector of lines. \item \verb{@includeRmd man/rmd/$\{1:filename\}.Rmd}: Insert the contents of an \code{.Rmd} into the current block. Superseded in favour of using a code chunk with a child document. \item \verb{@template $\{1:path-to-template\}}: Use a roxygen2 template. Now superseded in favour of inline R code. \item \verb{@templateVar $\{1:name\} $\{2:value\}}: Define variables for use in a roxygen2 template. } } \seealso{ Other documentation tags: \code{\link{tags-index-crossref}}, \code{\link{tags-rd-R6}}, \code{\link{tags-rd-S3}}, \code{\link{tags-rd-S4}}, \code{\link{tags-rd-S7}}, \code{\link{tags-rd-datasets}}, \code{\link{tags-rd-functions}} } \concept{documentation tags} roxygen2/man/object.Rd0000644000176200001440000000114215173670373014351 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/object-from-call.R \name{object} \alias{object} \title{Constructors for S3 object to represent R objects} \usage{ object(value, alias, type) } \arguments{ \item{value}{The object itself.} \item{alias}{Alias for object being documented, in case you create a generator function with different name.} \item{type}{Type of the object, character. E.g. \code{"data"} or \code{"s4method"}.} } \description{ These objects are usually created by the parsers, but it is also useful to generate them by hand for testing. } \keyword{internal} roxygen2/man/roclet.Rd0000644000176200001440000000423615173670373014402 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/roclet.R \name{roclet} \alias{roclet} \alias{roclet_preprocess} \alias{roclet_process} \alias{roclet_output} \alias{roclet_clean} \alias{roclet_tags} \title{Build a new roclet} \usage{ roclet(subclass, ...) roclet_preprocess(x, blocks, base_path) roclet_process(x, blocks, env, base_path) roclet_output(x, results, base_path, ...) roclet_clean(x, base_path) roclet_tags(x) } \arguments{ \item{subclass}{Class of the roclet, character vector.} \item{x}{A \code{roclet} object.} \item{blocks}{A list of \link{roxy_block} objects.} \item{base_path}{Path to root of source package.} \item{env}{Package environment.} \item{results}{Value returned from your \code{roclet_process()} method.} } \description{ Roclets are roxygen2's plugin system for producing different types of output, like Rd files (\code{\link[=rd_roclet]{rd_roclet()}}) or the \code{NAMESPACE} file (\code{\link[=namespace_roclet]{namespace_roclet()}}). To create a new roclet, you will need to create a constructor function that calls \code{roclet()}, and then implement the methods described below. See \code{vignette("extending")} for more details. } \section{Methods}{ \itemize{ \item \code{roclet_preprocess()} is called after blocks have been parsed but before code has been evaluated. This should only be needed if your roclet affects how code will be evaluated. Should return a roclet. \item \code{roclet_process()} called after blocks have been evaluated; i.e. the \verb{@eval} tag has been processed, and the object associated with each block has been determined. \item \code{roclet_output()} is given the output from \code{roclet_process()} and should produce files on disk. \item \code{roclet_clean()} called when \code{roxygenise(clean = TRUE)}. Should remove any files created by the roclet. } \subsection{Deprecated methods}{ \code{roclet_tags()} is no longer used; instead provide a \code{\link[=roxy_tag_parse]{roxy_tag_parse()}} method for each tag. } } \examples{ # Custom roclet custom_roclet <- roclet("custom") # Roclet that extends the existing Rd roclet. supercharged_rd_roclet <- roclet(c("cool", "rd")) } \keyword{internal} roxygen2/man/markdown-test.Rd0000644000176200001440000000154515173670373015711 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/markdown-link.R \name{markdown-test} \alias{markdown-test} \title{Dummy page to test roxygen's markdown formatting} \description{ Links are very tricky, so I'll put in some links here: Link to a function: \code{\link[=roxygenize]{roxygenize()}}. Link to an object: \link{roxygenize} (we just treat it like an object here. } \details{ Link to another package, function: \code{\link[desc:desc]{desc::desc()}}. Link to another package, non-function: \link[desc:desc]{desc::desc}. Link with link text: \link[=roxygenize]{this great function}, \code{\link{roxygenize}}, or \link[=roxygenize]{that great function}. In another package: \link[desc:desc]{and this one}. This is a table:\tabular{lr}{ \strong{foo} \tab \strong{bar} \cr 1 \tab 2 \cr 100 \tab 200 \cr } } \keyword{internal} roxygen2/man/tags-rd-functions.Rd0000644000176200001440000000604015173670373016454 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/tag-metadata.R \name{tags-rd-functions} \alias{tags-rd-functions} \alias{@description} \alias{@details} \alias{@example} \alias{@examples} \alias{@examplesIf} \alias{@noRd} \alias{@param} \alias{@rawRd} \alias{@return} \alias{@returns} \alias{@title} \alias{@usage} \title{Tags for documenting functions} \usage{ #' @description${1:A short description...} #' @details${1:Additional details...} #' @example ${1:path}.R #' @examples${1:# example code} #' @examplesIf ${1:condition}${2:# example code} #' @noRd #' @param ${1:name} ${2:description} #' @rawRd ${1:rd} #' @return ${1:description} #' @returns ${1:description} #' @title ${1:title} #' @usage ${1:fun}(${2:arg1, arg2 = default, ...}) } \description{ Learn the full details in \code{vignette('rd-functions')}. Key tags: \itemize{ \item \verb{@description$\{1:A short description...\} }: A short description of the purpose of the function. Usually around a paragraph, but can be longer if needed. \item \verb{@example $\{1:path\}.R}: Embed examples stored in another file. \item \verb{@examples$\{1:# example code\} }: Executable R code that demonstrates how the function works. Code must run without error. \item \verb{@examplesIf $\{1:condition\}$\{2:# example code\} }: Run examples only when \code{condition} is \code{TRUE}. \item \verb{@noRd}: Suppress \code{.Rd} generation for a block. Use for documentation blocks that should only be visible in the source code. \item \verb{@param $\{1:name\} $\{2:description\}}: Describe a function input. Should describe acceptable input types and how it affects the output. \code{description} is usually one or two sentences but can be as long as needed. Document multiple arguments by separating their names with commas without spaces. \item \verb{@returns $\{1:description\}}: Describe the function's output. Typically will be a 1-2 sentence description of the output type, but might also include discussion of important errors or warnings. \item \verb{@title $\{1:title\}}: A one-line description of the function shown in various indexes. An explicit \verb{@title} is not usually needed as by default it is taken from the first paragraph in the roxygen block. \item \verb{@usage $\{1:fun\}($\{2:arg1, arg2 = default, ...\})}: Override the default usage generated by roxygen2. Only needed when roxygen2 fails to correctly derive the usage of your function. } Other less frequently used tags: \itemize{ \item \verb{@details$\{1:Additional details...\} }: Additional details about the function. Generally superseded by instead using a level 1 heading. \item \verb{@rawRd $\{1:rd\}}: Insert literal text directly into the \code{.Rd} file. \item \verb{@return $\{1:description\}}: Describe the function's output. Superseded in favour of \verb{@returns}. } } \seealso{ Other documentation tags: \code{\link{tags-index-crossref}}, \code{\link{tags-rd-R6}}, \code{\link{tags-rd-S3}}, \code{\link{tags-rd-S4}}, \code{\link{tags-rd-S7}}, \code{\link{tags-rd-datasets}}, \code{\link{tags-reuse}} } \concept{documentation tags} roxygen2/man/tags_list.Rd0000644000176200001440000000160115173670373015074 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/tag-metadata.R \name{tags_list} \alias{tags_list} \alias{tags_metadata} \title{Access metadata about built-in or available tags} \usage{ tags_list(built_in = TRUE) tags_metadata() } \arguments{ \item{built_in}{Logical. Whether to restrict the result to built-in tags (\code{TRUE}) or to extend it to available tags (\code{FALSE}).} } \description{ Access metadata about built-in or available tags } \seealso{ Other extending: \code{\link[=load_options]{load_options()}}, \code{\link[=parse_package]{parse_package()}}, \code{\link[=rd_section]{rd_section()}}, \code{\link[=roc_proc_text]{roc_proc_text()}}, \code{\link[=roclet_find]{roclet_find()}}, \code{\link[=roxy_block]{roxy_block()}}, \code{\link[=roxy_tag]{roxy_tag()}}, \code{\link[=roxy_tag_rd]{roxy_tag_rd()}}, \code{\link{tag_parsers}} } \concept{extending} roxygen2/man/is_s3_generic.Rd0000644000176200001440000000141215173670373015617 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/object-s3.R \name{is_s3_generic} \alias{is_s3_generic} \alias{is_s3_method} \title{Determine if a function is an S3 generic or S3 method} \usage{ is_s3_generic(name, env = parent.frame()) is_s3_method(name, env = parent.frame()) } \arguments{ \item{name}{Name of function.} \item{env}{Base environment in which to look for function definition.} } \description{ \code{is_s3_generic} compares name to \code{.knownS3Generics} and \code{.S3PrimitiveGenerics}, then looks at the function body to see if it calls \code{\link[=UseMethod]{UseMethod()}}. \code{is_s3_method} builds names of all possible generics for that function and then checks if any of them actually is a generic. } \keyword{internal} roxygen2/man/figures/0000755000176200001440000000000015173670372014261 5ustar liggesusersroxygen2/man/figures/lifecycle-stable.svg0000644000176200001440000000247215154610530020203 0ustar liggesusers lifecycle: stable lifecycle stable roxygen2/man/figures/lifecycle-experimental.svg0000644000176200001440000000245015154610530021422 0ustar liggesusers lifecycle: experimental lifecycle experimental roxygen2/man/figures/lifecycle-deprecated.svg0000644000176200001440000000244015154610530021024 0ustar liggesusers lifecycle: deprecated lifecycle deprecated roxygen2/man/figures/lifecycle-superseded.svg0000644000176200001440000000244015154610530021067 0ustar liggesusers lifecycle: superseded lifecycle superseded roxygen2/man/figures/logo.png0000644000176200001440000004734614520721622015733 0ustar liggesusersPNG  IHDRޫh cHRMz&u0`:pQ<bKGDtIME 4$6HwN7IDATxi$Wu;95P=IZyh$ f6l56kyz]>u \c Anj%zꮹr#p""2*3VJ]1. ?  `E :}.ZGq|7YS]">\8%p"?=- .{.{u> g}!T~%r%p\2wBȽ=n8]|7D ԑ7 +f{2QG\k-@`~ ?T%r7%p\$a``a:#F]|Ct '%r%pAm(svT&ղ!Mjx # ..;:j^q ٟM\M9LZ"r!Op !4˗|>@~"(l b!x D@_^7D \2| fi'xcTIR$Jh/L]".\"ވJ 4?H$uٷW:d-J3d2i Z"m}DQ4M߄3iͤI&g(@f9_- \Ea'x{%Jt:I*Z-yl\"\-DV&T,Lΐd0͖z. mBe~\?7 ':3.wəi |;?-b%d~6i|~?D?h ]W prlV۲/|e%rp d~B׵z}x}9lV*eR$tj:&,Jĭ%phVI GH$ >9( fgH,?EexX@ o@xP8i]q,J$ϑX,?&[|yX uʀhfYha@ǙtTjJ-id+[\ltd~oE- ,Sqb>|ەĝ1VeRY4Ѳ@a_U- +,ҐR:q>CG•-^=zV >mlvNc HO4G8R'seErFT0edRm-3⚗-!WG05fIxDDcr6.q[@ݵV*dIFEIM},"=" %{mCX][\hl߲ek (? ' #4[NGJӴYJ-~ G֘lG2{yu9'TK?NIk(kDسq_c, GbDculZ+YMR)I--&[,@ 󻅚/e~PXZuDXPq>-yllgFi| jIhoHqzAݽI>UNl4NMx+DmE "Ѹs\v)jl&E6Ӷn/dCŮuQVe~N(%p3Vse,\=ߠ~A %<59x?5(8O&5Kbl9XU#\BM淵}D}‘92.]:s-r2iC[d_^])[SGwPthܑ~D]jB.2D%[w|7Bo 2`8J$)ʥ",\]ŇPb`.&kfsDu2N@ǟ-@ɑMR*EDu[{ఽSD^{@܍(ߟ'K tr:F L KM'GxlqCANTc2?C(#ZwvW+r$L[IHT6׃t m(?-2@(9OV!R*I')"[,2WhyEGC|5P3_|XPxt9p:-Jb>G6]K˄eR t=Me-\AM淩}:XPZYk+IЍ@_pAjj'4)ҳTʥvZzjl#Z@~/_]P4N(R~CV&{5~_'i=f-R$_6Ȗ *A*1L±QWidI]M [lHd~o@- Cd~pp,ǻ2ހĔ~F;l!'t ]hu`JITDΕ frΧJ.rbhH*_bH͞]P?*LJl?R->D eAì{=J^ Bc}d~3Zi-}~r͖;x=MX1V861.Q1K:w(mK̠O%2Jd~^s^ϫwi+6I-{)0d:[,OKr\tjbݻ̍CfYj3ƥZG(5ߍ\P2Psk2F\Pse}qY[/(WMO+ۖndCi{ㅈCM& "}x׹OJG}ܳ{gs{)%' i;:KPu*"tb-25olqޯ4Wd~Od~>p%inWs;aJ9p*M x!@,\jK7$n/ Cff.õ{`ݲ[/Ziye|uq''lfeնeD7- XPW(4O07#լ=|!xuW[6)o}<_Omb1H I\RHS-Z梲Eaז5vZ h^?>hf: Ӕ$B&޼g7fŐ)V %aRZYrRbVKTr)e 7E0/?2 h?pU++е2V )Io+|!=WoXB٘s_[tCt!A=>iiԬFYɛb ;Nj7"l?%uR>UH$ ;B|➭!)3Lh=ba]VsvSFxma * B=ig<4I^\tP ;8;\&$ښeB| ۹t(Z!ط3ϣ_f,UZv.!1/! <|J1fe&V}l-< chz$tMiS0apo풷~8<%W4{Zyʆp@~2tt_/&Ҩ6,[Ydž$}i=IپW74%#}~>yuA^7ny0t2bˌK+^$B;] [RVÃ7f\_۳pTlM)<|-\}ދa*_z,ڎNK)-yaj F}A |X|XJ4F|%<|6jԵ)%^]{7q%NNG kk8~zLUQtpxC1--icm} +lz&z {{] ^]}7otgORT:$Ծ{|hAP)/%XmEL!!Z;ćӦ)%;B|`ƞOh!/o Lekf҉ᴝVz-m 5UA*O=+rzu>w{4=r%p0{0_~lT:}@BE[ K@ l%j- ~o+6zB=?w̕yD 轓niJpǻn[f6_ak_kY)))VL*B\\TA]:/[lPuVP@47mܼt^M^ ]B`# x)3q.ma"<槇zu^Wçk`%&IjR5x IAU#bFeߥ ~yd{ѭ."0ԭ[ѫeN؜sUmoZR$UsSw,^?[5$2w\nfSb}vGm bHp!ڥ.#09mw]޿bkFdBtJd/T Q<!NЫ^]d2|T& 8zvvz7yG_,}xEnyu$(k]ؒw_޿b7M{we8{[x4U6s8ϣ ˆl< F3_<9{%+^ҫk{OKQ/_ku;yK 6-FH-0%ܸ--BNȧ3ש7.P& <Ls|{eD44{nڰ. ݪ((UѶ$E^F!j[vښyXJ_׸}Wߪ~uȥz-g<yA ~eTܶ3fgsꘪ8BFMΠ]MY H [TŎ«k!>$ -qj*öa/VyBjD"~2/fKxp=gS3ϩ6u$hru9粍agɫ^v7 =D`h !1;dRh\%݊х9B`JU9nmFwb7!uz ]Z vr)%T4W>}uщz0aNdt ہns5PS0Mo8UDٱ/$]:43);`4RzУQ_clKYo5ʗ Mdw t-Wql#@#mZjK r@x5nW߼NLs%4ϟS]y\99.q\a'.^4NvSs/v,;|MN̪!q^>[/<9,Tg=1k+uO`l(VLN3 ܻ;v%0LDW8GjJJչGu^rT=L{ 1@Jr%J4d zu>o[N'Ɏ?JUӪk]op |8D懥J]1{7֮M)1f;x+^f[K%N2Eliu 4";e}R"d:W᫏W:vɼ"p,q}%2 \`:۹ ^ĖuLXTrIs DjZ1&ҿc nt'LSy{Ю1p MNL:u?/ >k$UsVע&' k.:^f**}l[US:rzmKeB8=Sd"Sq4[z@ުx& W S%?{V2Mti^NtQHBtjSJf/+|<}"&Iuʆd+~Lp536C!v.8+ҡ z \1颻$^uڔW,E ϞJ٬k`/.hB0CNu /ٷPQK^܃^Mu:5GEhyeF3>o_Sjy~ȗMGFbp "l_O-uTMa"Sǝ4%E|n.MS)~~x*.aJw`9ku"B& lkTM?+c;UOѡI@6qYc.:l]Bj.MRe-U--Β*` _!Η ~rxrtPUS|JN-Ucg9:^k"~n `θgNC/ T :#: ն][IX}ONٷ p f!(UM(2Z)y>0:}!/xVQBy, p Є _W颓0䧇oG)UnدpۮWcItn\4M0)_f;}8+ Ô$_fvȫ'wlM{фj|k\7ͷWai=W9kjoGY)A9;FT%NQ5\p °g>t\߹~k b?u=[˴ ;7 'c+{u.W&#g-mf0zNt/=6cGk_cwmk*gɗ +M*Ƀ'89U-ў3'coqlRJ ʛ i\2+e37nvЖs"90O-4%O޳7\5&797:OZ5+/@Y^e!+|巣wC^Wl {cdIUK;2)/J =*&ye'ܸ=}W p`gnT)~4/ZceNi6'sȻ6Fj.6eG^)v mg[.m @Ы1dKUOxx'8=Sbh;Pn^Q5Lfswmbd])%PlǺn Ĕjf=q({F"ly>͢T5U89UhCg3*-pLlN6'$@ ?.!o?ԑ\}mHLRb G}CJ|~K\yK5Ֆs,g/v;/Lp#}>iY5%bSE:)҅3%j G kSiEWCnfpHJ^bc c |X>?B$#of(G"T%_'& Q;ᘏ;/16B*$Uҹ,4ͫc9^n ?< ?=<5$Ѹa{;.cpXǺS5%e,Gyq4,>r<{*w#-}=b Fxu[6L&e:/0-;IXά)$ 2*$>ogw=Av AtM6{7m 0t$@ž] }z<1q(w]Oq|VE!lSynu3B&~ӹʼ&xՃL#8t6 g3Wl Y x5=:K*_0%\:u(B18Bp%1>w;v^]sö?uNjS8ߙdcnbߥq{tAcC ۢI<8A0KE=#W4-;|h&9}Ύ ;\%ʗ=ˉ¢$^LS꯵zl m;MxRflk];nHد;TPL|M-^ M\%ʆy,O,YlCĩ]¼aĹy3?n؀Z̖SUyl33 +6yX3&1 COH嫎x}|bA>XLdʤtM0ZfB~MS>Ӕl {rK8,rt-;?qI,-gK8vu[D-J)yl0*( {ov߽a~|zئ޾6G0%&?;)#{l2ώwl ޴Sf,SڔpJ -I~Ҵ:F!0M3'R"Ržb>-hJ9bs"YA@>JҚ58' |➭ E}"D-z ~nSb/)%ER1taJxlf~z{tbY:ݥY)wܝWr]7T59>ݾw;xuDB\Ζ|;\E-hı$7yӞAL nc0x * vЁ`V4m16ou|\)7'D.EW"].Ock9|`/3kv2h>F2?8dR]% `Z;q/T߼*T3c*]9+&BS:K$6 Sr>YrfALl-hă޹ag9bP1Vd7c2S^t Y,ܰ=O^$SZgUys|f܃^M8\O1@v ?zɒ+x=‰i39Xk-}u# |N&8t6-zv`j.x4a=-REÔƜ}< ct0EnKpL04' }"E.(뎹,|d9X4_A*KLvjhد/kxM̒K2f^S2'wnaWui~dKQ@}LNW9'96_41`VcInCTV;kjJxi0MI_Gu[NZB[Z`x`ī2u< xʥG|*ض0P?~0MI"{%qz>w*͗%] }1d*[p#}'Ob?:( f91%\27ng)?q'SMcg%\距`֩s Q7ˆCls$ ʣL!s3-(6\2զYjU[Tc{Fmg!3Y2$/!>Wۯ"\_2T 5.3|LJlHl*_[1RVDW~7/8P5ӧX묨5[<ʏ<=]+>0,)VA6X>EWcwl(yxJRok-|v4%/zng]sE/ RϒAݘ&rpc(^v"G3,ڞI߸gvB XIJwFbga2M` \)? !'Լ%!챉rǮZ"G%9u}& d(woeߥ4_z ok,_<ɩ򁽛 BYOY Tw; M(YŬ!Y%K>]pϕ|~|334I[I"cwm+ x9YR{r֘%"uK_H숣iꚛzIAf:YauHl:mj0%~yiS̎ibg +ѡI^QBog\3HNJއZNyGǎβdj4ϝIiyݰ3wFΞ\0`VRl pִ+*U!Pry6lX?VsftGLg+sö?ٰoSJ~Cc$/3'<}"l|@MPe'9ÕmtŊaJ~%& 3`@}fvdgf?o81U >Gay4ZXYiB0O#pvefڳU=4!V#g[b: ^Mi|zJx$' +~5M{7n`H%%a7X~tMkII'Lh[NkbI~0ۃ'¯8Gfw a[=#>k@LwO1T>)%~~VeT ₤҄`"S;)Mv[&B&TS:9 $?>49/>\S)>I~̜Ϩbp34MH}?9R=[hGl?1&g^P͹VUe)ǺI1X. uiH>t l)V)V.*hh:ˑ9^VQ;LSRLD)NMT9[Bl2,Y&<Ȇ&T9`k_fʚ w}l'%U "T/?6_抍a ud"S<&N ]S5sT4dW~;#* 0AnC+j' 4脥x~H:%Lɜ@٢Lg+"&xu&%x.p_ 4M]M^53^-sQJ/\Mj\ZbDK,Q3*|Ue[IR:3nj,T?Co~^M+]v-N4˵g[V|oxWr AN4x4i;.8Ʒ1mu)9mk{E>EPg(e(R]g^X%UH8KYQ֫a?‰: ߴaUZW/̑-'yےOw9L|Zʺe+ uac_(b /,\N堫 UpXcXj]!K`X8@@NiSSGsk暶r]/[>~{=Z˰Aq3s)s+Ad]M%Z9!n\U5$Oݻlb.~^h@뇔HU{ώ)dw g :ֺmԻBD֯UخE^efnدQAUA&rh.ʽnyM*2ҹ,$U% {rS[/AߘU1ޔխW^u}W Yu`HOgǗ̎!aa[ŨTMJU{5|iWb+hϔR]pBKK"iV,YR..>); M@hC$BZϾt(DЧ!PX}3d #Ӝĕ|d*[df99UJ )W4T)JiI߾h"ՄCxq4nx]՛ģk$2O(Eo{ګ (.&YNAv Ĭ(Szy좧s)r\Բ?/D\:"{{PNY/򏽁Pi.!?T٦P~0PFSZG8Su]?xxIt.P7iPܪ`9=jMk_o0 |Y5oCYKN&'(I{c36D,Qe_5"[t6`*e@U,DYĵ6f 22/{"=%[tX@W-d0[s Tg[&2>Xnf5cE=F? 2? :0eolya^3 לe'oe.z*1O%h]7Y'਽ĵ,#܊z'ZjN]vv `8~nc|^XiA(6 1?ve.=EM旃%U f0J `W3suyenZFsM%!8q5  D=, >7-hoqkc#jŷ٢/Gse.Q-+?ز̯ :>WNzD̯8ZО>*?7vyffۗ7i?Kd~ijM,/% %[/w_lq}‘9]ZswP)ѼlhU 5U_܇hBZ0ǁfdWb%e~/"DE$/t1ml1/we=Z#;|g[kgF"w˂+[m>%*Q2g6zn. [4plqbβPEm-f[M4Q]I>3ĵѓ#tAE㳪eŮ@[-TUe~GQ2C@z6-~k,M XilpsvPGVMXs_-4(+Ϡm9 E٢7tI(u9P] {z 57 -qe]GӤZj[%*AkGߊ t7-mP~nJ>_a{Z#5?dw]zhH,STvt9xk5K\f5lqpeMbϪغy;>Ck6H[@hw[te+_IU4V栙7:a DZ6h^7-vr>_%,]([-Q [v`^B2?x~]:Ul}(orjyq=\;h vjz-d~)'Cn=fo\ĵ>F2lP[-BV5KQm=&[-ё:doFm-FkC8O旡Zl9DG("]=:rV fۢ+[lr`F2P e:.y/1?Gu[lQTj- 灗.q/5FlqneJ旦ZjA jY1: 4nkS8OgUl؏JxȀKfI,"[^6Ob2/R!%ph njdRJJQu/rp5n-~Ԣl1/[t9B]z.ی:"GQO6bM_ezFm=Ԍ-%nx;d~@y){K% b_-5tmŚҲP*u,[-^G⽴,[ Ɩ'['S { U%*%* j[-bx-.(K+ FN,D,U wCh-ބVM{s>t9(Zs Qj'XC]z.;:"u[6AP)S(ra, [-^c~qTEa.y. DޅؒlF 2QA.q.-6gPU ]_%pe*{2K.G-^QW3p #hgoD]\dC-;5m}W׳p ܣh.jrO:2?pۋp h07|رWQ\4jTIǭn~k .do;*EoQve~k ?coܜtEXtSoftwareAdobe ImageReadyqe<IENDB`roxygen2/man/figures/test-figure-1.png0000644000176200001440000003261015173670372017365 0ustar liggesusersPNG  IHDR(iCCPkCGColorSpaceGenericRGB8U]hU>+$΃Ԧ5lRфem,lAݝi&3i)>A['!j-P(G 3k~s ,[%,-:t} }-+*&¿ gPG݅ج8"eŲ]A b ;l õWϙ2_E,(ۈ#Zsێ<5)"E6N#ӽEkۃO0}*rUt.iei #]r >cU{t7+ԙg߃xuWB_-%=^ t0uvW9 %/VBW'_tMۓP\>@y0`D i|[` hh)Tj0B#ЪhU# ~yhu fp#1I/I"0! 'Sdd:J5ǖ"sdy#R7wAgdJ7kʕn^:}nWFVst$gj-tԝr_װ_7Z ~V54V }o[G=Nd>-UlaY5V}xg[?k&>srq߀].r_r_qsGjy4k iQܟBZ-<(d=dKO a/zv7]ǰod}sn?TF'|3Nn#I?"mzv~K=گsl<b|_|4>?pߋQrib 2* (Ѧh{28oIyes8';Z9h6g>xRx'b8ՃWOϫ[xn%|^z}%x c8eXIfMM*iNi0IDATx T}?/HdqڈFA1HSFEMZy$YlMG})U>(5B"j+jpCMeqc9K{gΙs<9oysg2s[& @TK!@R@J( K8D(/^ @K @@{  @ . @5@J( K8D(/^ @K @@{  @ . @5@J( K8D(/^ @K @@{  @ . @5@J( K8D(/^ @K @@{  @ . @5@J( K8D(/^ @K @@{  @ . @5@J( K8D(/^ @K @@{  @ . @5@J( K8D(/^ @K @@{  @ . @6-஻+W.+(v7\Zj_\jq&M*ZUG-UEzѧO#}Ȑ!qiY 2̙Vʥ®R W @  J  @@>[_ @\|.*%@ ..\mNJ.P_|y\pѣG|cm:Ğ{&L(h~B|~ԨQ1o޼:uj.i/Y${8묳bҥ1r  @@I q 7{Wt1Zj[ne?j,#@Iϟ??zfkR#2~#6uc#p)7n\+;TWr^B|WXf;c93[AS W^ye 6,ڷoN{e*D'ݓ~~m(JGIo[r  @@(|RY X @ :  @@>c` @<|$@ Oyd, 3V<C@确N X @ :  @@>c` @<|$@ Oyd, 3V<C@确N X @ :  @@>c` @<|$@ Oyd, 3V<C@确N X @ :  @@>c` @@eW^~rfhӬ)G GѶmxꩧ裏K.d={XTT9 @@-Z={?9Xre|+_]w5 ֨la7ld h3gѣpOkӦML6-f(]k E @@&WvUҥK+i~aa#[ @@3 oƫ:tw޹JV~m @# zSgt υ] @ [  @@.>v @l|J'@\UJ*" saW)V@gt υ] @ [  @@.>v @l|J'@\UJ*" saW)V@gt υ] @ [  @@.>v @l|J'@\UJ*" saW)V@gt υ] @ [  @@.>v @l|J'@\UJhmM/}ժUGEǎ^=  Bn馸{ߏ}_[;k!_%KĕW^3f/>u#F>j-$@.,|;6mO~4-\G?㥗^?<k㎋tgǼy󪭲u+ХKb-bĉ~7Gr@3nܸ_s=7J|OoIBYf̙37J @@ eݻ?:uI≧w\ "=\{Fr??&LollG';㓃˗=d{B|ҬVZ{'kH@h04\zqgĩ}n->8qbq%# ׃c-W`>T 0 ϟ_ v @cmG(:2E@Fw9z8C;˗kTN`cohQ[nez:K.__ vӦMkQ:[{ 6|8묳2Ap  @@n>7z @|vJ&@ UL*& sW1N@ggd ύ^ @ ;  @@n>7z @|vJ&@ UL*& sW1N@ggd ύ^ @ ;  @@n>7z @|vJ&@ UL*& sW1N@ggd ύ^ @ ;  @@n>7z @|vJ&@ UL*& sW1NMvE+x'Cq>ZJ@ {tXbE;fG)0nܸ#<=zŋZE z 2${xwSNV[cuL4<3qwÆ :~e]ְGIN;{G\~rxgcٲe(.gC}[?նKt޽*O?G;qĈqqǭ(D^x!6|{bk8 O?}z\wW3zAIhCM i @. q .4ԑT  .زlGD`O5.5slM}{gL0  @`}d|&u޼y1ue]pO>sYgwď9r}eZG,7pC^Ij*jNeD[`ONϘ1joڵku @XE]'xb3&/ܹs|kd -!@>-N7g?p̝;7'){ Plu>inY:b\ @Xgni P7_uUbŊ@޽cV @ uk6~~&~m @x5\VJn"@O5ѣGGu|AH  @X>1رczx @:z^~[s̉zb @xv[\-8qbz^B3 @@OZ|eӅ^" @@>m$ @`#FBٌԓ:}]=  @oe)'u7i+.Poe)u&z>$@WR @^|v @FF`ٔԋ@kx$@|I̞=;V\{gl]-N@!2 |qgF# &km]Ol$(΋HM79/i\Vj%`֭ۚpO:thl6 /Ԫ!@``9+ЦMlb{3 2|w.V[m/xMߏ?O/}D=# e[ W_A#YfEV b"@ kV>$;Mhp H@h0u4  @DD+ @A@7Hx$@%%L]!@ A#(/` h   @@|SW @@oHJ$ K4B|G P"_ $< @   @ H@h0u4  @DD+ @A@7Hx$@%%L]!@ A#(/` h   @@|SW @@oHJ$ K4B|G P"_ $< @ 6.]K,)ԣ}wGydK/c7(P؀4iRs9-pHt(gώ1cč7SLqň#7,J)3pn ,qrHc &T63wx7?ϱbŊ,$PM /8wN w}wqv(D'=|8餓bС)ɓѣGG6 _|궯zlUYHG}:uXչsxw+!@@  {,=*JNp ѥKرcv!DSmzc޼yVYF^&J÷j*-Z{l(@!>In;bfmV$+miagrj'|2ׯ_ ]Q0߀|'ȑ#cnX@.cǎgy&{8uF)_p+7A"Pګ4FkL'lKT Js @è @R@Wz#@F @(/0 @@)|)Q' @@0GJ! K1:A*|9 P _a  P) += @RR N @J_aB@buT Js @è @R@Wz#@F @(/0 @@)|)Q' @@0GJ! K1:A*|9 P _a  P) += @R)E/t/_\sM<Ѯ] c]w>h,.#T^: oF\q1r8Ӱ/YWu \[^SNnݺرccv~qWǘ1cZ @ C!Xxq|+/ƢE*!@MgF ;Xt=L]v]3 l6P w}F_*^{9sfq(Ŧ !GW @\|*'@l\Jrd# qU*U@ʯr U @ W+  @@6>W @\|*'@l\Jrd# qU*U@ʯr U @ W+  @@6>W @\|*'@l\Jrd# qU*U@ʯr U @ W+  @@6>W @\|*'@l\Jrd# qU*U@ʯr @lmR?Xrem۶ -iI/rL:5/_|pӧ=-ؐ@!x2dHt19x饗ִ;}{k=.Lj#bv^zE߾}??ol)^GcƌIn-3gL# \lYp i3-q>vm (I?mڴx'}qEvX̚5QƍK T)9+~E~Wѿ]o? z\m'@@ I'G_n$Gof|ߌN;mN~M'N V[U˒Kn,X t'oaqg}t/Pk~z1z5=8sc>{2O t!}#?#OzjwJ-P#C=4;_y q*VYGvzo+V\G}:Y@-CP'G_Q4hP$? ム @@!N @y|z*B@b44o^O @BB F @4 P_a мy=F ! 1 AW@7 @@!|!A# @@ T(/0hh^߼J#@ @+ Si @à @y|z*B@b44o^O @BB F @4 P_a мy=F ! 1 AW@7 @@!|!A# @@ T(/0hh^߼J#@hSVwߍ={{QjpW7xcqaŵ^=&@ 8_?>VZ&LH9rd <8 XϞV @|N;m[lE5*& PdI}[̝;7& Pd>|x?9s[M<9>2dz/zƠo߾qWİaâ}.k;w^^V @Ơ_~oj(S-"@, 7P @xxcE @`&*O@oLldB @ ⍉ @ML P<_1" VeRꠀz*8H~Spmۖ5ۇ~:tye>vE֎3kWXk%,_<<&+cwlS MA}xSO.z[ 3g,z3}'tR=:?|uՈoV#6o16%@" e4B@7˦ @^|v @FF`ٔԋN kV=l|8,[nYͭ6[;M{롡Nl6fCs뢍~zټJrp>W~ @l|6J%@ \UNٸ** sW9F@gT ϕ_ @ R  @V\WD6>y1?+ omo]۔2|A 2$o&g4%\'cǎm[nov.+AXpa|;߉ݻǗx׬+_ԑidnxg㡇J?7Rl^M`ѢEqgj}c??Ŷn/rL4)~E򵵦 $o@wƌ_2=oۿۘ7o^ȑ#c]ws_GuT,]bHۓ_}ѱco߾1k֬&fO y1jԨhժէ{O>$ƍz?9sD׮]P]ϟm۶MC[ěoٰF ,_<&LwuWlV{M6-9hӦMy>|m6#6"Ml~헾LvO~xK[ɓcժUo~aMHN}v!ƌ]tI?~Jk"o}+~8蠃_z_oy<ѻwx˖-K_ +OXyE&-I'?&`?? ZfrԞL.#9.X꫱N;EnR۵O17t{.X }cijr?I'_idےp?"yyw4ro-OyfrJ9)֖qrF$y{Lo7[#pWaɽ8'N|+qUWMHy+v^dIt[aқkEڵ۾~azbI'>~$VvS;vahݺu':uS?=9M0pq-4zD:zcKvwG֬믿>=Ҽ{pOnfJBt7i'NHRN>ɻyS7G}t4\wON-?#ѿhTnyL^;.~_WM|dPR|?ɟJ=/|!'7L:Ӌ{55$G牓Ku#GÇoX ;/~#9m\'65M`~8z|&>{?6,~_VzmqV k $7.%>?H>\IN'Hnu l^J%@ +  @@6>W @\|*'@l\Jrd# qU*U@ʯr U @ W+  @@6>W @\|*'@l\Jrd# qU*U@ʯr U @ W+  @@6>W @\|*'P[G}4tJG_~y L2*@'guVlѷoH5L{n1p߆ű`[mU1k֬tݸqcիW V,]4-:uO?t~qկ~3f̈x FSLYSN9%:tgώ38# A[nI2dȚɂ֭[7%ړui.dzc=62GZ3-ԯV ,xSr*>Ҝ:Ϳ  @@M J @@m|mFj" k¬ P[_[o @0V@[m @&&*!@V J @@m|mFj" k¬ P[_[o @0V@[m @&&*!@VIENDB`roxygen2/man/tag_parsers.Rd0000644000176200001440000000422215173670373015417 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/tag-parser.R \name{tag_parsers} \alias{tag_parsers} \alias{tag_value} \alias{tag_inherit} \alias{tag_name} \alias{tag_two_part} \alias{tag_name_description} \alias{tag_words} \alias{tag_words_line} \alias{tag_toggle} \alias{tag_code} \alias{tag_examples} \alias{tag_markdown} \alias{tag_markdown_with_sections} \title{Parse tags} \usage{ tag_value(x, multiline = FALSE) tag_inherit(x) tag_name(x) tag_two_part( x, first, second, required = TRUE, markdown = TRUE, multiline = FALSE ) tag_name_description(x) tag_words(x, min = 0, max = Inf, multiline = FALSE) tag_words_line(x) tag_toggle(x) tag_code(x) tag_examples(x) tag_markdown(x) tag_markdown_with_sections(x) } \arguments{ \item{x}{A \link{roxy_tag} object to parse} \item{multiline}{If \code{FALSE} (the default), tags that span multiple lines will generate a warning. Set to \code{TRUE} for tags where multiline content is expected (e.g., \verb{@usage}, \verb{@rawRd}).} \item{first, second}{Name of first and second parts of two part tags} \item{required}{Is the second part required (TRUE) or can it be blank (FALSE)?} \item{markdown}{Should the second part be parsed as markdown?} \item{min, max}{Minimum and maximum number of words} } \value{ A \link{roxy_tag} object with the \code{val} field set to the parsed value. } \description{ These functions parse the \code{raw} tag value, convert a string into a richer R object and storing it in \code{val}, or provide an informative warning and returning \code{NULL}. } \section{New tag}{ To create a new \verb{@mytag} define \code{roxy_tag_parse.roxy_tag_mytag()}. It should either call one of the functions here, or directly set \code{x$val}. } \seealso{ Other extending: \code{\link[=load_options]{load_options()}}, \code{\link[=parse_package]{parse_package()}}, \code{\link[=rd_section]{rd_section()}}, \code{\link[=roc_proc_text]{roc_proc_text()}}, \code{\link[=roclet_find]{roclet_find()}}, \code{\link[=roxy_block]{roxy_block()}}, \code{\link[=roxy_tag]{roxy_tag()}}, \code{\link[=roxy_tag_rd]{roxy_tag_rd()}}, \code{\link[=tags_list]{tags_list()}} } \concept{extending} roxygen2/man/tags-namespace.Rd0000644000176200001440000000472315173670373016003 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/tag-metadata.R \name{tags-namespace} \alias{tags-namespace} \alias{@evalNamespace} \alias{@export} \alias{@exportClass} \alias{@exportMethod} \alias{@exportPattern} \alias{@exportS3Method} \alias{@import} \alias{@importClassesFrom} \alias{@importFrom} \alias{@importMethodsFrom} \alias{@rawNamespace} \alias{@useDynLib} \title{Tags for managing the \code{NAMESPACE}} \usage{ #' @evalNamespace ${1:r-code} #' @export #' @exportClass ${1:class} #' @exportMethod ${1:generic} #' @exportPattern ${1:pattern} #' @exportS3Method ${1:package}::${2:generic} #' @import ${1:package} #' @importClassesFrom ${1:package} ${2:class} #' @importFrom ${1:package} ${2:function} #' @importMethodsFrom ${1:package} ${2:generic} #' @rawNamespace ${1:namespace directives} #' @useDynLib ${1:package} } \description{ Learn the full details in \code{vignette('namespace')}. Key tags: \itemize{ \item \verb{@export}: Export this function, method, generic, or class so it's available outside of the package. \item \verb{@exportS3Method $\{1:package\}::$\{2:generic\}}: Export an S3 method. Only needed when the method is for a generic from a suggested package. \item \verb{@importFrom $\{1:package\} $\{2:function\}}: Import specific functions from a package. \item \verb{@useDynLib $\{1:package\}}: Import compiled code from another package. } Other less frequently used tags: \itemize{ \item \verb{@evalNamespace $\{1:r-code\}}: Evaluate arbitrary code in the package namespace and insert the results into the \code{NAMESPACE}. Should return a character vector of directives. \item \verb{@exportClass $\{1:class\}}: Export an S4 class. For expert use only; in most cases you should use \verb{@export} so roxygen2 can automatically generate the correct directive. \item \verb{@exportMethod $\{1:generic\}}: Export S4 methods. For expert use only; in most cases you should use \verb{@export} so roxygen2 can automatically generate the correct directive. \item \verb{@exportPattern $\{1:pattern\}}: Export all objects matching a regular expression. \item \verb{@import $\{1:package\}}: Import all functions from a package. Use with extreme care. \item \verb{@importClassesFrom $\{1:package\} $\{2:class\}}: Import S4 classes from another package. \item \verb{@importMethodsFrom $\{1:package\} $\{2:generic\}}: Import S4 methods from a package. \item \verb{@rawNamespace $\{1:namespace directives\}}: Insert literal text directly into the \code{NAMESPACE}. } } roxygen2/man/markdown_evaluate.Rd0000644000176200001440000000306315173670373016617 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/markdown-code.R \name{markdown_evaluate} \alias{markdown_evaluate} \title{Expand embedded inline code} \usage{ markdown_evaluate(text) } \arguments{ \item{text}{Input text.} } \value{ Text with R code expanded. A character vector of the same length as the input \code{text}. } \description{ For example this becomes two: 2. Variables can be set and then reused, within the same tag: The value of \code{x} is 100. We have access to the internal functions of the package, e.g. since this is \emph{roxygen2}, we can refer to the internal \code{markdown} function, and this is \code{TRUE}: TRUE. To insert the name of the current package: roxygen2. The \code{iris} data set has 5 columns: \code{Sepal.Length}, \code{Sepal.Width}, \code{Petal.Length}, \code{Petal.Width}, \code{Species}. \if{html}{\out{
    }}\preformatted{# Code block demo x + 1 #> [1] 101 }\if{html}{\out{
    }} Chunk options: \if{html}{\out{
    }}\preformatted{names(mtcars) nrow(mtcars) #> [1] "mpg" "cyl" "disp" "hp" "drat" "wt" "qsec" "vs" "am" "gear" #> [11] "carb" #> [1] 32 }\if{html}{\out{
    }} Plots: \if{html}{\out{
    }}\preformatted{plot(1:10) }\if{html}{\out{
    }} \figure{test-figure-1.png} Alternative knitr engines: \if{html}{\out{
    }}\preformatted{```\{r\} # comment this <- 10 is <- this + 10 good <- this + is }\if{html}{\out{
    }} Also see \code{vignette("rd-formatting")}. } \keyword{internal} roxygen2/man/object_format.Rd0000644000176200001440000000100015173670373015712 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/object-format.R \name{object_format} \alias{object_format} \title{Default format for data} \usage{ object_format(x) } \arguments{ \item{x}{A data object} } \value{ A \code{character} value with valid \code{Rd} syntax, or \code{NULL}. } \description{ This function is called to generate the default "Format" section for each data object. The default implementation will return the class and dimension information. } \keyword{internal} roxygen2/man/tags-rd-R6.Rd0000644000176200001440000000156115173670373014736 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/tag-metadata.R \name{tags-rd-R6} \alias{tags-rd-R6} \alias{@field} \alias{@R6method} \title{Tags for documenting R6} \usage{ #' @field ${1:name} ${2:description} #' @R6method ${1:Class}$${2:method} } \description{ Learn the full details in \code{vignette('rd-R6')}. Key tags: \itemize{ \item \verb{@field $\{1:name\} $\{2:description\}}: Describe a R6 or refClass field. \item \verb{@R6method $\{1:Class\}$$\{2:method\}}: Document an R6 method that can't be discovered by introspection, such as methods added via \verb{$set()}. } } \seealso{ Other documentation tags: \code{\link{tags-index-crossref}}, \code{\link{tags-rd-S3}}, \code{\link{tags-rd-S4}}, \code{\link{tags-rd-S7}}, \code{\link{tags-rd-datasets}}, \code{\link{tags-rd-functions}}, \code{\link{tags-reuse}} } \concept{documentation tags} roxygen2/man/roxygen2-package.Rd0000644000176200001440000000260015173670373016251 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/roxygen2-package.R \docType{package} \name{roxygen2-package} \alias{roxygen2} \alias{roxygen2-package} \title{roxygen2: In-Line Documentation for R} \description{ \if{html}{\figure{logo.png}{options: style='float: right' alt='logo' width='120'}} Generate your Rd documentation, 'NAMESPACE' file, and collation field using specially formatted comments. Writing documentation in-line with code makes it easier to keep your documentation up-to-date as your requirements change. 'roxygen2' is inspired by the 'Doxygen' system for C++. } \seealso{ Useful links: \itemize{ \item \url{https://roxygen2.r-lib.org/} \item \url{https://github.com/r-lib/roxygen2} \item Report bugs at \url{https://github.com/r-lib/roxygen2/issues} } } \author{ \strong{Maintainer}: Hadley Wickham \email{hadley@posit.co} (\href{https://orcid.org/0000-0003-4757-117X}{ORCID}) [copyright holder] Authors: \itemize{ \item Hadley Wickham \email{hadley@posit.co} (\href{https://orcid.org/0000-0003-4757-117X}{ORCID}) [copyright holder] \item Peter Danenberg \email{pcd@roxygen.org} [copyright holder] \item Gábor Csárdi \email{csardi.gabor@gmail.com} \item Manuel Eugster [copyright holder] } Other contributors: \itemize{ \item Posit Software, PBC (\href{https://ror.org/03wc8by49}{ROR}) [copyright holder, funder] } } \keyword{internal} roxygen2/man/load.Rd0000644000176200001440000000313715173670373014030 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/load.R \name{load} \alias{load} \alias{load_pkgload} \alias{load_installed} \alias{load_source} \title{Load package code} \usage{ load_pkgload(path) load_installed(path) load_source(path) } \arguments{ \item{path}{Path to source package} } \description{ roxygen2 is a dynamic documentation system, which means it works with the objects inside your package, not just the source code used to create them. These functions offer various ways of loading your package to suit various constraints: \itemize{ \item \code{load_pkgload()} uses \code{pkgload::load_all()} to simulate package loading as closely as we know how. It offers high fidelity handling of code that uses S4, but requires that the package be compiled. \item \code{load_source()} simulates package loading by attaching packages listed in \code{Depends} and \code{Imports}, then sources all files in the \verb{R/} directory. This was the default strategy used in roxygen2 6.0.0 and earlier; it's primary advantage is that it does not need compilation. \item \code{load_installed()} uses the installed version of the package. Use this strategy if you have installed a development version of the package already. This is the highest fidelity strategy, but requires work outside of roxygen2. } You can change the default strategy for your function with roxygen2 \code{load} option. Override the default off \code{pkgload} to use the \code{source} or \code{installed} strategies: \if{html}{\out{
    }}\preformatted{Roxygen: list(load = "source") }\if{html}{\out{
    }} } roxygen2/man/tags-rd-S7.Rd0000644000176200001440000000127515173670373014742 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/tag-metadata.R \name{tags-rd-S7} \alias{tags-rd-S7} \alias{@prop} \title{Tags for documenting S7} \usage{ #' @prop ${1:name} ${2:description} } \description{ Learn the full details in \code{vignette('rd-S7')}. Key tags: \itemize{ \item \verb{@prop $\{1:name\} $\{2:description\}}: Describe an S7 class property that is not a constructor parameter. } } \seealso{ Other documentation tags: \code{\link{tags-index-crossref}}, \code{\link{tags-rd-R6}}, \code{\link{tags-rd-S3}}, \code{\link{tags-rd-S4}}, \code{\link{tags-rd-datasets}}, \code{\link{tags-rd-functions}}, \code{\link{tags-reuse}} } \concept{documentation tags} roxygen2/man/tags-rd-S3.Rd0000644000176200001440000000133015173670373014726 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/tag-metadata.R \name{tags-rd-S3} \alias{tags-rd-S3} \alias{@method} \title{Tags for documenting S3} \usage{ #' @method ${1:generic} ${2:class} } \description{ Learn the full details in \code{vignette('rd-S3')}. Key tags: \itemize{ \item \verb{@method $\{1:generic\} $\{2:class\}}: If ambiguous, clarify which generic an S3 method belongs too. Only needed in very rare cases. } } \seealso{ Other documentation tags: \code{\link{tags-index-crossref}}, \code{\link{tags-rd-R6}}, \code{\link{tags-rd-S4}}, \code{\link{tags-rd-S7}}, \code{\link{tags-rd-datasets}}, \code{\link{tags-rd-functions}}, \code{\link{tags-reuse}} } \concept{documentation tags} roxygen2/man/load_options.Rd0000644000176200001440000000537115173670373015605 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/options.R \name{load_options} \alias{load_options} \alias{roxy_meta_get} \title{Load roxygen2 options} \usage{ load_options(base_path = ".") roxy_meta_get(key = NULL, default = NULL) } \arguments{ \item{base_path}{Path to package.} \item{key}{Key of the options, e.g. \code{"packages"}.} \item{default}{Default value.} } \description{ Options can be stored in \code{DESCRIPTION} using \verb{Config/roxygen2/} fields, or in \code{man/roxygen/meta.R}. Call \code{roxy_meta_get()} to access current option values from within tag and roclet methods. Options in \code{man/roxygen/meta.R} override those present in \code{DESCRIPTION}. } \section{Possible options}{ \itemize{ \item \code{roclets} \verb{}: giving names of \link[=roclet]{roclets} to run. See \code{\link[=roclet_find]{roclet_find()}} for details. \item \code{packages} \verb{}: packages to load that implement new tags. \item \code{load} \verb{}: how to load R code. See \link{load} for details. \item \code{old_usage} \verb{}: use old style usage formatting? \item \code{markdown} \verb{}: translate markdown syntax to Rd? \item \code{r6} \verb{}: document R6 classes? \item \code{current_package} \verb{} (read only): name of package being documented. \item \code{rd_family_title} \verb{}: overrides for \verb{@family} titles. See the \emph{rd-functions} vignette for details: \code{vignette("rd-functions")} \item \code{knitr_chunk_options} \verb{}: default chunk options used for knitr. \item \code{restrict_image_formats} \verb{}: if \code{TRUE} then PDF images are only included in the PDF manual, and SVG images are only included in the HTML manual. (This only applies to images supplied via markdown.) } } \section{How to set}{ Either set in \code{DESCRIPTION} using \verb{Config/roxygen2/} fields: \if{html}{\out{
    }}\preformatted{Config/roxygen2/markdown: TRUE Config/roxygen2/load: installed }\if{html}{\out{
    }} Or if you need more complex options (like \code{rd_family_title} or \code{knitr_chunk_options}), put them in \code{man/roxygen/meta.R}: \if{html}{\out{
    }}\preformatted{list( rd_family_title = list(models = "Model functions"), knitr_chunk_options = list(fig.width = 7) ) }\if{html}{\out{
    }} } \seealso{ Other extending: \code{\link[=parse_package]{parse_package()}}, \code{\link[=rd_section]{rd_section()}}, \code{\link[=roc_proc_text]{roc_proc_text()}}, \code{\link[=roclet_find]{roclet_find()}}, \code{\link[=roxy_block]{roxy_block()}}, \code{\link[=roxy_tag]{roxy_tag()}}, \code{\link[=roxy_tag_rd]{roxy_tag_rd()}}, \code{\link{tag_parsers}}, \code{\link[=tags_list]{tags_list()}} } \concept{extending} roxygen2/man/update_collate.Rd0000644000176200001440000000362315173670373016076 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/collate.R \name{update_collate} \alias{update_collate} \alias{@include} \title{Update Collate field in DESCRIPTION} \usage{ update_collate(base_path) } \arguments{ \item{base_path}{Path to package directory.} } \description{ By default, R loads files in alphabetical order. This is fine if your package doesn't have any cross-file dependencies, but if you're using a tool like S4, you'll need to make sure that classes are loaded before subclasses and generics are defined before methods. You can do this by hand by setting the \code{Collate} field in the \code{DESCRIPTION} or automate it by using \verb{@include} tags to specify the cross-file dependencies: \if{html}{\out{
    }}\preformatted{#' @include before.R NULL }\if{html}{\out{
    }} If there are no \verb{@include} tags, roxygen2 will leave the \code{Collate} field as is. This makes it easier to use roxygen2 with an existing collate directive, but if you remove all your \verb{@include} tags, you'll need to also manually delete the collate field. Generally, you should not need to run this function yourself; it will be run automatically by any package that needs to load your R files in collation order. \code{update_collate()} is not not technically a \link{roclet}, like \code{\link[=rd_roclet]{rd_roclet()}} and \code{\link[=namespace_roclet]{namespace_roclet()}}, because you have to be able to load the pacakge before you can process it with roclets. However, because it was historical implemented as a roclet, it's still controlled by the \code{roclets} argument of \code{\link[=roxygenize]{roxygenize()}}. } \examples{ #' If `example-a.R`, `example-b.R` and `example-c.R` live in `R/` #' and we're in `example-a.R`, then the following @include tag #' ensures that example-b and example-c are sourced before example-a. #' @include example-b.R example-c.R NULL } roxygen2/man/roxygenize.Rd0000644000176200001440000000417715173670373015321 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/roxygenize.R \name{roxygenize} \alias{roxygenize} \alias{roxygenise} \title{Document a package with roxygen2} \usage{ roxygenize(package.dir = ".", roclets = NULL, load_code = NULL, clean = FALSE) roxygenise(package.dir = ".", roclets = NULL, load_code = NULL, clean = FALSE) } \arguments{ \item{package.dir}{Location of package top level directory. Default is working directory.} \item{roclets}{Character vector of \link[=roclet]{roclets} to use. The default, \code{NULL}, uses the roxygen \code{roclets} option, which defaults to \code{c("collate", "namespace", "rd")}. This will update (if needed) the \code{Collate} field with \code{\link[=update_collate]{update_collate()}}, produce the \code{NAMESPACE} file with \code{\link[=namespace_roclet]{namespace_roclet()}}, and produce the Rd files with \code{\link[=rd_roclet]{rd_roclet()}}. (Note that \code{update_collate()} is not technically a roclet but is still controlled with this argument for historical reasons.)} \item{load_code}{A function used to load all the R code in the package directory. The default, \code{NULL}, uses the strategy defined by the \code{load} roxygen option, which defaults to \code{\link[=load_pkgload]{load_pkgload()}}. See \link{load} for more details.} \item{clean}{If \code{TRUE}, roxygen will delete all files previously created by roxygen before running each roclet.} } \value{ \code{NULL} } \description{ This is the workhorse function that builds manual pages and metadata for a package. It is powered by \link[=roclet]{roclets}, roxygen2's plugin system for producing different types of output. See the documentation of the individual components (\code{\link[=rd_roclet]{rd_roclet()}}, \code{\link[=namespace_roclet]{namespace_roclet()}}, \code{\link[=update_collate]{update_collate()}}) for more details, or learn how to make your own in \code{vignette("extending")}. } \details{ Note that roxygen2 is a dynamic documentation system: it works by inspecting loaded objects in the package. This means that you must be able to load the package in order to document it: see \link{load} for details. } roxygen2/man/parse_package.Rd0000644000176200001440000000355315173670373015700 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/parse.R \name{parse_package} \alias{parse_package} \alias{parse_file} \alias{parse_text} \alias{env_file} \alias{env_package} \title{Parse a package, file, or inline code} \usage{ parse_package(path = ".", env = env_package(path)) parse_file(file, env = env_file(file), srcref_path = NULL) parse_text(text, env = env_file(file)) env_file(file) env_package(path) } \arguments{ \item{path, file, text}{Either specify a \code{path} to the root directory of a package, an R \code{file}, or a character vector \code{text}.} \item{env}{An environment environment containing the result of evaluating the input code. The defaults will do this for you in a test environment: for real code you'll need to generate the environment yourself. You can also set to \code{NULL} if you only want to get the tokenized code blocks only. This suppresses evaluation of \verb{@eval} tags, and will not find the code object associated with each block.} \item{srcref_path}{Path to be used as source ref.} } \value{ A list of roxy_block objects } \description{ \code{parse_package()}, \code{parse_file()}, and \code{parse_text()} allow you to use roxygen's parsing code to parse the roxygen blocks from a package, file, or character vector of code. \code{env_package()} and \code{env_file()} provide defaults that generate a temporary environment making it possible to associate each block with the corresponding live object. } \seealso{ Other extending: \code{\link[=load_options]{load_options()}}, \code{\link[=rd_section]{rd_section()}}, \code{\link[=roc_proc_text]{roc_proc_text()}}, \code{\link[=roclet_find]{roclet_find()}}, \code{\link[=roxy_block]{roxy_block()}}, \code{\link[=roxy_tag]{roxy_tag()}}, \code{\link[=roxy_tag_rd]{roxy_tag_rd()}}, \code{\link{tag_parsers}}, \code{\link[=tags_list]{tags_list()}} } \concept{extending} roxygen2/man/tags-rd-formatting.Rd0000644000176200001440000000125515173670373016621 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/tag-metadata.R \name{tags-rd-formatting} \alias{tags-rd-formatting} \alias{@md} \alias{@noMd} \alias{@section} \title{Tags related to markdown support} \usage{ #' @md #' @noMd #' @section ${1:section title}: } \description{ Learn the full details in \code{vignette('rd-formatting')}. Other less frequently used tags: \itemize{ \item \verb{@md}: Force markdown processing for a block. \item \verb{@noMd}: Suppress markdown processing for a block. \item \verb{@section $\{1:section title\}: }: Add an arbitrary section to the documentation. Now generally superseded in favour of using a level 1 heading. } } roxygen2/man/markdown-internals.Rd0000644000176200001440000000240415173670373016724 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/markdown-escaping.R \name{escape_rd_for_md} \alias{escape_rd_for_md} \alias{unescape_rd_for_md} \title{Escape fragile Rd tags} \usage{ escape_rd_for_md(text) unescape_rd_for_md(rd_text, esc_text) } \arguments{ \item{text}{Input text. Potentially contains Rd and/or markdown markup.} \item{rd_text}{The markdown parsed and interpreted text.} \item{esc_text}{The original escaped text from \code{escape_rd_for_md()}.} } \value{ \itemize{ \item \code{escape_rd_for_md}: a "safe" version of the input text, where each fragile Rd tag is replaced by a placeholder. The original text is added as an attribute for each placeholder. \item \code{unescape_rd_for_md}: the original Rd text. } } \description{ \code{escape_rd_for_md()} replaces fragile Rd tags with placeholders, to avoid interpreting them as markdown. \code{unescape_rd_for_md()} puts the original text back in place of the placeholders after the markdown parsing is done. The fragile tags are listed in \code{escaped_for_md}. Some Rd macros are treated specially: \itemize{ \item For \code{if}, markdown is only allowed in the second argument. \item For \code{ifelse} markdown is allowed in the second and third arguments. } } \keyword{internal} roxygen2/man/needs_roxygenize.Rd0000644000176200001440000000201415173670373016463 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/roxygenize-needs.R \name{needs_roxygenize} \alias{needs_roxygenize} \title{Check if documentation needs to be updated} \usage{ needs_roxygenize(package.dir = ".", quiet = FALSE) } \arguments{ \item{package.dir}{Location of package top level directory. Default is working directory.} \item{quiet}{If \code{TRUE}, suppresses the message listing out-of-date man pages.} } \value{ A logical value, invisibly. \code{TRUE} if any man pages appear to be out of date; \code{FALSE} otherwise. } \description{ A lightweight check that compares modification times of \code{.Rd} files in \verb{man/} with the source files listed in their backrefs. This is much faster than running \code{\link[=roxygenize]{roxygenize()}} but can suffer from both false negatives (e.g. if an inherited documentation topic has changed) and false positives (e.g. if a source file was modified but the change doesn't affect the documentation). } \examples{ \dontrun{ needs_roxygenize() } } roxygen2/man/roxy_block.Rd0000644000176200001440000000401415173670373015257 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/block.R \name{roxy_block} \alias{roxy_block} \alias{block_has_tags} \alias{block_get_tags} \alias{block_get_tag} \alias{block_get_tag_value} \title{Blocks} \usage{ roxy_block(tags, file, line, call, object = NULL) block_has_tags(block, tags) block_get_tags(block, tags) block_get_tag(block, tag) block_get_tag_value(block, tag) } \arguments{ \item{tags}{A list of \link{roxy_tag}s.} \item{file, line}{Location of the \code{call} (i.e. the line after the last line of the block).} \item{call}{Expression associated with block.} \item{object}{Optionally, the object associated with the block, found by inspecting/evaluating \code{call}.} \item{block}{A \code{roxy_block} to manipulate.} \item{tag}{A single tag name.} } \description{ A \code{roxy_block} represents a single roxygen2 block. The \verb{block_*} functions provide a few helpers for common operations: \itemize{ \item \code{block_has_tags(blocks, tags)}: does \code{block} contain any of these \code{tags}? \item \code{block_get_tags(block, tags)}: get all instances of \code{tags} \item \code{block_get_tag(block, tag)}: get single tag. Returns \code{NULL} if 0, throws warning if more than 1. \item \code{block_get_tag_value(block, tag)}: gets \code{val} field from single tag. } } \examples{ # The easiest way to see the structure of a roxy_block is to create one # using parse_text: text <- " #' This is a title #' #' @param x,y A number #' @export f <- function(x, y) x + y " # parse_text() returns a list of blocks, so I extract the first block <- parse_text(text)[[1]] block } \seealso{ Other extending: \code{\link[=load_options]{load_options()}}, \code{\link[=parse_package]{parse_package()}}, \code{\link[=rd_section]{rd_section()}}, \code{\link[=roc_proc_text]{roc_proc_text()}}, \code{\link[=roclet_find]{roclet_find()}}, \code{\link[=roxy_tag]{roxy_tag()}}, \code{\link[=roxy_tag_rd]{roxy_tag_rd()}}, \code{\link{tag_parsers}}, \code{\link[=tags_list]{tags_list()}} } \concept{extending} roxygen2/man/roxy_tag.Rd0000644000176200001440000000547215173670373014751 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/tag.R, R/utils-warn.R \name{roxy_tag} \alias{roxy_tag} \alias{roxy_tag_parse} \alias{roxy_tag_warning} \alias{warn_roxy_tag} \title{\code{roxy_tag} S3 constructor} \usage{ roxy_tag(tag, raw, val = NULL, file = NA_character_, line = NA_character_) roxy_tag_parse(x) roxy_tag_warning(x, ...) warn_roxy_tag(tag, message, parent = NULL, envir = parent.frame()) } \arguments{ \item{tag}{Tag name. Arguments starting with \code{.} are reserved for internal usage.} \item{raw}{Raw tag value, a string.} \item{val}{Parsed tag value, typically a character vector, but sometimes a list. Usually filled in by \code{tag_parsers}} \item{file, line}{Location of the tag} \item{x}{A tag} \item{...}{Additional data to be stored in the condition object. If you supply condition fields, you should usually provide a \code{class} argument. You may consider prefixing condition fields with the name of your package or organisation to prevent name collisions.} \item{message}{Warning message} \item{parent}{Supply \code{parent} when you rethrow an error from a condition handler (e.g. with \code{\link[rlang:try_fetch]{try_fetch()}}). \itemize{ \item If \code{parent} is a condition object, a \emph{chained error} is created, which is useful when you want to enhance an error with more details, while still retaining the original information. \item If \code{parent} is \code{NA}, it indicates an unchained rethrow, which is useful when you want to take ownership over an error and rethrow it with a custom message that better fits the surrounding context. Technically, supplying \code{NA} lets \code{abort()} know it is called from a condition handler. This helps it create simpler backtraces where the condition handling context is hidden by default. } For more information about error calls, see \ifelse{html}{\link[rlang:topic-error-chaining]{Including contextual information with error chains}}{\link[rlang:topic-error-chaining]{Including contextual information with error chains}}.} \item{envir}{passed to \code{rlang::warn()} as \code{.envir}.} } \description{ \code{roxy_tag()} is the constructor for tag objects. \code{roxy_tag_warning()} is superseded by \code{warn_roxy_tag()}; use to generate a warning that includes the location of the tag. } \section{Methods}{ Define a method for \code{roxy_tag_parse} to support new tags. See \link{tag_parsers} for more details. } \seealso{ Other extending: \code{\link[=load_options]{load_options()}}, \code{\link[=parse_package]{parse_package()}}, \code{\link[=rd_section]{rd_section()}}, \code{\link[=roc_proc_text]{roc_proc_text()}}, \code{\link[=roclet_find]{roclet_find()}}, \code{\link[=roxy_block]{roxy_block()}}, \code{\link[=roxy_tag_rd]{roxy_tag_rd()}}, \code{\link{tag_parsers}}, \code{\link[=tags_list]{tags_list()}} } \concept{extending} roxygen2/man/tags-rd-datasets.Rd0000644000176200001440000000206515173670373016257 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/tag-metadata.R \name{tags-rd-datasets} \alias{tags-rd-datasets} \alias{@format} \alias{@source} \title{Tags for documenting datasets} \usage{ #' @format ${1:description} #' @source ${1:description} } \description{ Learn the full details in \code{vignette('rd-datasets')}. Key tags: \itemize{ \item \verb{@format $\{1:description\}}: Describe the type/shape of a dataset. If the dataset is a data frame, include a description of each column. If not supplied, will be automatically generated by \code{object_format()}. \item \verb{@source $\{1:description\}}: Describe where the dataset came from. Provide a link to the original source (if possible) and briefly describe any manipulation that you performed when importing the data. } } \seealso{ Other documentation tags: \code{\link{tags-index-crossref}}, \code{\link{tags-rd-R6}}, \code{\link{tags-rd-S3}}, \code{\link{tags-rd-S4}}, \code{\link{tags-rd-S7}}, \code{\link{tags-rd-functions}}, \code{\link{tags-reuse}} } \concept{documentation tags} roxygen2/man/tags-index-crossref.Rd0000644000176200001440000000375115173670373017002 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/tag-metadata.R \name{tags-index-crossref} \alias{tags-index-crossref} \alias{@aliases} \alias{@backref} \alias{@concept} \alias{@family} \alias{@keywords} \alias{@references} \alias{@seealso} \title{Tags for indexing and cross-references} \usage{ #' @aliases ${1:alias} #' @backref ${1:path} #' @concept ${1:concept} #' @family ${1:family name} #' @keywords ${1:keyword} #' @references ${1:reference} #' @seealso [${1:func}()] } \description{ Learn the full details in \code{vignette('index-crossref')}. Key tags: \itemize{ \item \verb{@aliases $\{1:alias\}}: Add additional aliases to the topic. Use \code{NULL} to suppress the default alias automatically generated by roxygen2. \item \verb{@concept $\{1:concept\}}: Add additional keywords or phrases to be included in the \code{help.search()} index. Each \verb{@concept} should be a single term or phrase. \item \verb{@family $\{1:family name\}}: Generate \verb{@seealso} entries to all other functions in \verb{family name}. \item \verb{@keywords $\{1:keyword\}}: Add a standardised keyword, indexed by \code{help.search()}. These are generally not useful apart from \verb{@keywords internal} which flags the topic as internal and removes from topic indexes. \item \verb{@references $\{1:reference\}}: Pointers to the related literature. Usually formatted like a bibliography. \item \verb{@seealso [$\{1:func\}()]}: Link to other related functions or urls. Usually a sentence or two, or a bulleted list. } Other less frequently used tags: \itemize{ \item \verb{@backref $\{1:path\}}: Manually override the backreference that points from the \code{.Rd} file back to the source \code{.R} file. Only needed when generating code. } } \seealso{ Other documentation tags: \code{\link{tags-rd-R6}}, \code{\link{tags-rd-S3}}, \code{\link{tags-rd-S4}}, \code{\link{tags-rd-S7}}, \code{\link{tags-rd-datasets}}, \code{\link{tags-rd-functions}}, \code{\link{tags-reuse}} } \concept{documentation tags} roxygen2/DESCRIPTION0000644000176200001440000000374215175154672013560 0ustar liggesusersPackage: roxygen2 Title: In-Line Documentation for R Version: 8.0.0 Authors@R: c( person("Hadley", "Wickham", , "hadley@posit.co", role = c("aut", "cre", "cph"), comment = c(ORCID = "0000-0003-4757-117X")), person("Peter", "Danenberg", , "pcd@roxygen.org", role = c("aut", "cph")), person("Gábor", "Csárdi", , "csardi.gabor@gmail.com", role = "aut"), person("Manuel", "Eugster", role = c("aut", "cph")), person("Posit Software, PBC", role = c("cph", "fnd"), comment = c(ROR = "03wc8by49")) ) Description: Generate your Rd documentation, 'NAMESPACE' file, and collation field using specially formatted comments. Writing documentation in-line with code makes it easier to keep your documentation up-to-date as your requirements change. 'roxygen2' is inspired by the 'Doxygen' system for C++. License: MIT + file LICENSE URL: https://roxygen2.r-lib.org/, https://github.com/r-lib/roxygen2 BugReports: https://github.com/r-lib/roxygen2/issues Depends: R (>= 4.1) Imports: brew, cli (>= 3.3.0), commonmark, desc (>= 1.2.0), knitr, lifecycle, methods, pkgload (>= 1.5.2), R6 (>= 2.1.2), rlang (>= 1.1.0), utils, withr, xml2 Suggests: covr, R.methodsS3, S7, R.oo, rmarkdown (>= 2.16), testthat (>= 3.1.2), yaml LinkingTo: cpp11 VignetteBuilder: knitr Config/Needs/development: testthat Config/Needs/website: tidyverse/tidytemplate Config/roxygen2/load: installed Config/roxygen2/markdown: TRUE Config/roxygen2/version: 7.3.3.9000 Config/testthat/edition: 3 Config/testthat/parallel: TRUE Encoding: UTF-8 Language: en-GB NeedsCompilation: yes Packaged: 2026-04-30 12:51:23 UTC; hadleywickham Author: Hadley Wickham [aut, cre, cph] (ORCID: ), Peter Danenberg [aut, cph], Gábor Csárdi [aut], Manuel Eugster [aut, cph], Posit Software, PBC [cph, fnd] (ROR: ) Maintainer: Hadley Wickham Repository: CRAN Date/Publication: 2026-05-01 16:50:02 UTC