roxygen2/0000755000176200001440000000000014553554653012046 5ustar liggesusersroxygen2/NAMESPACE0000644000176200001440000002264514553531150013261 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_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_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_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_section) S3method(merge,rd_section_minidesc) S3method(merge,rd_section_param) S3method(merge,rd_section_reexport) S3method(merge,rd_section_section) 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,rcclass) S3method(object_defaults,s3generic) S3method(object_defaults,s3method) S3method(object_defaults,s4class) S3method(object_defaults,s4generic) S3method(object_defaults,s4method) 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(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_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_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_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(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) import(stringr) importFrom(R6,R6Class) importFrom(knitr,knit) importFrom(knitr,opts_chunk) importFrom(purrr,keep) importFrom(purrr,map) importFrom(purrr,map2) importFrom(purrr,map_chr) importFrom(purrr,map_int) importFrom(purrr,map_lgl) 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/README.md0000644000176200001440000000607714520721622013321 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: * Start with `vignette("rd")` to learn how document your functions with roxygen2. * `vignette("rd-other")` discusses how to document other things like datasets, the package itself, and the various pieces used by R's 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. * For the `Collate` field in the `DESCRIPTION`, see `?update_collate()`. roxygen2/man/0000755000176200001440000000000014550054451012605 5ustar liggesusersroxygen2/man/load_options.Rd0000644000176200001440000000423614525717717015610 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.} } \description{ Options can be stored in the \code{Roxygen} field of the \code{DESCRIPTION}, or in \code{man/roxygen/meta.R}. In either case, the code is parsed and evaluated in a child of the base environment. 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 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} vignette for details: \code{vignette("rd", package = "roxygen2")} \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}: \if{html}{\out{
}}\preformatted{Roxygen: list(markdown = TRUE, load = "installed") }\if{html}{\out{
}} Or if longer, you can put in \verb{/man/roxygen/meta.R}: \if{html}{\out{
}}\preformatted{list( markdown = TRUE, load = "installed" ) }\if{html}{\out{
}} } \keyword{internal} roxygen2/man/tags_list.Rd0000644000176200001440000000047714264333036015076 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 tags} \usage{ tags_list(built_in = TRUE) tags_metadata() } \description{ Access metadata about built-in tags } \keyword{internal} roxygen2/man/load.Rd0000644000176200001440000000313714525717717014034 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/tag_parsers.Rd0000644000176200001440000000274013545643447015425 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) tag_inherit(x) tag_name(x) tag_two_part(x, first, second, required = TRUE, markdown = TRUE) tag_name_description(x) tag_words(x, min = 0, max = Inf) 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{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}. } \keyword{internal} roxygen2/man/tags-index-crossref.Rd0000644000176200001440000000356514550054451016774 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}}, \code{\link{tags-rd-other}}, \code{\link{tags-reuse}} } \concept{documentation tags} roxygen2/man/RoxyTopic.Rd0000644000176200001440000002053014262717326015043 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{Method \code{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{Method \code{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{Method \code{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{Method \code{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{Method \code{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{Method \code{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{Method \code{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{Method \code{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{Method \code{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{Method \code{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{Method \code{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{Method \code{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/update_collate.Rd0000644000176200001440000000330514525717717016077 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. Unfortunately not every alphabet puts letters in the same order, so you can't rely on alphabetic ordering if you need one file loaded before another. (This usually doesn't matter but is important for S4, where you need to make sure that classes are loaded before subclasses and generics are defined before methods.). You can override the default alphabetical ordering with \verb{@include before.R}, which specify that \code{before.R} must be loaded before the current file. Generally, you will not need to run this function yourself; it should be run automatically by any package that needs to load your R files in collation order. } \section{Collate}{ This is not a roclet because roclets need the values of objects in a package, and those values can not be generated unless you've sourced the files, and you can't source the files unless you know the correct order. If there are no \verb{@include} tags, roxygen2 will leave collate 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. } \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 statement #' ensures that example-b and example-c are sourced before example-a. #' @include example-b.R example-c.R NULL } roxygen2/man/rd_roclet.Rd0000644000176200001440000000171114525717717015066 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 roclet is the workhorse of roxygen2, producing the \code{.Rd} files that R uses to document functions, datasets, packages, classes, and more. See \code{vignette("rd")} for details. Generally you will not call this function directly but will instead use \code{\link[=roxygenise]{roxygenise()}} specifying the rd roclet. } \examples{ #' The length of a string (in characters) #' #' @param x A character vector. #' @returns An integer vector the same length as `x`. #' `NA` strings have `NA` length. #' @seealso [nchar()] #' @export #' @examples #' str_length(letters) #' str_length(c("i", "like", "programming", NA)) str_length <- function(x) { } } \seealso{ \link{tags-rd}, \link{tags-rd-other}, \link{tags-reuse}, \link{tags-index-crossref} for tags provided by this roclet. } roxygen2/man/namespace_roclet.Rd0000644000176200001440000000217514525717717016422 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/namespace.R \name{namespace_roclet} \alias{namespace_roclet} \title{Roclet: make \code{NAMESPACE}} \usage{ namespace_roclet() } \description{ This roclet automates the production of a \code{NAMESPACE} file, which controls the functions imported and exported by your package, as described in \href{https://cran.r-project.org/doc/manuals/r-release/R-exts.html}{Writing R extensions}. The \code{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 \code{vignette("namespace")} for details. } \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) { } # You'll also often find global imports living in a file called # R/{package}-package.R. #' @importFrom magrittr \%>\% #' @import rlang NULL } \seealso{ \link{tags-namespace} for tags that generate \code{NAMESPACE} directives. } roxygen2/man/tags-rd-formatting.Rd0000644000176200001440000000125514525717717016625 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/roxy_block.Rd0000644000176200001440000000317514525717717015272 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_tag(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 } \keyword{internal} roxygen2/man/roxygenize.Rd0000644000176200001440000000323614264614532015307 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/roxygenize.R \name{roxygenize} \alias{roxygenize} \alias{roxygenise} \title{Process a package with the Rd, namespace and collate roclets} \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 roclet names to use with package. The default, \code{NULL}, uses the roxygen \code{roclets} option, which defaults to \code{c("collate", "namespace", "rd")}.} \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 uses roclets, the built-in document transformation functions, to build all documentation for a package. See the documentation for the individual roclets, \code{\link[=rd_roclet]{rd_roclet()}}, \code{\link[=namespace_roclet]{namespace_roclet()}}, and for \code{\link[=update_collate]{update_collate()}}, for more details. } \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/roxygen2-package.Rd0000644000176200001440000000234014527236502016244 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 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 [copyright holder, funder] } } \keyword{internal} roxygen2/man/escape_examples.Rd0000644000176200001440000000213714264537272016246 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/tags-reuse.Rd0000644000176200001440000000627614550054451015166 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} #' @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. \item \verb{@inheritParams $\{1:source\}}: Inherit argument documentation from another function. Only inherits documentation for arguments that aren't already documented locally. \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}}, \code{\link{tags-rd-other}} } \concept{documentation tags} roxygen2/man/tags-namespace.Rd0000644000176200001440000000472314525717717016007 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/roclet_find.Rd0000644000176200001440000000142314264614456015375 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()") } \keyword{internal} roxygen2/man/figures/0000755000176200001440000000000014553540041014247 5ustar liggesusersroxygen2/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.png0000644000176200001440000003260414553540041017356 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}?/KX4(h#j#EL]5Qk V瑸g5Z&HTV^"h(9_=sΜ~{?sj͟0 @h]  x/ PB_A%  @@ | U @ @%%T]"@kP@pPux PB_A%  @@ | U @ @%%T]"@kP@pPux PB_A%  @@ | U @ @%%T]"@kP@pPux PB_A%  @@ | U @ @%%T]"@kP@pPux PB_A%  @@ | U @ @%%T]"@kP@pPux PB_A%  @@ | U @ @%%T]"@kP@pPux PB_A%  @@ | U @ @%%T]"@kP@pPumIsOZ%uY_  @ G>҂Vk}Ԝ'GC 38*$@)0{Xzu.w >v @l|J'@\UJ*" saW)VphѢl{t PrB+bѳgh׮]lѩS{cĉ%#@/P5*ϟSN]w5 K /sN,[,FW"(@!|b}Ν;GVK.q5\'O.)n @@~?|YmT36pʔ)ѭ[,$@Y q7/GqD\wuYUI8E'cǎ^zV[mK,_|10ӦMˤ %@n+fK.<8NTۥp }z*y䑘;wnz=>9jO@4ӫnO;gyf @~8.†b-"_,Y.ײlړ:!iVp@z^cƌ?* @@@ ,޽{]>o޼4.(B7;Ok׮|5 @`=N;-ׄ b=HoN>Օ\>+W~YV @HUW]Æ ;g#z uġC_]늘'@^sϺ/D_{݊W믿>*B|>NnK>h1:J(|ҹn\ Т sߢud, 3V<C@确N X @ :  @@>c` @<|$@ Oyd, 3V<C@确N X @ :  @@>c` @<|$@ Oyd, 3V<C@确N X @ :  @@>c` @<|$@ Oyd, 3V<C@确N X P)|3gNە+5@f-Ma @`I?O?{l\zê ُ%xիW hnݺ7tPp 1Fe قI`=7|3^{ԩS.Tb늘'@L:vIЛp >[_ @\|.*%@ l}Nr𹰫d+ U:E@®R W @  J  @@>[_ @\|.*%@ l}Nr𹰫d+ U:E@®R W @  J  @@>[_ @\|.*%@ l}Nr𹰫d+ U:E@®R W @  J  @@>[_ @\|.*%@ l}Nr𹰫d+ U:E@®R @lozW>(:wBI"p7~7h1\ ,/]4x  @ /z(Əw}wm6~i8xW#>;VZ8쩧ѡZhQ,^vK,+WV]g!U wwēO>n\veqI'K/{׮if "={ԩS۱p8ӮG?JC~S7ML6*ɣ>Y$|c=ֆ{Cߒw~6hB>}zr!.>㵣F~o 'OsύW[eVk׮[ĝww~$4&LK.n/P?c1gΜ$f͊3gn~O@ $ۓ˜=z_|1r˘4iRzVt*auiS ^x!{n"9'Fǎ7?#@@iЊ+sIŽ p!>iVVpOo>9䩉6A 騣ڄ-mR 1 @VV!@55Vz @@ | UEj% k% PC_ClU @Z ZIP@[U @VV!@55Vz @@ | UEj% k% PC_ClU @Z ZIP@[U @VV!@55Vz @@ | UEj% k% PC_ClU @Z ZIP@[U @VV!@55Vz @@ | UEj% k% PC5KU+wy'~ŋcw3<3Zv\TW؂ڂ_ l?8oƎ;C Yf+W읬!P _#peYg~z7n8C;,N#p"@ ,\0W0`X`A23* :2E@=zӧWaѳgϊefUMvE"@ WQF.oV~q]wŊ+O̵]*'7Uv(.]v_`7mڴe-O P]vq9dX d'>;[% @|n*&@ lLrѫd' ೳU2M@Fb V @ 7  @@v>;[% @|n*&@ lLrѫd' ೳU2M@Fb V @ 7  @@v>;[% @|n*&@ lLrѫd' ೳU2M@Fb V @ 7  @@v>;[% @|n*&@ lLrѫd' ೳU2M@Fb @V2 <OFNO}s7& ~ٲer&w̎S`„ ( G}4zK,)fc@ "_2dH<;ic뭷ÇNJ+X hxg /{7 \sM|ߏ/a4@!N?w9k+bժUs?a\r%|뭷8maѣGU P#gy&ƌ;w^[#NX; #P~/Rk.リhѢM-v @@A [;\L>ۦ{ĉ l @6$MvgRϟSN]w5 j]sN$?r i zG>`p >_'ݞ|VL7:9 Pl>9?cƌNU[nUYHG`S_|q|1vHfJ/Olq% @ } Gs㓣#9eo"@-^'M1!Rk ]- @F`#:V\ݻwwq  @ >~uСCDwѯ+bO`kcOM @@ T?f̘H>i1 '&;wn  PUK3TtkOW,3CO3oW;LoXh(@SI+]w袋]d(gDlD( @zXoV P]`Mvա,%@$^&z>m%@^Mvձ,%@"|pdW/ç @@#ZJԋN , @zz ^EO>>VZ{wk/+=#@ 7:\F?0>رc$A?qĘ7o^le> ~lB . >m7H胧}2p >#X… cw\ICm6^zZ5C=L@l@4@cڶmmڴ5kT{ED@-sD[ou?.䒵!}/?ƗTWhom T`kŠAbYUVmf wY +@ Sɝ&48oHJ$ K4B|G P"_ $< @   @ H@h0u4  @DD+ @A@7Hx$@%%L]!@ A#(/` h   @@|SW @@oHJ$ K4B|G P"_ $< @   @ H@h0u4  @DD+ @A@7Hx$@%%L]!@ A#(/` h   @@ ˖-KZWQۿ>:oxWL@ (lO4);8$\>Ǝ7xcL2%&L#F7|(M|@\S{,\+VĪU" ;.&NXܹs7ި*?ʕ+@5$/ѣG:92dH{qYgU2FSNC8'OGy$ƌ:u$_~9z衪۾ѥK,$PM࣏>-ܲbV[mn23(@!~㏧GEin!v;w]ve;H~M~̟?*T8DuVZŋ㏏?UE(D' ɑ-wuW 808hӦM $g7ɩc9&z꩸{-LBw G|޷׾#G}ݷaG7.}xSOm\R|Ү䦦_W@!gBC# ~L1- P) += @RR N @J_aB@buT 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!жЉXbE\{cE⢋.vۭ(#Pڷf͚4hPqWȑ#SOMþd]* soyO:5vq7n\կ~5;vlcd( 3UK,C=bXxq23 y~H޽{<˖-[)S[nk=!@p*_|_ݻ/~7o^̜93FbSؘ# Yg}v$GNt5r- @% 8oɣc:H~L #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 U @ W+  @@6m)JObժUѾ}+%1uXbEzѧOT @`c87bȐ!ѹs8W^Y;ﮝO<#Fݻ{} PzB;6 (:t[;hq^re/7bŊ8T9sbN;5i٩ Ӿ?ũv 3guվSN9%ƌjDc7￷΍d @z2RI! eS P/^FJ;  @@#|#lJE@Hi'h56M>[|K.ܺi[oݻwCCyvmM6ܺhl^|6 @\ϕ_ @ R  @@>W~ @l|6J%@ \UNٸ** sW9F@gTcUVŚ5k/O^?b}I.g#ߺ9e>2dH_U/nv4 p饗q㚡Ė[ok̙3g-¢Ev#/O&|'1a„4ԓ ͞=;uք `h߾}-"|͆7Q`Ŋ1qĸ{b뭷kڴimG㡇ئh3h#dNqqG645w}z7Ѱf$>;ucǎ]Do(Ѯ7aC 7Ѯ]{99|5۰" U'_iBےkrJz-OlB viHN'2 ZfrԞL.#]xᅑ9.Xk;0_sK… 7Hި&yEF- N:)wwuW#igMm&h]MON'gD)=:ӛ~lz!\Oફ#8"{qWW_}zY4䞑bK_HhHh6,Y)VԡCKQ7.&{r# MbEig={ѺuO$lQ}=hmMޓ Azkìfm"9} GϞ=7lwwoJ?~|zy̔կҀO>ܥ}&ϓw $o<=hJ~GlZJwߝ>O%ϓe8g?Yw5l=l 2(՟?_ߟOOM6SN"ϛ([ӧO{M,n fyRGr8V{l@r .gy&.hO&#<zJ H>=G6m◿ez if/_g @rRr3cijs%t)zX5T kd# qU*U@ʯr U @ W+  @@6>W @\|*'@l\Jrd# qU*U@ʯr U @ W+  @@6>W @\|*'@l\Jrd# qU*U@ʯrxǢk׮tԨQqW4z?; ^ @ 3 [bСq9vm}dYtGc c…o}+zw}c֬Y &qk֬I.~_j+ k6XlYۿ[l3GN_"f̘cĈ1eʔ>ӢSNYgÆ K}M ~q뭷e2d~ P[NmI /8Zj'|rI8ScJً-J_nݺg7 =\1~>|x\2h߾}-ZoQí*va4ܓ;wUV$޿_Mϛ7/=ߧO'3gL̎;ƁXj+}mFPɑ{ivٳg]oϷfh۶m$At筷Z{;s΍义|~j&PX>8&M˗/ o۴={/| qM7)^zœO>~qquŸq2|J?Oѯ_dA֭kɺ4|r~~Fid|Z#-|W+XdIz|rZ~)9|ig_w{V@[m @&NׄY% @j#@55aV  @@M|MUBj+ k6 P_f @ zD@ׄY% @j#@55aV  @@M|MUBj+ k6 P_f @ zD5_tIENDB`roxygen2/man/markdown-internals.Rd0000644000176200001440000000322714525717717016734 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 Rd markup, to avoid interpreting it as markdown} \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{ For \code{escape_rd_for_md}: A \dQuote{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. For \code{unescape_rd_for_md}: Rd text. } \description{ This is needed, if we want to stay compatible with existing markup, even if markdown mode is switched on. Fragile Rd tags (tags that may contain markup that can be picked up by the markdown parser), are replaced by placeholders. After the markdown to Rd conversion is done, the original text is put back in place of the placeholders. It puts back the protected fragile Rd commands into the text after the markdown parsing. } \details{ The list of protected Rd tags is 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. } See also \code{roclet-rd.R} for the list of tags that uses the markdown-enabled parser. Some tags, e.g. \verb{@aliases}, \verb{@backref}, etc. only use the standard Roxygen parser. } \keyword{internal} roxygen2/man/roc_proc_text.Rd0000644000176200001440000000063714527377516015772 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. } \keyword{internal} roxygen2/man/tags-rd.Rd0000644000176200001440000000563014525717717014456 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/tag-metadata.R \name{tags-rd} \alias{tags-rd} \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')}. 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-other}}, \code{\link{tags-reuse}} } \concept{documentation tags} roxygen2/man/is_s3_generic.Rd0000644000176200001440000000141314525717717015624 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/markdown_pass1.Rd0000644000176200001440000000312514525717717016043 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/vignette_roclet.Rd0000644000176200001440000000165514525717717016315 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/roxy_tag.Rd0000644000176200001440000000212414527172500014727 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} } \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. } \keyword{internal} roxygen2/man/double_escape_md.Rd0000644000176200001440000000127414525717717016367 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/markdown-escaping.R \name{double_escape_md} \alias{double_escape_md} \title{Check markdown escaping} \usage{ double_escape_md(text) } \arguments{ \item{text}{Input text.} } \value{ Double-escaped text. } \description{ This is a regression test for Markdown escaping. } \details{ Each of the following bullets should look the same when rendered: \itemize{ \item Backticks: \verb{\\}, \verb{\\\%}, \verb{\\$}, \verb{\\_} \item \verb{\verb{}}: \verb{\\}, \verb{\\\%}, \verb{\$}, \verb{\_} } [ this isn't a link ] \[ neither is this \] } \examples{ "\%" # percent "\"" # double quote '\'' # single quote } \keyword{internal} roxygen2/man/object.Rd0000644000176200001440000000101513541216367014344 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.} } \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/object_format.Rd0000644000176200001440000000100014264614456015712 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/markdown-test.Rd0000644000176200001440000000156214525717717015714 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]{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-other.Rd0000644000176200001440000000276714525717717015605 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/tag-metadata.R \name{tags-rd-other} \alias{tags-rd-other} \alias{@field} \alias{@format} \alias{@method} \alias{@slot} \alias{@source} \title{Tags for documenting datasets and classes} \usage{ #' @field ${1:name} ${2:description} #' @format ${1:description} #' @method ${1:generic} ${2:class} #' @slot ${1:name} ${2:description} #' @source ${1:description} } \description{ Learn the full details in \code{vignette('rd-other')}. Key tags: \itemize{ \item \verb{@field $\{1:name\} $\{2:description\}}: Describe a R6 or refClass field. \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{@method $\{1:generic\} $\{2:class\}}: Force a function to be recognised as an S3 method. This affects the default usage and the \code{NAMESPACE} directive produced by \verb{@export}. Only needed if automatic detection fails. \item \verb{@slot $\{1:name\} $\{2:description\}}: Describe the slot of an S4 class. \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}}, \code{\link{tags-reuse}} } \concept{documentation tags} roxygen2/man/rd_section.Rd0000644000176200001440000000212113544473137015231 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/field.R \name{rd_section} \alias{rd_section} \title{Construct an \code{rd_section} object} \usage{ rd_section(type, value) } \arguments{ \item{type}{Section type. Stored in \code{type} field, and in class \verb{rd_section_\{type\}}. To avoid namespace clashes between different extensions, this should include the package name.} \item{value}{Section data. Only used by \code{format()} and \code{merge()} methods.} } \description{ An \code{rd_section} represents an Rd command that can appear at the top-level of an Rd document, like \verb{\\name\{\}}, \verb{\\title\{\}}, \verb{\\description\{\}}, or \verb{\\section\{\}}. } \section{Methods}{ If provide your own \code{rd_section} type, you'll also need to define a \verb{format.rd_section_\{type\}} method that returns formatted Rd output. You may also need to provide a \verb{merge.rd_section_\{type\}} method if two sections can not be combined with \code{rd_section(x$type, c(x$value, y$value))}. See \code{vignette("extending")} for more details. } \keyword{internal} roxygen2/man/roxy_tag_rd.Rd0000644000176200001440000000110013544731730015411 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. } \keyword{internal} roxygen2/man/parse_package.Rd0000644000176200001440000000265714525717717015710 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.} } \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. } \keyword{internal} roxygen2/man/roclet.Rd0000644000176200001440000000326514525717717014407 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{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{ To create a new roclet, you will need to create a constructor function that wraps \code{roclet}, and then implement the methods described below. } \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 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. } } \keyword{internal} roxygen2/DESCRIPTION0000644000176200001440000000355214553554653013561 0ustar liggesusersPackage: roxygen2 Title: In-Line Documentation for R Version: 7.3.1 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")) ) 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 (>= 3.6) Imports: brew, cli (>= 3.3.0), commonmark, desc (>= 1.2.0), knitr, methods, pkgload (>= 1.0.2), purrr (>= 1.0.0), R6 (>= 2.1.2), rlang (>= 1.0.6), stringi, stringr (>= 1.0.0), utils, withr, xml2 Suggests: covr, R.methodsS3, R.oo, rmarkdown (>= 2.16), testthat (>= 3.1.2), yaml LinkingTo: cpp11 VignetteBuilder: knitr Config/Needs/development: testthat Config/Needs/website: tidyverse/tidytemplate Config/testthat/edition: 3 Config/testthat/parallel: TRUE Encoding: UTF-8 Language: en-GB RoxygenNote: 7.3.0.9000 NeedsCompilation: yes Packaged: 2024-01-22 19:47:59 UTC; hadleywickham Author: Hadley Wickham [aut, cre, cph] (), Peter Danenberg [aut, cph], Gábor Csárdi [aut], Manuel Eugster [aut, cph], Posit Software, PBC [cph, fnd] Maintainer: Hadley Wickham Repository: CRAN Date/Publication: 2024-01-22 21:10:03 UTC roxygen2/build/0000755000176200001440000000000014553543157013142 5ustar liggesusersroxygen2/build/vignette.rds0000644000176200001440000000066614553543157015511 0ustar liggesusersRO0D@`Ɏzb!1ă׺Kg{іnW=[<  rjї^Wlxc 2a3JA0A9% Rr$DSIP`mˠP;B7 {<QiIV"8|q`@Zt=C-( İZ3tMb^Մ&eDWY{%IDF%2QE`-B5W?@LיX١ZKFP/ 0˒9ao_)S {>q:LCf]:o`;K#+kxUVl{JVmOZQl̦ 56^&Sm'P6my22"9/9v念dqdFS&Yviroxygen2/tests/0000755000176200001440000000000014553543144013201 5ustar liggesusersroxygen2/tests/testthat/0000755000176200001440000000000014553554653015050 5ustar liggesusersroxygen2/tests/testthat/test-rd-template.R0000644000176200001440000000236514531364526020366 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/roxygen-block-3-B.Rd0000644000176200001440000000465314552504103020431 0ustar liggesusers> res[[n]] % Generated by roxygen2: do not edit by hand % Please edit documentation in ./roxygen-block-3.R \name{B} \alias{B} \title{Class B} \description{ Class B Description. } \details{ Class B details. } \section{Super class}{ \code{roxygen2::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
}} \if{html}{\out{
}} \if{html}{\out{}} \if{latex}{\out{\hypertarget{method-B-meth1}{}}} \subsection{Method \code{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{Method \code{meth4()}}{ A 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{Method \code{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{
}} } } } roxygen2/tests/testthat/Rd-example-3.R0000644000176200001440000000002013523072405017303 0ustar liggesusersx1 <- 1 x2 <- 2 roxygen2/tests/testthat/test-object-format.R0000644000176200001440000000054014262717326020676 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)) }) }) roxygen2/tests/testthat/broken-namespace/0000755000176200001440000000000014553543157020257 5ustar liggesusersroxygen2/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/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/test-object-s3.R0000644000176200001440000000251314536371735017741 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("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-markdown.R0000644000176200001440000003617114527136371017774 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%`"), "\\verb{5\\%}") expect_equal(markdown("`'5%'`"), "\\code{'5\\%'}") expect_equal(markdown("`%*%`"), "\\code{\\%*\\%}") }) test_that("inline verbatim escapes Rd special chars", { expect_equal(markdown("`{`"), "\\verb{\\{}") expect_equal(markdown("`}`"), "\\verb{\\}}") expect_equal(markdown("`\\`"), "\\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"), "\\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 <- "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%"), "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("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"), "\\subsection{This is good}{\n\nyes\n}" ) }) 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"), "\\subsection{Heading 2}{\n\\subsection{Heading 3}{\n\nText.\n}\n\n}" ) }) 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"), "Even this\n\\subsection{Can have a subsection.}{\n\nYes.\n}" ) }) 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("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("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/collate/0000755000176200001440000000000014553543144016464 5ustar liggesusersroxygen2/tests/testthat/collate/undershorts.R0000644000176200001440000000000513523072405021153 0ustar liggesusersNULL roxygen2/tests/testthat/collate/tie.R0000644000176200001440000000003113523072405017353 0ustar liggesusers#' @include shirt.R NULL roxygen2/tests/testthat/collate/jacket.R0000644000176200001440000000005213523072405020036 0ustar liggesusers#' @include tie.R #' @include belt.R NULL roxygen2/tests/testthat/collate/watch.R0000644000176200001440000000001513523072405017702 0ustar liggesusersNULL x <- 2 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/shoes.R0000644000176200001440000000010713523072405017717 0ustar liggesusers#' @include socks.R #' @include undershorts.R #' @include pants.R NULL roxygen2/tests/testthat/collate/shirt.R0000644000176200001440000000000513523072405017724 0ustar liggesusersNULL roxygen2/tests/testthat/collate/pants.R0000644000176200001440000000003713523072405017725 0ustar liggesusers#' @include undershorts.R NULL roxygen2/tests/testthat/collate/socks.R0000644000176200001440000000000513523072405017715 0ustar liggesusersNULL 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/example.Rmd0000644000176200001440000000007614262717326017145 0ustar liggesusers```{r} # comment this <- 10 is <- this + 10 good <- this + is roxygen2/tests/testthat/test-rd-usage.R0000644000176200001440000002236014535665174017663 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"), "\\usage{\nf(a = 1, b = 2, c = a + b)\n}") }) # 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"), "\\usage{\n\\method{mean}{foo}(x)\n}") }) 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"), "\\usage{\na(a = \"\\%\\\\\\\\\")\n}") }) 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"), '\\usage{\n%\\\n}') }) 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) {}), "`\\%foo\\%bar`(x, table)" ) expect_equal( call_to_usage(`%foo%bar<-` <- function(x, value) {}), "`\\%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) {}), "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) {}), "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) {}), "\\method{mean}{foo}(x)" ) expect_equal( call_to_usage(mean.function <- function(x) {}), "\\method{mean}{`function`}(x)" ) expect_equal( call_to_usage("+.foo" <- function(x, b) {}), "\\method{+}{foo}(x, b)" ) expect_equal( call_to_usage("%%.foo" <- function(x, b) {}), "\\method{\\%\\%}{foo}(x, b)" ) expect_equal( call_to_usage("[<-.foo" <- function(x, value) {}), "\\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) {}) }), "\\S4method{sum}{Foo}(x, ..., na.rm = FALSE)" ) expect_equal( call_to_usage({ setClass("Foo") setMethod("+", "Foo", function(e1, e2) "foo") }), "\\S4method{+}{Foo,ANY}(e1, e2)" ) expect_equal( call_to_usage({ setClass("Foo") setMethod("[<-", "Foo", function(x, i, j, ..., value) "foo") }), "\\S4method{[}{Foo}(x, i, j, ...) <- value" ) expect_equal( call_to_usage({ setGeneric("%&&%", function(x, y) standardGeneric("%&&%")) setMethod("%&&%", signature("logical", "logical"), function(x, y) {}) }), "\\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 }) }), "\\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]]) }), "\\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("preserves non-breaking-space", { expect_equal( call_to_usage(f <- function(a = "\u{A0}") {}), 'f(a = "\u{A0}")' ) }) 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/testCollateParse/0000755000176200001440000000000014520730024020304 5ustar liggesusersroxygen2/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/testCollateParse/R/0000755000176200001440000000000014520730024020505 5ustar liggesusersroxygen2/tests/testthat/testCollateParse/R/b.R0000644000176200001440000000002514520730024021046 0ustar liggesusers b <- function() { } roxygen2/tests/testthat/testCollateParse/R/c.R0000644000176200001440000000002514520730024021047 0ustar liggesusers c <- function() { } roxygen2/tests/testthat/testRawNamespace/0000755000176200001440000000000014553543157020313 5ustar liggesusersroxygen2/tests/testthat/testRawNamespace/NAMESPACE0000644000176200001440000000021314553532300021512 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/DESCRIPTION0000644000176200001440000000042214553531033022005 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.0.9000 roxygen2/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/test-rd-section.R0000644000176200001440000000214314527136371020211 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-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/man-roxygen/0000755000176200001440000000000013540202201017262 5ustar liggesusersroxygen2/tests/testthat/man-roxygen/values.R0000644000176200001440000000005013523072405020713 0ustar liggesusers#' <%= x %> #' @param <%= y %> <%= z %> roxygen2/tests/testthat/test-rd-family.R0000644000176200001440000000613114550054451020021 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(str_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_equal(str_count(seealso, fixed("\\code{\\link")), 1) }) 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}()}") }) 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 be set", { local_roxy_meta_set("rd_family_title", list(a = "Custom 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 prefix:") }) 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/templates/0000755000176200001440000000000014553543144017037 5ustar liggesusersroxygen2/tests/testthat/templates/man-roxygen/0000755000176200001440000000000014553543144021303 5ustar liggesusersroxygen2/tests/testthat/templates/man-roxygen/UCase.R0000644000176200001440000000000013540202201022371 0ustar liggesusersroxygen2/tests/testthat/templates/man-roxygen/lcase.r0000644000176200001440000000000014520713066022537 0ustar liggesusersroxygen2/tests/testthat/templates/man/0000755000176200001440000000000014553554653017621 5ustar liggesusersroxygen2/tests/testthat/templates/man/roxygen/0000755000176200001440000000000014553543144021305 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/test-object-import.R0000644000176200001440000000237714527224165020730 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", "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"), c("comparison-expectations", "comparison-expectations") ) ) }) 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")) }) roxygen2/tests/testthat/test-namespace.R0000644000176200001440000003263414553537636020116 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)" )) }) # @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("@exportS3methd 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 parsed correctly", { out <- roc_proc_text(namespace_roclet(), " #' @importFrom test test1 #' test2 NULL ") expect_equal(sort(out), sort(c( "importFrom(test,test1)", "importFrom(test,test2)" ))) }) 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 <- test_path("testRawNamespace") NAMESPACE <- file.path(test_pkg, "NAMESPACE") lines_orig <- read_lines(NAMESPACE) expect_no_error(roxygenize(test_pkg, namespace_roclet())) # 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/roxygen-block-3-C.Rd0000644000176200001440000000534314552504103020427 0ustar liggesusers> res[[n]] % Generated by roxygen2: do not edit by hand % Please edit documentation in ./roxygen-block-3.R \name{C} \alias{C} \title{Class C} \description{ Class C Description. } \details{ Classs C details. } \section{Super classes}{ \code{roxygen2::A} -> \code{roxygen2::B} -> \code{C} } \section{Public fields}{ \if{html}{\out{
}} \describe{ \item{\code{nosuchfield}}{This will warn.} \item{\code{field5}}{C field 5.} \item{\code{duplicatefield}}{Multiple.} \item{\code{duplicatefield}}{times.} } \if{html}{\out{
}} } \section{Active bindings}{ \if{html}{\out{
}} \describe{ \item{\code{nosuchfield}}{This will warn.} \item{\code{active2}}{C binding 2.} \item{\code{active4}}{C binding 4.} \item{\code{active6}}{C binding 6.} \item{\code{duplicate_binding}}{Double.} \item{\code{duplicate_binding}}{Double double.} } \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()}} \item \href{#method-C-undocumented_method}{\code{C$undocumented_method()}} } } \if{html}{\out{
Inherited methods
}} \if{html}{\out{
}} \if{html}{\out{}} \if{latex}{\out{\hypertarget{method-C-meth2}{}}} \subsection{Method \code{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{Method \code{meth5()}}{ C method 5. \subsection{Usage}{ \if{html}{\out{
}}\preformatted{C$meth5()}\if{html}{\out{
}} } } \if{html}{\out{
}} \if{html}{\out{}} \if{latex}{\out{\hypertarget{method-C-undocumented_method}{}}} \subsection{Method \code{undocumented_method()}}{ \subsection{Usage}{ \if{html}{\out{
}}\preformatted{C$undocumented_method()}\if{html}{\out{
}} } } } roxygen2/tests/testthat/roxygen-block-3.R0000644000176200001440000000534513560326573020120 0ustar liggesusers #' Class A #' #' @description #' Class A description. #' #' @details #' Class A details #' #' @param fogbehindme This is not show up in the output at all. #' @param Z zzzzzzz 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. #' @examples #' ## Example for meth2 meth2 = function(Z = 10, ...) { }, #' @field field2 A field 2. field2 = "foobar", #' @details Method 3 details. #' @param duplicate This one is #' @param duplicate Twice #' @return twice. #' @return really? meth3 = function(duplicate, missing) { }, #' @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 2. 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 A 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 #' Classs C details. #' #' @field nosuchfield This will warn. C <- R6::R6Class( "C", inherit = B, cloneable = FALSE, public = list( 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() { }, undocumented_field = NULL, undocumented_method = function() { }, #' @field duplicatefield Multiple. #' @field duplicatefield times. duplicatefield = NULL ), 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) { }, undocumented_binding = function(x) { }, #' @field duplicate_binding Double. #' @field duplicate_binding Double double. duplicate_binding = function(x) { } ) ) roxygen2/tests/testthat/test-rd-name-alias.R0000644000176200001440000000735613542227742020566 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"), fixed("a"), all = FALSE) expect_match(out$get_value("alias"), fixed("b"), all = FALSE) }) 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/Rd-example-4.txt0000644000176200001440000000002214220671366017732 0ustar liggesusers\dontrun{ 1 + 1 } roxygen2/tests/testthat/test-rd-params.R0000644000176200001440000000246214527226566020042 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("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("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/no-desc/0000755000176200001440000000000013523072405016362 5ustar liggesusersroxygen2/tests/testthat/no-desc/NAMESPACE0000644000176200001440000000003713523072405017601 0ustar liggesusersexportPattern("^[[:alpha:]]+") roxygen2/tests/testthat/no-desc/R/0000755000176200001440000000000013523072405016563 5ustar liggesusersroxygen2/tests/testthat/no-desc/R/no-description.R0000644000176200001440000000000513523072405021636 0ustar liggesusersNULL roxygen2/tests/testthat/test-parse.R0000644000176200001440000000055614116121427017250 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-load.R0000644000176200001440000000240114262717326017057 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_error(find_load_strategy("blahblahb"), "Unknown value") }) test_that("NULL uses option", { expect_equal(find_load_strategy(NULL, "installed"), load_installed) }) test_that("informative errors for bad inputs", { expect_error(find_load_strategy(1), "string or function") expect_error(find_load_strategy(NULL, list()), "string") }) roxygen2/tests/testthat/test-block.R0000644000176200001440000001414714527136371017243 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-tag-parser.R0000644000176200001440000000463014531364526020212 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_words_line(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_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_line() gives useful warnings", { expect_snapshot({ tag <- roxy_test_tag("a\nb") expect_parse_failure(tag_words_line(tag)) tag <- roxy_test_tag("a\nb\n2") expect_parse_failure(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-markdown.R0000644000176200001440000000313514527136371020371 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-rd-include-rmd.R0000644000176200001440000002022114531364526020745 0ustar liggesusersskip_if_not_installed("rmarkdown") 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) gsub(path, "", x, fixed =TRUE) ) }) 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-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/test-rd-examples.R0000644000176200001440000000602214527136371020363 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, fixed("example <- 'example1'"), all = FALSE) expect_match(examples, fixed("example <- 'example2'"), all = FALSE) }) 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(str_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 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(. <- roc_proc_text(rd_roclet(), block)) }) 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}")) }) # 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/testUtf8Escape/0000755000176200001440000000000014520730024017675 5ustar liggesusersroxygen2/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/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/empty/0000755000176200001440000000000014527136371016200 5ustar liggesusersroxygen2/tests/testthat/empty/DESCRIPTION0000644000176200001440000000014114527136371017702 0ustar liggesusersPackage: empty Version: 1.0.0 Encoding: UTF-8 Title: Empty package Description: An empty package roxygen2/tests/testthat/empty/R/0000755000176200001440000000000013523072405016371 5ustar liggesusersroxygen2/tests/testthat/empty/R/empty-package.R0000644000176200001440000000000513523072405021236 0ustar liggesusersNULL roxygen2/tests/testthat/test-select-args.R0000644000176200001440000000140514344445343020352 0ustar liggesuserstest_that("warns on invalid input", { topic <- RoxyTopic$new() topic$add(rd_section("name", "test")) expect_snapshot({ select_args_text(sum, "-xlab:", topic) select_args_text(sum, '"a"', topic) select_args_text(function(x, y, z) {}, "-x:z", topic) }) }) test_that("positive initial values starts from nothing", { f <- function(x, y, z) {} expect_equal(select_args_text(f, "x y"), c("x", "y")) }) test_that("negative initial starts from everything", { f <- function(x, y, z) {} expect_equal(select_args_text(f, "-z"), c("x", "y")) }) test_that("can alternative exclusion and inclusion", { f <- function(x, y, z) {} expect_equal(select_args_text(f, "-z z"), c("x", "y", "z")) expect_equal(select_args_text(f, "z -z"), 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/test-rd-s4.R0000644000176200001440000000142214531364526017072 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-markdown-code.R0000644000176200001440000001273314262717326020703 0ustar liggesuserstest_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"), "\\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]] ) expect_equal(out$get_value("description"), "\\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("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)) }) 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-describe-in.R0000644000176200001440000001505614550054451020732 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", { 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/testCollateNoIncludes/0000755000176200001440000000000014520730024021275 5ustar liggesusersroxygen2/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/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/Rd-example-1.R0000644000176200001440000000002613523072405017307 0ustar liggesusersexample <- 'example1' roxygen2/tests/testthat/test-rd-find-link-files.R0000644000176200001440000000031314262717326021516 0ustar liggesuserstest_that("generates informative warnings", { expect_snapshot({ tag <- roxy_test_tag() find_topic_filename("11papaya", "foo", tag) find_topic_filename("stringr", "foofofofoo", tag) }) }) roxygen2/tests/testthat/roxygen-block-3-A.Rd0000644000176200001440000000646614552504103020434 0ustar liggesusers> res[[n]] % Generated by roxygen2: do not edit by hand % Please edit documentation in ./roxygen-block-3.R \name{A} \alias{A} \title{Class A} \description{ Class A description. } \details{ Class A details } \examples{ ## ------------------------------------------------ ## Method `A$meth1` ## ------------------------------------------------ ## Example for meth1 ## ------------------------------------------------ ## Method `A$meth2` ## ------------------------------------------------ ## Example for meth2 } \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 2.} } \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{Method \code{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{Method \code{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{Examples}{ \if{html}{\out{
}} \preformatted{## Example for meth2 } \if{html}{\out{
}} } } \if{html}{\out{
}} \if{html}{\out{}} \if{latex}{\out{\hypertarget{method-A-meth3}{}}} \subsection{Method \code{meth3()}}{ \subsection{Usage}{ \if{html}{\out{
}}\preformatted{A$meth3(duplicate, missing)}\if{html}{\out{
}} } \subsection{Arguments}{ \if{html}{\out{
}} \describe{ \item{\code{duplicate}}{This one is} \item{\code{duplicate}}{Twice} } \if{html}{\out{
}} } \subsection{Details}{ Method 3 details. } \subsection{Returns}{ twice. } } \if{html}{\out{
}} \if{html}{\out{}} \if{latex}{\out{\hypertarget{method-A-clone}{}}} \subsection{Method \code{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{
}} } } } roxygen2/tests/testthat/test-object-from-call.R0000644000176200001440000001526114527377516021277 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(purrr::map_int) expect_s3_class(obj, "import") expect_equal(obj$alias, "map_int") expect_equal(obj$value$pkg, "purrr") }) # 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 data created with assignment", { obj <- call_to_object({ foo <- 1:10 }) expect_s3_class(obj, "data") }) 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", "...")) }) # 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) }) roxygen2/tests/testthat/test-object-defaults.R0000644000176200001440000000627214527224165021223 0ustar liggesuserstest_that("@docType data automatically adds sensible defaults", { 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("keyword"), "datasets") expect_false(is.null(out$get_value("format"))) }) test_that("@docType data automatically added to data objects", { out <- roc_proc_text(rd_roclet(), " #' Title. a <- data.frame(a = 1:10) ")[[1]] 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("a")) expect_equal(out$get_value("keyword"), "datasets") }) # 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.") }) roxygen2/tests/testthat/helper-test.R0000644000176200001440000000150714527136371017424 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))) } 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-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-markdown-link.R0000644000176200001440000003277214527136371020732 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()]"), "\\code{[[}. \\code{\\link[=subset]{subset()}}" ) }) test_that("can escape [ to avoid spurious links", { expect_equal( markdown("\\[test\\]"), "[test]" ) expect_equal( markdown("\\[ [test] \\]"), "[ \\link{test} ]" ) }) test_that("\\Sexpr with options not converted to links", { expect_equal( markdown("\\Sexpr[results=rd]{runif(1)}"), "\\Sexpr[results=rd]{runif(1)}" ) }) test_that("% in links are escaped", { expect_equal(markdown("[x][%%]"), "\\link[=\\%\\%]{x}") expect_equal(markdown("[%][x]"), "\\link[=x]{\\%}") expect_equal(markdown("[%%]"), "\\link{\\%\\%}") expect_equal(markdown("[base::%%]"), "\\link[base:Arithmetic]{base::\\%\\%}") }) test_that("{ and } in links are escaped (#1259)", { expect_equal(markdown("[`foo({ bar })`][x]"), "\\code{\\link[=x]{foo(\\{ bar \\})}}") expect_equal(markdown("[`{{`][x]"), "\\code{\\link[=x]{\\{\\{}}") # Non code parts are not escaped (invalid Rd) expect_equal(markdown("[foo({ bar })][x]"), "\\link[=x]{foo({ bar })}") }) test_that("non-text nodes in links fails", { tag <- roxy_tag("title", NULL, NULL, file = "foo.R", line = 10) expect_snapshot({ markdown("[`foo` bar][x]", tag = tag) markdown("[__baz__][x]", tag = tag) }) }) 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][stringr::bar111]. #' @md foo <- function() {} " expect_snapshot(out1 <- roc_proc_text(rd_roclet(), block)[[1]]) out2 <- roc_proc_text(rd_roclet(), " #' Title #' #' Description, see \\link[stringr: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]{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", { suppressWarnings(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]{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 \\linkS4class{linktos4} as well. foo <- function() {}")[[1]] expect_equivalent_rd(out1, out2) suppressWarnings(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("linking to self is unqualified", { local_roxy_meta_set("current_package", "myself") rd <- markdown("foo [myself::fun()] and [myself::obj] bar") expect_equal( rd, "foo \\code{\\link[=fun]{fun()}} and \\link{obj} bar" ) }) 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) }) roxygen2/tests/testthat/test-tag.R0000644000176200001440000000435214527136371016721 0ustar liggesuserstest_that("warn about unknown tags", { block <- " #' @unknown foo <- function() {} " expect_snapshot(. <- roc_proc_text(rd_roclet(), block)) }) # Test low-level behaviour ---------------------------------------------------- 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("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("\\%{", 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("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)) }) # Test that incomplete Rd is caught in Rd blocks ------------------------------- 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/test-tokenize.R0000644000176200001440000000751314262717326020001 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-simple.R0000644000176200001440000000133313542227702020031 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/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-object-rc.R0000644000176200001440000000363514116121427020007 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/roxygen-block-2.R0000644000176200001440000000057213523072405020103 0ustar liggesusers#' The length of a string (in characters). #' #' @param string input character vector #' @return numeric vector giving number of characters in each element of the #' character vector. Missing strings have missing length. #' @seealso \code{\link{nchar}} which this function wraps #' @export #' @examples #' str_length(letters) #' str_length(c("i", "like", "programming", NA)) roxygen2/tests/testthat/roxygen-block-1.R0000644000176200001440000000033713523072405020101 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 roxygen2/tests/testthat/_snaps/0000755000176200001440000000000014553537636016335 5ustar liggesusersroxygen2/tests/testthat/_snaps/roxygenize-setup.md0000644000176200001440000000254014552504103022177 0ustar liggesusers# useful error if no DESCRIPTION Code roxygen_setup(path) Condition Error in `roxygen_setup()`: ! `package.dir` ('') does not contain a DESCRIPTION # informs about initial setup Code roxygen_setup(path, cur_version = "8.0.0") Message First time using roxygen2. Upgrading automatically... Setting `RoxygenNote` 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 -------------------------------------------------------------------------------- Setting `RoxygenNote` to "8.0.0" Output [1] FALSE roxygen2/tests/testthat/_snaps/tag.md0000644000176200001440000000057014552504104017413 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-section.md0000644000176200001440000000032214552504103020701 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/rd-params.md0000644000176200001440000000026614552504103020527 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/markdown.md0000644000176200001440000000642014552504102020460 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 # 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? # 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{
}} } # 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/rd.md0000644000176200001440000000250314552504104017243 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/collate.md0000644000176200001440000000113114552504101020252 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/markdown-link.md0000644000176200001440000000220114552504102021404 0ustar liggesusers# non-text nodes in links fails Code markdown("[`foo` bar][x]", tag = tag) Message x foo.R:10: @title (automatically generated) markdown links must contain plain text. i Problematic link: x Output [1] "" Code markdown("[__baz__][x]", tag = tag) Message x foo.R:10: @title (automatically generated) markdown links must contain plain text. i Problematic link: x Output [1] "" # 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 unavailable topic 11pkg::function. Caused by error in `find.package()`: ! there is no package called '11pkg' x :4: @description refers to unavailable topic 11pkg::object. Caused by error in `find.package()`: ! there is no package called '11pkg' --- Code out1 <- roc_proc_text(rd_roclet(), block)[[1]] Message x :4: @description refers to unavailable topic stringr::bar111. roxygen2/tests/testthat/_snaps/block.md0000644000176200001440000000221214552504102017723 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-s4.md0000644000176200001440000000040614552504103017566 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/rd-markdown.md0000644000176200001440000000024214552504103021060 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/roxygenize.md0000644000176200001440000000026014552504103021036 0ustar liggesusers# can regenerate NAMESPACE even if its broken Code roxygenise(path) Message Writing 'NAMESPACE' i Loading brokenNamespace Writing 'NAMESPACE' roxygen2/tests/testthat/_snaps/object-package.md0000644000176200001440000000232214552504102021472 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 = "1234")) Output [1] "H W \\email{h@w.com} (\\href{https://orcid.org/1234}{ORCID})" Code person_desc(comment = c(ORCID = "https://orcid.org/1234")) Output [1] "H W \\email{h@w.com} (\\href{https://orcid.org/1234}{ORCID})" Code person_desc(comment = c(ORCID = "1234", "extra")) Output [1] "H W \\email{h@w.com} (\\href{https://orcid.org/1234}{ORCID}) (extra)" # 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/object-from-call.md0000644000176200001440000000027214552504102021755 0ustar liggesusers# recommends use of _PACKAGE Code out <- parse_text(block)[[1]] Message x :3: `@docType "package"` is deprecated. i Please document "_PACKAGE" instead. roxygen2/tests/testthat/_snaps/rd-template.md0000644000176200001440000000063714552504103021061 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 in `map_chr()`: i In index: 1. Caused by error: ! Can't find template "doesn't-exist" roxygen2/tests/testthat/_snaps/rd-find-link-files.md0000644000176200001440000000076614552504103022224 0ustar liggesusers# generates informative warnings Code tag <- roxy_test_tag() find_topic_filename("11papaya", "foo", tag) Message x test.R:1: @test refers to unavailable topic 11papaya::foo. Caused by error in `find.package()`: ! there is no package called '11papaya' Output [1] "foo" Code find_topic_filename("stringr", "foofofofoo", tag) Message x test.R:1: @test refers to unavailable topic stringr::foofofofoo. Output [1] "foofofofoo" roxygen2/tests/testthat/_snaps/rd-describe-in.md0000644000176200001440000000550414552504103021430 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-raw.md0000644000176200001440000000123014552504103020025 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/_snaps/rd-examples.md0000644000176200001440000000214014552504102021052 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()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} maybe-run-this-code \dontshow{\}) # examplesIf} \dontshow{if (foobar()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} and-this \dontshow{\}) # examplesIf} } # @examplesIf warns about unparseable condition Code . <- 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 + ^ roxygen2/tests/testthat/_snaps/rd-inherit.md0000644000176200001440000000501614552504104020705 0ustar liggesusers# \links are transformed \arguments{ \item{algo}{The hashing algoritm 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]{foo}}, \code{\link[=bar]{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': @inheritDotsParam failed. Caused by error in `FUN()`: ! object 'z' not found # useful warnings if can't find topics Code get_rd("base2::attach", source = "source") Message x In topic 'source': @inherits failed because base2 is not installed. Output NULL Code get_rd("base::function_not_found", source = "source") Message x In topic 'source': @inherits failed to find topic base::function_not_found. Output NULL Code get_rd("function", RoxyTopics$new(), source = "source") Message x In topic 'source': @inherits failed to find topic "function". Output NULL Code get_rd("foo::bar()", RoxyTopics$new(), source = "source") Message x In topic 'source': @inherits failed to find topic "foo::bar()". Output NULL roxygen2/tests/testthat/_snaps/topics.md0000644000176200001440000000030114552504104020131 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/select-args.md0000644000176200001440000000152514552504104021052 0ustar liggesusers# warns on invalid input Code select_args_text(sum, "-xlab:", topic) Message x In topic 'test': @inheritDotsParam failed. Caused by error in `parse()`: ! :2:0: unexpected end of input 1: -xlab: ^ Output character(0) Code select_args_text(sum, "\"a\"", topic) Message x In topic 'test': @inheritDotsParam 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(function(x, y, z) { }, "-x:z", topic) Message x In topic 'test': @inheritDotsParam 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/rd-family.md0000644000176200001440000000310214552504103020515 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}()}, \code{\link{foo}()}, \code{\link{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}()}, \code{\link{foo}()}, \code{\link{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}()}, \code{\link{foo1}()}, \code{\link{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}()}, \code{\link{foo1}()}, \code{\link{foo2}()} } \concept{a} roxygen2/tests/testthat/_snaps/utils.md0000644000176200001440000000075214552504104020002 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 roxygen2/tests/testthat/_snaps/rd-usage.md0000644000176200001440000000346314552504103020352 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-include-rmd.md0000644000176200001440000000114614552504104021446 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) Message Quitting from lines 2-3 [unnamed-chunk-2] () Quitting from lines 2-3 [unnamed-chunk-1] () x :3: @includeRmd failed to evaluate ''. Caused by error: ! Error roxygen2/tests/testthat/_snaps/object-import.md0000644000176200001440000000101114552504102021403 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}}} }} roxygen2/tests/testthat/_snaps/tag-parser.md0000644000176200001440000001261714552504104020712 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_words_line(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 2. # 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_line() gives useful warnings Code tag <- roxy_test_tag("a\nb") expect_parse_failure(tag_words_line(tag)) Output Message: x test.R:1: @test must be a single line, not 2. i The first line is "a" Code tag <- roxy_test_tag("a\nb\n2") expect_parse_failure(tag_words_line(tag)) Output Message: x test.R:1: @test must be a single line, not 3. i The first line is "a" # 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/markdown-code.md0000644000176200001440000000237114552504102021371 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 lines 1-1 x :4: @description failed to evaluate inline markdown code. Caused by error in `map_chr()`: i In index: 1. 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{
}} roxygen2/tests/testthat/_snaps/rd-r6.md0000644000176200001440000000362714552504103017577 0ustar liggesusers# R6 edge cases, class without (documented) fields Code topic_add_r6_methods(rd, block, environment()) Message x :7: Undocumented R6 field: undocumented_field. # warning if no method comes after the docs Code topic_add_r6_methods(rd, block, environment()) Message x :10: @description Cannot find matching R6 method. # class with no inherited methods Code cat(format(rd$get_section("rawRd"))) 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{Method \code{meth1()}}{ method1 \subsection{Usage}{ \if{html}{\out{
}}\preformatted{C2$meth1()}\if{html}{\out{
}} } } } # integration test Code res <- roclet_process(roc, blocks = blocks, env = env, base_path = test_path()) Message x roxygen-block-3.R:13: Must use one @param for each argument. x $meth3(duplicate) is documented multiple times x roxygen-block-3.R:13: Must use one @param for each argument. x $meth3(missing) is not documented x roxygen-block-3.R:13: Must use one @return per R6 method. x roxygen-block-3.R:92: Undocumented R6 method: undocumented_method. x roxygen-block-3.R:92: Undocumented R6 fields: field2 and undocumented_field. x roxygen-block-3.R:92: R6 field documented multiple times: duplicatefield. x roxygen-block-3.R:92: Unknown R6 field: nosuchfield. x roxygen-block-3.R:92: Undocumented R6 active binding: undocumented_binding. x roxygen-block-3.R:92: R6 active binding documented multiple times: duplicate_binding. roxygen2/tests/testthat/_snaps/object-r6.md0000644000176200001440000000067114552504102020433 0ustar liggesusers# extract_r6_data without source refs Code extract_r6_data(C) Condition Error in `map_int()`: i In index: 1. i With name: meth1. Caused by 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-format.md0000644000176200001440000000142114552504102021366 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." roxygen2/tests/testthat/_snaps/markdown-state.md0000644000176200001440000000026214552504101021573 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/namespace.md0000644000176200001440000000520114553537636020611 0ustar liggesusers# @exportS3method generates fully automatically Code . <- roc_proc_text(namespace_roclet(), block) Message x :2: @exportS3Method must be used with an 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. # 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/testRbuildignore/0000755000176200001440000000000013523072405020357 5ustar liggesusersroxygen2/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/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/made-by-roxygen/0000755000176200001440000000000013523072405020041 5ustar 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/made-by-roxygen/without-header.Rd0000644000176200001440000000001613523072405023256 0ustar liggesusers # Test file roxygen2/tests/testthat/made-by-roxygen/empty.Rd0000644000176200001440000000000013523072405021454 0ustar liggesusersroxygen2/tests/testthat/test-object-r6.R0000644000176200001440000000056614262717326017745 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) }) roxygen2/tests/testthat/escapes.Rd0000644000176200001440000000010514225272441016742 0ustar liggesusers\name{x} \alias{x} \title{title} \description{ % Comment \code{\\} } roxygen2/tests/testthat/testNonASCII/0000755000176200001440000000000014520730024017231 5ustar liggesusersroxygen2/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/testNonASCII/R/0000755000176200001440000000000014520730024017432 5ustar liggesusersroxygen2/tests/testthat/testNonASCII/R/a.R0000644000176200001440000000011614520730024017773 0ustar liggesusers#' 中文注释 #' #' @note 我爱中文。 printChineseMsg <- function() { } roxygen2/tests/testthat/test-roxygenize-setup.R0000644000176200001440000000211214527136371021477 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")) }) roxygen2/tests/testthat/test-options/0000755000176200001440000000000014553554653017520 5ustar liggesusersroxygen2/tests/testthat/test-options/meta-error.R0000644000176200001440000000003214262677627021717 0ustar liggesusersstop("This is an error!") 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/DESCRIPTION0000644000176200001440000000004014262677676021230 0ustar liggesusersRoxygen: list(old_usage = TRUE) roxygen2/tests/testthat/test-options/meta-character.R0000644000176200001440000000000414262677627022521 0ustar liggesusers"a" roxygen2/tests/testthat/testLazyData/0000755000176200001440000000000014527170577017461 5ustar liggesusersroxygen2/tests/testthat/testLazyData/NAMESPACE0000644000176200001440000000005613523072405020663 0ustar liggesusers# Generated by roxygen2: do not edit by hand 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/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/testLazyData/R/0000755000176200001440000000000014520730024017640 5ustar liggesusersroxygen2/tests/testthat/testLazyData/R/a.R0000644000176200001440000000002714520730024020202 0ustar liggesusers#' Data #' #' Desc "a" roxygen2/tests/testthat/test-rd-r6.R0000644000176200001440000002260214527136371017076 0ustar liggesuserstest_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("roxygen-block-3.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("method", "method", "method", "field", "field", "active", "active", "active", "method", "method", "method", "method", "field", "field", "field", "active", "active", "active") ) 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("r6_fields", { text <- " #' @title Title #' @description Description. #' @details Details. #' @field field1 Foo. #' @field field2 Bar. #' @field bind1 Active binding. C <- R6::R6Class( public = list( field1 = NULL, field2 = \"foobar\" ), active = list( bind1 = function(x) { } ) )" block <- parse_text(text)[[1]] r6data <- block_get_tag_value(block, ".r6data") expect_silent(doc <- r6_fields(block, r6data)) expect_true(any(grepl("code{field1}}{Foo.", doc, fixed = TRUE))) expect_true(any(grepl("code{field2}}{Bar.", doc, fixed = TRUE))) }) test_that("r6_active_bindings", { text <- " #' @title Title #' @description Description. #' @details Details. #' @field bind1 Active binding. #' @field bind2 Active 2. C <- R6::R6Class( public = list( field1 = NULL, field2 = \"foobar\" ), active = list( bind1 = function(x) { }, bind2 = function(x) { } ) )" block <- parse_text(text)[[1]] r6data <- block_get_tag_value(block, ".r6data") expect_silent(doc <- r6_active_bindings(block, r6data)) expect_true(any(grepl("code{bind1}}{Active binding.", doc, fixed = TRUE))) expect_true(any(grepl("code{bind2}}{Active 2.", doc, fixed = TRUE))) }) test_that("R6 edge cases, class without methods", { text <- " #' @title Title #' @description Description. #' @details Details. #' @field field1 Field. #' @field field2 Another field. #' @field bind1 Active binding. #' @field bind2 Active 2. C <- R6::R6Class( cloneable = FALSE, public = list( field1 = NULL, field2 = \"foobar\" ), active = list( bind1 = function(x) { }, bind2 = function(x) { } ) )" block <- parse_text(text)[[1]] rd <- RoxyTopic$new() expect_silent(topic_add_r6_methods(rd, block, environment())) expect_false(grepl("method", format(rd), ignore.case = TRUE)) }) test_that("R6 edge cases, class without (documented) fields", { text <- " #' @title Title #' @description Description. #' @details Details. #' @field bind1 Active binding. #' @field bind2 Active 2. C <- R6::R6Class( public = list( ), active = list( bind1 = function(x) { }, bind2 = function(x) { } ) )" block <- parse_text(text)[[1]] rd <- RoxyTopic$new() expect_silent(topic_add_r6_methods(rd, block, environment())) expect_false(grepl("field", format(rd), ignore.case = TRUE)) text <- " #' @title Title #' @description Description. #' @details Details. #' @field bind1 Active binding. #' @field bind2 Active 2. C <- R6::R6Class( public = list( undocumented_field = NULL ), active = list( bind1 = function(x) { }, bind2 = function(x) { } ) )" block <- parse_text(text)[[1]] rd <- RoxyTopic$new() expect_snapshot(topic_add_r6_methods(rd, block, environment())) expect_false(grepl("field", format(rd), ignore.case = TRUE)) }) test_that("R6 edge cases, class without active bindings", { text <- " #' @title Title #' @description Description. #' @details Details. #' @field field1 Field. #' @field field2 Another field. C <- R6::R6Class( public = list( field1 = NULL, field2 = \"foobar\" ), active = list( ) )" block <- parse_text(text)[[1]] rd <- RoxyTopic$new() expect_silent(topic_add_r6_methods(rd, block, environment())) expect_false(grepl("active", format(rd), ignore.case = TRUE)) }) test_that("R6 edge cases, class without anything", { text <- " #' @title Title #' @description Description. #' @details Details. C <- R6::R6Class( cloneable = FALSE, public = list( ), active = list( ) )" block <- parse_text(text)[[1]] rd <- RoxyTopic$new() expect_silent(topic_add_r6_methods(rd, block, environment())) doc <- format(rd) expect_false(grepl("method", format(rd), ignore.case = TRUE)) expect_false(grepl("field", format(rd), ignore.case = TRUE)) expect_false(grepl("active", format(rd), ignore.case = TRUE)) }) test_that("warning if no method comes after the docs", { text <- " #' @title Title #' @description Description. #' @details Details. #' @field field1 Yep. C <- R6::R6Class( public = list( #' @description Method 1. method1 = function() { }, #' @description Dangling. field1 = NULL ) )" eval(parse(text = text, keep.source = TRUE)) block <- parse_text(text, env = environment())[[1]] rd <- RoxyTopic$new() expect_snapshot(topic_add_r6_methods(rd, block, environment())) doc <- format(rd) }) test_that("class with no inherited methods", { text <- " C1 <- R6::R6Class('C1', cloneable = FALSE) #' @title Title #' @description Description. #' @details Details. C2 <- R6::R6Class('C2', inherit = C1, cloneable = FALSE, public = list( #' @description method1 meth1 = function() 1 ) )" env <- new.env(parent = globalenv()) eval(parse(text = text, keep.source = TRUE), envir = env) block <- parse_text(text, env = env)[[1]] rd <- RoxyTopic$new() topic_add_r6_methods(rd, block, env) expect_snapshot(cat(format(rd$get_section("rawRd")))) }) test_that("integration test", { wd <- getwd() on.exit(setwd(wd), add = TRUE) setwd(test_path()) env <- new.env(parent = asNamespace("roxygen2")) eval( parse(test_path("roxygen-block-3.R"), keep.source = TRUE), envir = env ) blocks <- parse_file(test_path("roxygen-block-3.R"), env = env) roc <- roclet_preprocess(roclet_find("rd")) expect_snapshot( res <- roclet_process(roc, blocks = blocks, env = env, base_path = test_path()) ) tmp <- tempfile() on.exit(unlink(tmp), add = TRUE) for (n in names(res)) { path <- test_path(paste0("roxygen-block-3-", n)) verify_output(path, res[[n]]) cat(format(res[[n]]), file = tmp) expect_silent(chk <- tools::checkRd(tmp)) expect_equal(length(chk), 0L) } }) test_that("r6 option", { text <- " #' @title Title #' @description Description. C <- R6::R6Class( public = list( field = NULL, #' @description Method desc. #' @param arg Method arg. meth = function(arg) { } ) )" local_roxy_meta_set("r6", FALSE) expect_silent( out <- roc_proc_text(rd_roclet(), text) ) rd <- format(out$C.Rd) expect_false(grepl("section{Methods}", rd, fixed = TRUE)) expect_true(grepl("arguments{", rd, fixed = TRUE)) }) roxygen2/tests/testthat/test-rd-raw.R0000644000176200001440000000141414526152403017327 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.R0000644000176200001440000000077314262677567017661 0ustar liggesuserstest_that("merged options from can load options from DESCRIPTION", { 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("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/testCollateOverwrite/0000755000176200001440000000000014520730024021220 5ustar liggesusersroxygen2/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/testCollateOverwrite/R/0000755000176200001440000000000014520730024021421 5ustar liggesusersroxygen2/tests/testthat/testCollateOverwrite/R/a.R0000644000176200001440000000002714520730024021763 0ustar liggesusers#' @include b.R a <- 1 roxygen2/tests/testthat/testCollateOverwrite/R/b.R0000644000176200001440000000000614520730024021761 0ustar liggesusersb <- 2roxygen2/tests/testthat/test-object-package.R0000644000176200001440000000544014525476560021011 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" = "1234")) person_desc(comment = c("ORCID" = "https://orcid.org/1234")) person_desc(comment = c("ORCID" = "1234", "extra")) }) }) 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("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") expect_equal(package_url_parse("x y"), "x \\doi{\\%3C-\\%3E} y") }) 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, two@email.me}" ) }) roxygen2/tests/testthat/test-utils.R0000644000176200001440000000503614263111134017271 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("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) expect_snapshot(write_if_different(path, "a <- 2")) 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)) }) roxygen2/tests/testthat/test-rd-inherit.R0000644000176200001440000003737714531364526020230 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" ) }) # 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("@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") ) }) # 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)) }) # 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("base2::attach", source = "source") get_rd("base::function_not_found", source = "source") get_rd("function", RoxyTopics$new(), source = "source") get_rd("foo::bar()", 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-rd.R0000644000176200001440000001265514527377516016570 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 overriden 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-rd-markdown-escaping.R0000644000176200001440000000535514262717326022167 0ustar liggesuserstag_df <- function(tag, start, end, argend = NULL) { df <- data.frame( stringsAsFactors = FALSE, tag = tag, start = start, end = end ) if (!is.null(argend)) df$argend <- argend df } test_that("find_all_tag_names", { text <- "blah blah \\mytag blah blah" expect_equal( find_all_tag_names(text), tag_df("\\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("blah blah \\mytag blah blah", "\\mytag", 11, 16, 16), list("blah blah \\mytag{arg1} blah blah", "\\mytag", 11, 16, 22), list("blah blah \\mytag{arg1}{arg2} blah blah", "\\mytag", 11, 16, 28), list("blah\\mytag", "\\mytag", 5, 10, 10), list("blah \\mytag", "\\mytag", 6, 11, 11), list("blah\\mytag{arg}", "\\mytag", 5, 10, 15), list("\\mytag hoohoo", "\\mytag", 1, 6, 6), list("\\mytag", "\\mytag", 1, 6, 6), list("\\mytag{arg}", "\\mytag", 1, 6, 11), list("blah \\mytag\nblah blah", "\\mytag", 6, 11, 11), ## Multiple tags list("blah \\tag1 \\tag2{arg} blah", c("\\tag1", "\\tag2"), c(6, 12), c(10, 16), c(10, 21)), list("blah \\tag1{ \\tag2{arg} } blah", c("\\tag1", "\\tag2"), c(6, 13), c(10, 17), c(24, 22)), list("blah \\tag1{\n\\tag2{arg}\n} blah", c("\\tag1", "\\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("\\frag", "\\frag1", "\\frag2") cases <- list( list("This is \\frag{here}, \\this{arg} not", "\\frag"), list("Embedded \\frag{ into \\frag1{arg} plus }", "\\frag"), list( "blah \\cmd{ \\frag{arg} \\frag{arg} } \\frag2 blah", c("\\frag", "\\frag", "\\frag2") ) ) for (case in cases) { expect_equal( find_fragile_rd_tags(case[[1]], fragile)$tag, case[[2]], info = case[[1]] ) } }) test_that("str_sub_same", { expect_equal( str_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( str_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( str_sub_same( "123456789ab", data.frame(start = numeric(), end = numeric(), argend = numeric()), "xxx" ), "123456789ab" ) }) 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/test-collate.R0000644000176200001440000000315414527172500017562 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/test-markdown-state.R0000644000176200001440000000511414527136371021103 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"), "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"), "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"), "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() suppressWarnings(out2 <- roc_proc_text(rd_roclet(), block)) expect_equal(out2[[1]]$get_value("description"), "`code`") }) roxygen2/tests/testthat/testEagerData/0000755000176200001440000000000014520730024017543 5ustar liggesusersroxygen2/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/testEagerData/R/0000755000176200001440000000000014520730024017744 5ustar liggesusersroxygen2/tests/testthat/testEagerData/R/a.R0000644000176200001440000000002714520730024020306 0ustar liggesusers#' Data #' #' Desc "a" roxygen2/tests/testthat/test-rd-backref.R0000644000176200001440000000075614527136371020152 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/testNamespace/0000755000176200001440000000000014553543157017641 5ustar liggesusersroxygen2/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/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/Rd-example-2.R0000644000176200001440000000002613523072405017310 0ustar liggesusersexample <- 'example2' roxygen2/tests/testthat.R0000644000176200001440000000007414262717326015167 0ustar liggesuserslibrary(testthat) library(roxygen2) test_check("roxygen2") roxygen2/src/0000755000176200001440000000000014553543157012632 5ustar liggesusersroxygen2/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/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/src/isComplete.cpp0000644000176200001440000000477014262717326015450 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. // 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 n = string.length(); char in_string = '\0'; bool in_escape = false; bool in_r_comment = false; bool in_latex_comment = false; int braces = 0, r_braces = 0; for(int i = 0; i < n; i++) { char cur = string[i]; if (in_escape) { // Swallow escaped characters in_escape = false; } else if (in_string != '\0') { // Look for end of string if (cur == in_string) { in_string = false; } else if (cur == '\\') { in_escape = true; } } else if (in_r_comment) { // Inside R comments, braces must match. // R comments are terminated by newline or } not matched by { if (cur == '\n') { in_r_comment = false; r_braces = 0; } else if (cur == '{') { braces++; r_braces++; } else if (cur == '}') { braces--; r_braces--; if (r_braces == 0) in_r_comment = false; } } else if (in_latex_comment) { if (cur == '\n') { in_latex_comment = false; } } else { switch(cur) { case '{': braces++; break; case '}': braces--; break; case '\\': in_escape = true; break; case '#': if (is_code) in_r_comment = true; break; case '%': in_latex_comment = true; break; case '\'': if (is_code) in_string = '\''; break; case '"': if (is_code) in_string = '"'; break; } } if (mode == 1) { bool complete = braces == 0 && !in_escape && !in_string; if (complete && i + 1 < n && string[i + 1] != '{') { return i; } } } bool complete = braces == 0 && !in_escape && !in_string; if (mode == 0) { if (complete) return 1; else return 0; } else { if (complete) return n - 1; else return -1; } } [[cpp11::register]] int findEndOfTag(std::string string, bool is_code) { return roxygen_parse_tag(string, is_code, 1); } [[cpp11::register]] bool rdComplete(std::string string, bool is_code) { return roxygen_parse_tag(string, is_code, 0) == 1 ? true : false; } 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/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/cpp11.cpp0000644000176200001440000000570014527141272014255 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); extern "C" SEXP _roxygen2_findEndOfTag(SEXP string, SEXP is_code) { BEGIN_CPP11 return cpp11::as_sexp(findEndOfTag(cpp11::as_cpp>(string), cpp11::as_cpp>(is_code))); 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, 2}, {"_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/vignettes/0000755000176200001440000000000014553543157014053 5ustar liggesusersroxygen2/vignettes/roxygen2.Rmd0000644000176200001440000001063214525233523016266 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`. ## Learn more This vignette provides a high-level description of roxygen2 and the overall workflow you'll use with it. The other vignettes provide more detail on the most important individual components: - Start with `vignette("rd")` to learn how document your functions with roxygen2. - `vignette("rd-other")` discusses how to document other things like datasets, the package itself, and the various pieces used by R's 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. ## Running roxygen There are three main ways to run roxygen: - `roxygen2::roxygenise()`. - `devtools::document()`. - `Ctrl + Shift + D`, if you're using RStudio. You can mix handwritten Rd and roxygen2; roxygen2 will never overwrite a file it didn't create. ## Basic process 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. #' @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. roxygen2/vignettes/rd-formatting.Rmd0000644000176200001440000003453114525212067017272 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: Roxygen: list(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 two basic forms: - `[topic]`: The link text is automatically generated from the topic. - `[text][topic]`: You supply the link text. ### Default link text First we explore the simplest form: `[ref]`. The presence of trailing parentheses, e.g., `[func()]`, signals that the target `func` is a function, which causes two things to happen: - The link text `func()` is automatically typeset as code. - The parentheses are stripped in the derived Rd link target. +----------------------+---------------------+---------------------------------------------+ | Markdown | Links to help\ | Notes | | | topic for ... | | +:=====================+:====================+:============================================+ | `[func()]`\ | a function in same\ | Always typeset as code.\ | | `[pkg::func()]` | package or in `pkg` | Produces Rd: `\code{\link[=func]{func()}}`\ | | | | or `\code{\link[pkg:func]{pkg::func()}}` | +----------------------+---------------------+---------------------------------------------+ | `[thing]`\ | a topic in same\ | Use for a dataset or general doc page.\ | | `[pkg::thing]` | package or in `pkg` | Not typeset as code.\ | | | | Produces Rd: `\link{thing}` or\ | | | | `\link[pkg:thing]{pkg::thing}` | +----------------------+---------------------+---------------------------------------------+ | `` [`thing`] ``\ | a topic in same\ | Same as above, but explicit backticks\ | | `` [`pkg::thing`] `` | package or in `pkg` | mean that it **is** typeset as code.\ | | | | Good for documenting a class.\ | | | | Produces Rd: `\code{\link{thing}}` or\ | | | | `\code{\link[pkg:thing]{pkg::thing}}` | +----------------------+---------------------+---------------------------------------------+ ### Custom link text Use the second form `[text][ref]` to link to the topic specified by `ref`, but with `text` as the link text. +----------------------------+---------------------+-----------------------------------------+ | Markdown | Links to help\ | Notes | | | topic for ... | | +:===========================+:====================+:========================================+ | `[text][func()]`\ | a function in same\ | Text is not typeset as code.\ | | `[text][pkg::func()]` | package or in `pkg` | Produces Rd: `\link[=func]{text}` or\ | | | | `\link[pkg:func]{text}` | +----------------------------+---------------------+-----------------------------------------+ | `[text][thing]`\ | a topic in same\ | Text is not typeset as code.\ | | `[text][pkg::thing]` | package or in `pkg` | Use for a topic that documents `NULL`\ | | | | and name is set via `@name`,\ | | | | e.g., a dataset or concept.\ | | | | Produces Rd: `\link[=thing]{text}` or\ | | | | `\link[pkg:thing]{text}` | +----------------------------+---------------------+-----------------------------------------+ | `` [`text`][thing] ``\ | a topic in same\ | Same as above, but explicit backticks\ | | `` [`text`][pkg::thing] `` | package or in `pkg` | mean that text is typeset as code.\ | | | | Produces Rd: `\code{\link{=thing}}` or\ | | | | `\code{\link[pkg:thing]{pkg::thing}}` | +----------------------------+---------------------+-----------------------------------------+ ### Operators Links to operators or objects that contain special characters do not currently work. So to link to (e.g.) the `%>%` operator in the `magrittr` package, instead of `[magrittr::%>%]`, you will need to use the `Rd` notation: `\code{\link[magrittr]{\%>\%}}`. ## 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()`. ### 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/extending.Rmd0000644000176200001440000002346614525212067016507 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) ``` ## Basics Roxygen is extensible with user-defined **roclets**. It means that you can take advantage of Roxygen's parser and extend it with your own `@tags`. There are two primary ways to extend roxygen2: - Add a new tag that generates a new top-level section in `.Rd` files. - Add a new roclet that does anything you like. This vignette will introduce you to the key data structures in roxygen2, and then show you how to use these two extension points. This vignette is very rough, so you are expected to have also read some roxygen2 source code to understand all the extension points. Hopefully, it's useful enough to help you get started, and if you have problems, please [file an issue](https://github.com/r-lib/roxygen2/issues/new)! ```{r setup} library(roxygen2) ``` ## Key data structures Before we talk about 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, 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 ``` ## Adding a new `.Rd` tag The easiest way to extend roxygen2 is to create a new tag that adds output to `.Rd` files. This requires two steps: 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. 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! ``` And generate 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 it's 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) } ``` ```{r, include = FALSE} # Needed for vignette registerS3method("roxy_tag_parse", "roxy_tag_tip", roxy_tag_parse.roxy_tag_tip) ``` We 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 roxygen options). Next, we define a method for `roxy_tag_rd()`, which must create an `rd_section()`. We're going to create a new 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 the `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 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. ## 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. Second, you define a roclet that tells roxygen how to process an entire package. ### Custom tags In this example we will make a new `@memo` tag to enable printing the memos at the console when the roclet runs. We choose that the `@memo` has this syntax: @memo [Headline] Description As an example: @memo [EFFICIENCY] Currently brute-force; find better algorithm. As above, we first define a parse method: ```{r} roxy_tag_parse.roxy_tag_memo <- function(x) { if (!grepl("^\\[.*\\].*$", x$raw)) { roxy_tag_warning(x, "Invalid memo format") return() } parsed <- stringi::stri_match(str = x$raw, regex = "\\[(.*)\\](.*)")[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 check if 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]]) ``` ### 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()` just 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 Roxygen: list(roclets = c("collate", "rd", "namespace", "yourPackage::roclet")) ``` Optionally, you can add your roclet package to the target package as a `Suggests:` dependency: ```r usethis::use_dev_package("yourPackage", type = "Suggests", remote = "yourGithubID/yourPackage") ``` You don't have to do this, but it will help other developers working on the target package. roxygen2/vignettes/rd.Rmd0000644000176200001440000001416614525212067015124 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 introduces the basics of roxygen2 for documenting functions. See `vignette("rd-other")` for documenting other types of objects and `vignette("reuse")` for reusing documentation across topics. ## 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. ## 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. You can also use explicit `@title`, `@description`, and `@details` tags. This is unnecessary unless you want to have a multi-paragraph description, bulleted list, or other more exotic structure. ```{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. ``` ## Functions 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 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. 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. ### 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`. For the purpose of illustration, it's often useful to include code that causes an error. You can do this by wrapping the code in `try()` or using `\dontrun{}` to exclude from the executed example code. For finer control, you can 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. 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 case, the function usage (which appears beneath the description in the generates 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/namespace.Rmd0000644000176200001440000002144314531364526016454 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] in 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()`, `exportS3method()`, `exportClasses()`, or`exportMethods()`) you need. [^1]: Including S3 and S4 generics and methods. Note that datasets should never be exported as they are not found in `NAMESPACE`. Instead, datasets will either be automatically exported if you set `LazyData: true` in your `DESCRIPTION`, or made available after calling `data()` if not. ### Functions A function should be exported if it is user facing; it should not be exported if it's for internal use only. 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. ```{r} #' Add two numbers together #' #' @param x,y A pair of numbers. #' @export add <- function(x, y) { x + y } ``` ### S3 An S3 generic works like a regular R function so export it following the advice above: if you want users to call it, export; otherwise, don't. ```{r} #' Take an object to bizarro world #' #' @param x A vector. #' @export bizarro <- function(x, ...) { UseMethod("bizarro") } ``` While S3 methods are regular functions with a special naming scheme, their "export" works a bit differently. S3 methods are exported only in the sense that calling the generic with the appropriate class will call the method; a user can't directly access the method definition by typing its name. A more technically correctly term would be to say that the method is **registered** so that the generics can find it. You must register, i.e. `@export`, every S3 method regardless of whether or not the generic is exported. roxygen2 will warn you if you have forgotten. ```{r} #' @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. You have four options for documenting an S3 method: - Don't document it; it's not required, and not needed for simple generics where the user won't care about the details. - If the method is particularly complex or has many arguments that the generic does not, you can document it in its own file. In this case, just document it as if it's a function. - You can use `@rdname` to document it with other methods for the generic. This is a good option if it's your generic, and you're providing a bunch of methods for different classes. - You can use `@rdname` to document it with other methods for the class. This is typically the least appealing option because the different generics will have different arguments, leading to a cluttered and potentially confusing page. ```{r} #' Take an object to bizarro world #' #' @description #' This is an S3 generic. This package provides methods for the #' following classes: #' #' * `character`: reverses the order of the letters in each element of #' the vector. #' #' @param x A vector. #' @export bizarro <- function(x, ...) { UseMethod("bizarro") } #' @export #' @rdname bizarro bizarro.character <- function(x, ...) { letters <- strsplit(x, "") letters_rev <- lapply(letters, rev) vapply(letters_rev, paste, collapse = "", FUN.VALUE = character(1)) } ``` Typically, you will write methods for generics that are either defined in the current package or a package that is a hard dependency[^2] of your package. Sometimes, however, you will want to write a method for a suggested dependency. In this case, `@export` will not work because it assumes the generic is included or imported in your `NAMESPACE`. Instead, use `@exportS3Method`. This will use "delayed" method registration, which means the method will only be registered when the suggested package is loaded. [^2]: i.e. it is listed in either the `Imports` or `Depends` fields in your `DESCRIPTION`. To use `@exportS3Method` you must provide the package and generic name in the following format: ```{r} #' @exportS3Method pkg::generic generic.foo <- function(x, ...) { } ``` ### S4 - **Classes**: export the class object if you want others to be able to extend it. - **Generics:** treat it like a function and `@export` if user facing. - **Methods**: you only need to `@export` a method, if the generic lives in another package. Unlike S3, you must document S4 methods. Because method details are often not that important, it's common to use `@rdname` to put the documentation for unimportant methods into a single topic with `@keywords internal`. ### 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) # From backports: #' @rawNamespace if (getRversion() < "4.0.0") export(stopifnot) ``` If you need to automate this, `@evalNamespace fun()` will evaluate `fun()` in the 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 place holder. 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 (either implicitly or explicitly), the methods will also be available. In other words, you don't need to do anything special for S3 methods. As long as you've imported the generic, all the methods will also be available. ### 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/rd-other.Rmd0000644000176200001440000001672614553531033016245 0ustar liggesusers--- title: "Documenting other objects" description: > How to document datasets, packages, and the classes, generics, and methods of S3, S4, and R6. output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Documenting other objects} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ``` ## 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. ```{r} #' 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. ``` 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. ## 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: ```{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. ## S3 - S3 **generics** are regular functions, so document them as such. If necessary, include a section that provides additional details for developers implementing methods. - S3 **classes** have no formal definition, so document the [constructor](https://adv-r.hadley.nz/s3.html#s3-constructor). - 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 should, however, always `@export` S3 methods). If your method is more complicated, you should document it by setting `@rdname` or `@describeIn`. For complicated methods, you might document in their own file (i.e. `@rdname generic.class`; for simpler methods you might document with the generic (i.e. `@describeIn generic)`. Learn more about these tags in `vignette("reuse")`. - Generally, roxygen2 will automatically figure out the generic that the method belongs to, and you should only need to use `@method` if there is ambiguity. 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 (e.g.) `@method all.equal data.frame`. ## S4 S4 **generics** are also functions, so document them as such. Document **S4 classes** by adding a roxygen block before `setClass()`. Use `@slot` to document the slots of the class. Here's a simple example: ```{r} #' An S4 class to represent a bank account #' #' @slot balance A length-one numeric vector Account <- setClass("Account", slots = list(balance = "numeric") ) ``` S4 **methods** are a little more complicated. Unlike S3 methods, all S4 methods must be documented. You can document them 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 the next section for more details. ## R6 - R6 methods can be documented in-line, i.e. the method's documentation comments come right before the definition of the method. - Method documentation can use the `@description`, `@details`, `@param`, `@return` and `@examples` tags. These are used to create a subsection for the method, within a separate 'Methods' section. All roxygen comment lines of a method documentation must appear after a tag. - `@param` tags that appear before the class definition are automatically inherited by all methods, if needed. - R6 fields and active bindings can make use of the `@field` tag. Their documentation should also be in-line. - roxygen2 checks that all public methods, public fields, active bindings and all method arguments are documented, and issues warnings otherwise. - To turn off the special handling of R6 classes and go back to the roxygen2 6.x.x behavior, use the `r6 = FALSE` option in `DESCRIPTION`, in the `Roxygen` entry: `Roxygen: list(r6 = FALSE)`. roxygen2 automatically generates additional sections for an R6 class: - 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. An example from the R6 tutorial: ```{r} #' R6 Class Representing a Person #' #' @description #' A person has a name and a hair color. #' #' @details #' A person can also greet you. Person <- R6::R6Class("Person", public = list( #' @field name First or full name of the person. name = NULL, #' @field hair Hair color of the person. hair = NULL, #' @description #' Create a new person object. #' @param name Name. #' @param hair Hair color. #' @return 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. #' @examples #' P <- Person("Ann", "black") #' P$hair #' P$set_hair("red") #' P$hair set_hair = function(val) { self$hair <- val }, #' @description #' Say hi. greet = function() { cat(paste0("Hello, my name is ", self$name, ".\n")) } ) ) ``` roxygen2/vignettes/reuse.Rmd0000644000176200001440000003533614527204227015645 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} #' 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 all of each function, along with its description. It uses a number of heuristics to determine the heading of this section, depending on 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 general 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. ### 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 `@inherits foo` to inherit the documentation for every supported tag from another topic. Currently, `@inherits` 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. ## 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_pass1(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. ## 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/index-crossref.Rmd0000644000176200001440000000630214520721622017440 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/R/0000755000176200001440000000000014553537636012251 5ustar liggesusersroxygen2/R/rd.R0000644000176200001440000001704414527377516013007 0ustar liggesusers#' @import stringr NULL #' Roclet: make Rd files #' #' @description #' This roclet is the workhorse of roxygen2, producing the `.Rd` files that #' R uses to document functions, datasets, packages, classes, and more. #' See `vignette("rd")` for details. #' #' Generally you will not call this function directly #' but will instead use [roxygenise()] specifying the rd roclet. #' #' @seealso [tags-rd], [tags-rd-other], [tags-reuse], [tags-index-crossref] for #' tags provided by this roclet. #' @export #' @examples #' #' The length of a string (in characters) #' #' #' #' @param x A character vector. #' #' @returns An integer vector the same length as `x`. #' #' `NA` strings have `NA` length. #' #' @seealso [nchar()] #' #' @export #' #' @examples #' #' str_length(letters) #' #' str_length(c("i", "like", "programming", NA)) #' str_length <- function(x) { #' } rd_roclet <- function() { roclet("rd") } #' @export roclet_process.roclet_rd <- function(x, blocks, env, base_path) { # 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_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, ~ .$get_name()[[1]])) if (length(names) > 0) { hrefs <- paste0("ide:run:pkgload::dev_help('", names, "')") } else { hrefs <- character() } # Always check for roxygen2 header before overwriting NAMESPACE (#436), # even when running for the first time mapply(write_if_different, paths, contents, href = hrefs) 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(purrr::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) 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 #' @keywords internal 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/select-args.R0000644000176200001440000000347514527377516014616 0ustar liggesusersselect_args_text <- function(fun, select = "", topic) { pieces <- strsplit(select, " +")[[1]] tryCatch( { parsed <- lapply(pieces, function(x) parse(text = x)[[1]]) select_args(fun, parsed) }, error = function(e) { warn_roxy_topic(topic$get_name(), "@inheritDotsParam failed", parent = e) character() } ) } # Figure out which arguments that the user wants given a function and # unevaluated list select_args <- function(fun, select = list()) { stopifnot(is.function(fun)) stopifnot(is.list(select)) args <- names(formals(fun)) 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/load.R0000644000176200001440000000552014262717326013306 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")) { if (is.function(x)) { return(x) } if (is.null(x)) { x <- option if (!is.character(x) || length(x) != 1) { cli::cli_abort("roxygen2 {.code load} option must be a string") } } else { if (!is.character(x) || length(x) != 1) { cli::cli_abort("{.code load_code} must be a string or function") } } switch(x, pkgload = load_pkgload, source = load_source, installed = load_installed, cli::cli_abort("Unknown value of {.code load} option") ) } roxygen2/R/rd-r6.R0000644000176200001440000003077514525720265013331 0ustar liggesusers topic_add_r6_methods <- function(rd, block, env) { r6data <- block_get_tag_value(block, ".r6data") self <- r6data$self methods <- self[self$type == "method", ] methods <- methods[order(methods$file, methods$line), ] methods$tags <- replicate(nrow(methods), list(), simplify = FALSE) r6_tags <- c("description", "details", "param", "return", "examples") del <- integer() for (i in seq_along(block$tags)) { tag <- block$tags[[i]] # Not inline? if (is.na(tag$line) || tag$line < block$line) next # Not a method tag? if (! tag$tag %in% r6_tags) next del <- c(del, i) meth <- find_method_for_tag(methods, tag) if (is.na(meth)) { warn_roxy_tag(tag, "Cannot find matching R6 method") next } midx <- which(meth == methods$name) methods$tags[[midx]] <- c(methods$tags[[midx]], list(tag)) del <- c(del, i) } methods <- add_default_methods(methods, block) nodoc <- map_int(methods$tags, length) == 0 if (any(nodoc)) { warn_roxy_block(block, "Undocumented R6 method{?s}: {methods$name[nodoc]}") } block$tags[del] <- NULL # Now do the main tags first. We leave out the param tags, those are # for the methods for (tag in block$tags) { if (! tag$tag %in% c("param", "field")) { rd$add(roxy_tag_rd(tag, env = env, base_path = base_path)) } } # We need to add the whole thing as a big section. rd_lines <- c( r6_superclass(block, r6data, env), r6_fields(block, r6data), r6_active_bindings(block, r6data), r6_methods(block, r6data, methods) ) rd$add(rd_section("rawRd", paste(rd_lines, collapse = "\n"))) # Dump all method examples at the end of the examples block ex_lines <- r6_all_examples(block, methods) if (length(ex_lines) > 0) { ex_txt <- paste0(r6_all_examples(block, methods), collapse = "\n") rd$add(rd_section("examples", ex_txt), overwrite = FALSE) } } 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 } r6_superclass <- function(block, r6data, env) { super <- r6data$super cls <- unique(super$classes$classname) if (length(cls) == 0) return() lines <- character() push <- function(...) lines <<- c(lines, ...) title <- if (length(cls) > 1) "Super classes" else "Super class" push(paste0("\\section{", title, "}{")) pkgs <- super$classes$package[match(cls, super$classes$classname)] has_topic <- purrr::map2_lgl(cls, pkgs, has_topic) path <- ifelse( has_topic, sprintf("\\code{\\link[%s:%s]{%s::%s}}", pkgs, cls, pkgs, cls), sprintf("\\code{%s::%s}", pkgs, cls) ) me <- sprintf("\\code{%s}", block$object$value$classname) push(paste(c(rev(path), me), collapse = " -> ")) push("}") lines } r6_fields <- function(block, r6data) { self <- r6data$self fields <- self$name[self$type == "field"] active <- self$name[self$type == "active"] tags <- purrr::keep( block$tags, function(t) t$tag == "field" && ! t$val$name %in% active ) labels <- gsub(",", ", ", map_chr(tags, c("val", "name"))) docd <- str_trim(unlist(strsplit(labels, ","))) # Check for missing fields miss <- setdiff(fields, docd) if (length(miss) > 0) { warn_roxy_block(block, "Undocumented R6 field{?s}: {miss}") } # Check for duplicate fields dup <- unique(docd[duplicated(docd)]) if (length(dup) > 0) { warn_roxy_block(block, "R6 field{?s} documented multiple times: {dup}") } # Check for extra fields xtra <- setdiff(docd, fields) if (length(xtra) > 0) { warn_roxy_block(block, "Unknown R6 field{?s}: {xtra}") } if (length(docd) == 0) return() # We keep the order of the documentation vals <- map_chr(tags, c("val", "description")) c("\\section{Public fields}{", "\\if{html}{\\out{
}}", "\\describe{", paste0("\\item{\\code{", labels, "}}{", vals, "}", collapse = "\n\n"), "}", "\\if{html}{\\out{
}}", "}" ) } r6_active_bindings <- function(block, r6data) { self <- r6data$self fields <- self$name[self$type == "field"] active <- self$name[self$type == "active"] tags <- purrr::keep( block$tags, function(t) t$tag == "field" && ! t$val$name %in% fields ) labels <- gsub(",", ", ", map_chr(tags, c("val", "name"))) docd <- str_trim(unlist(strsplit(labels, ","))) # Check for missing bindings miss <- setdiff(active, docd) if (length(miss) > 0) { warn_roxy_block(block, "Undocumented R6 active binding{?s}: {miss}") } # Check for duplicate bindings dup <- unique(docd[duplicated(docd)]) if (length(dup) > 0) { warn_roxy_block(block, "R6 active binding{?s} documented multiple times: {dup}") } if (length(docd) == 0) return() # We keep the order of the documentation vals <- map_chr(tags, c("val", "description")) c("\\section{Active bindings}{", "\\if{html}{\\out{
}}", "\\describe{", paste0("\\item{\\code{", labels, "}}{", vals, "}", collapse = "\n\n"), "}", "\\if{html}{\\out{
}}", "}" ) } r6_methods <- function(block, r6data, methods) { # And then the methods, if any if (nrow(methods) == 0) return() lines <- character() push <- function(...) lines <<- c(lines, ...) push("\\section{Methods}{") push(r6_method_list(block, methods)) push(r6_inherited_method_list(block, r6data)) for (i in seq_len(nrow(methods))) { push(r6_method_begin(block, methods[i,])) push(r6_method_description(block, methods[i,])) push(r6_method_usage(block, methods[i,])) push(r6_method_params(block, methods[i,])) push(r6_method_details(block, methods[i,])) push(r6_method_return(block, methods[i,])) push(r6_method_examples(block, methods[i,])) push(r6_method_end(block, methods[i,])) } push("}") lines } find_method_for_tag <- function(methods, tag) { w <- which( basename(methods$file) == basename(tag$file) & methods$line > tag$line )[1] methods$name[w] } # vectorized r6_show_name <- function(names) { ifelse(names == "initialize", "new", names) } r6_method_list <- function(block, methods) { nms <- r6_show_name(methods$name) c("\\subsection{Public methods}{", "\\itemize{", sprintf( "\\item \\href{#method-%s-%s}{\\code{%s$%s()}}", methods$class, nms, block$object$alias, nms ), "}", "}" ) } r6_inherited_method_list <- function(block, r6data) { super <- r6data$super if (is.null(super)) return() # drop methods that were shadowed in a subclass 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() } super_meth <- super_meth[rev(seq_len(nrow(super_meth))), ] details <- paste0( "Inherited methods" ) c("\\if{html}{\\out{", details, "
    ", sprintf( paste0( "
  • ", "", "%s::%s$%s()", "", "
  • " ), super_meth$package, super_meth$classname, super_meth$name, super_meth$package, super_meth$classname, super_meth$classname, super_meth$name, super_meth$package, super_meth$classname, super_meth$name ), "
", "", "}}" ) } r6_method_begin <- function(block, method) { nm <- r6_show_name(method$name) c( "\\if{html}{\\out{
}}", paste0("\\if{html}{\\out{}}"), paste0("\\if{latex}{\\out{\\hypertarget{method-", method$class, "-", nm, "}{}}}"), paste0("\\subsection{Method \\code{", nm, "()}}{") ) } r6_method_description <- function(block, method) { det <- purrr::keep(method$tags[[1]], function(t) t$tag == "description") # Add an empty line between @description tags, if there isn't one # there already txt <- map_chr(det, "val") c( sub("\n?\n?$", "\n\n", head(txt, -1)), utils::tail(txt, 1) ) } r6_method_usage <- function(block, method) { name <- paste0(block$object$alias, "$", r6_show_name(method$name)) fake <- paste(rep("X", nchar(name)), collapse = "") usage <- format(function_usage(fake, method$formals[[1]])) c( "\\subsection{Usage}{", paste0( "\\if{html}{\\out{
}}", "\\preformatted{", sub(paste0("^", fake), name, usage), "}", "\\if{html}{\\out{
}}" ), "}\n" ) } r6_method_details <- function(block, method) { det <- purrr::keep(method$tags[[1]], function(t) t$tag == "details") # Add an empty line between @details tags, if there isn't one # there already txt <- map_chr(det, "val") if (length(txt) == 0) return() c( "\\subsection{Details}{", sub("\n?\n?$", "\n\n", head(txt, -1)), utils::tail(txt, 1), "}\n" ) } r6_method_params <- function(block, method) { par <- purrr::keep(method$tags[[1]], function(t) t$tag == "param") nms <- gsub(",", ", ", map_chr(par, c("val", "name"))) # Each arg should appear exactly once mnames <- str_trim(unlist(strsplit(nms, ","))) dup <- unique(mnames[duplicated(mnames)]) for (m in dup) { warn_roxy_block(block, c( "Must use one @param for each argument", x = "${method$name}({m}) is documented multiple times" )) } # Now add the missing ones from the class fnames <- names(method$formals[[1]]) miss <- setdiff(fnames, mnames) is_in_cls <- map_lgl( block$tags, function(t) { !is.na(t$line) && t$line < block$line && t$tag == "param" && t$val$name %in% miss } ) par <- c(par, block$tags[is_in_cls]) # Check if anything is missing nms <- gsub(",", ", ", map_chr(par, c("val", "name"))) mnames <- str_trim(unlist(strsplit(nms, ","))) miss <- setdiff(fnames, mnames) for (m in miss) { warn_roxy_block(block, c( "Must use one @param for each argument", x = "${method$name}({m}) is not documented" )) } if (length(par) == 0) return() # Order them according to formals firstnames <- str_trim( map_chr(strsplit(map_chr(par, c("val", "name")), ","), 1) ) par <- par[order(match(firstnames, fnames))] val <- map_chr(par, c("val", "description")) nms <- gsub(",", ", ", map_chr(par, c("val", "name"))) # Ready to go c( "\\subsection{Arguments}{", "\\if{html}{\\out{
}}", "\\describe{", paste0("\\item{\\code{", nms, "}}{", val, "}", collapse = "\n\n"), "}", "\\if{html}{\\out{
}}", "}" ) } r6_method_return <- function(block, method) { ret <- purrr::keep(method$tags[[1]], function(t) t$tag == "return") if (length(ret) == 0) return() if (length(ret) > 1) { warn_roxy_block(block, "Must use one @return per R6 method") } ret <- ret[[1]] c( "\\subsection{Returns}{", ret$val, "}" ) } r6_method_examples <- function(block, method) { exa <- purrr::keep(method$tags[[1]], function(t) t$tag == "examples") if (length(exa) == 0) return() txt <- map_chr(exa, "val") c("\\subsection{Examples}{", paste0( "\\if{html}{\\out{
}}\n", "\\preformatted{", txt, "\n", "}\n", "\\if{html}{\\out{
}}\n", collapse = "\n" ), "}\n" ) } r6_method_end <- function(block, method) { c( "}" ) } r6_all_examples <- function(block, methods) { unlist(lapply( seq_len(nrow(methods)), function(i) { exa <- purrr::keep(methods$tags[[i]], function(t) t$tag == "examples") if (length(exa) == 0) return() name <- paste0(block$object$alias, "$", r6_show_name(methods$name[i])) c( "\n## ------------------------------------------------", paste0("## Method `", name, "`"), "## ------------------------------------------------\n", paste(map_chr(exa, "val"), collapse = "\n") ) })) } first_five <- function(x) { x <- encodeString(x, quote = "`") if (length(x) > 5) x <- c(x[1:5], "...") paste(x, collapse = ", ") } roxygen2/R/rd-eval.R0000644000176200001440000000133214312351064013703 0ustar liggesusersroxy_eval <- function(expr, env) { local_reproducible_output() eval(expr, env) } roxy_knit <- function(text, envir, options) { old_opts <- purrr::exec(opts_chunk$set, options) withr::defer(purrr::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, 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/utils.R0000644000176200001440000001023714553531033013520 0ustar liggesusersinternal_f <- function(p, f) { stopifnot(is.character(p), length(p) == 1) stopifnot(is.character(f), length(f) == 1) get(f, envir = asNamespace(p)) } "%||%" <- function(a, b) { if (length(a) > 0) a else b } subs <- matrix(ncol = 2, byrow = T, 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' )) subs[, 2] <- paste0("-", subs[, 2], "-") nice_name <- function(x) { x <- stringi::stri_replace_all_fixed(x, subs[, 1], subs[, 2], vectorize_all = FALSE) # Clean up any remaining x <- str_replace_all(x, "[^A-Za-z0-9_.-]+", "-") x <- str_replace_all(x, "-+", "-") x <- str_replace_all(x, "^-|-$", "") x <- str_replace_all(x, "^\\.", "dot-") x } write_if_different <- function(path, contents, href = 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 {.path {name}}", i = "It already exists and was not generated by 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)) return(FALSE) if (!str_detect(name, "^[a-zA-Z][a-zA-Z0-9_.-]*$")) { cli::cli_inform(c( x = "Skipping {.path {name}}", i = "Invalid file name" )) FALSE } else { if (!is.null(href)) { name <- cli::style_hyperlink(name, href) } cli::cli_inform("Writing {.path {name}}") writeBin(charToRaw(contents), path) TRUE } } same_contents <- function(path, contents) { if (length(contents) != 1) { cli::cli_abort("`contents` must be character(1)", .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, ...) { stopifnot(is.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 = "" ) } # 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 } is_syntactic <- function(x) make.names(x) == x has_quotes <- function(x) str_detect(x, "^(`|'|\").*\\1$") strip_quotes <- function(x) str_replace(x, "^(`|'|\")(.*)\\1$", "\\2") roxygen2/R/parse.R0000644000176200001440000000520314527172500013470 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 #' @keywords internal parse_package <- function(path = ".", env = env_package(path)) { files <- package_files(path) list_of_blocks <- lapply(files, tokenize_file) blocks <- purrr::flatten(list_of_blocks) if (!is.null(env)) { blocks <- lapply(blocks, block_set_env, env = env) } blocks <- order_blocks(blocks) blocks } #' @export #' @rdname parse_package 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 <- vapply(blocks, block_order, double(1)) blocks[order(ord)] } #' @export roxy_tag_parse.roxy_tag_order <- function(x) { tag_value(x) } roxygen2/R/zzz.R0000644000176200001440000000011214263172460013207 0ustar liggesusers.onLoad <- function(...) { as_character_rd <<- make_as_character_rd() } roxygen2/R/tag.R0000644000176200001440000000514514527172500013136 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. #' #' @keywords internal #' @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/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/roclet.R0000644000176200001440000000706114535664735013671 0ustar liggesusers#' Build a new roclet. #' #' To create a new roclet, you will need to create a constructor function #' that wraps `roclet`, and then implement the methods described below. #' #' @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 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 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. #' @keywords internal #' @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) { 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 #' @keywords internal 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/object-defaults.R0000644000176200001440000000632714547622571015453 0ustar liggesusersobject_defaults <- function(x, block) UseMethod("object_defaults") #' @export object_defaults.default <- function(x, block) list() #' @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.package <- function(x, block) { desc <- x$value$desc logo_path <- file.path(x$value$path, "man", "figures", "logo.png") if (file.exists(logo_path)) { fig <- "\\if{html}{\\figure{logo.png}{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.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/rd-family.R0000644000176200001440000000354514550054451014251 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 } 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, function(x) escape(x[1])) by_file <- map_chr(other_aliases[order_c(other_aliases_order)], function(x) { obj <- find_object(x[1], env) suffix <- if (is.function(obj$value)) "()" else "" paste0("\\code{\\link{", escape(x[1]), "}", suffix, "}") }) links <- paste(by_file, collapse = ",\n") seealso <- topics_process_family_prefix(family) topic$add(rd_section("seealso", paste0(seealso, "\n", links))) } } 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/markdown-link.R0000644000176200001440000001512114264615730015140 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. #' #' Starting from R 4.0.2-ish, explicit cross-package links to topics are not #' allowed, so for each such linked topic, we look up the linked file. #' #' These are the link references we add: #' ``` #' MARKDOWN LINK TEXT CODE RD #' -------- --------- ---- -- #' [fun()] fun() T \\link[=fun]{fun()} #' [obj] obj F \\link{obj} #' [pkg::fun()] pkg::fun() T \\link[pkg:file]{pkg::fun()} #' [pkg::obj] pkg::obj F \\link[pkg:file]{pkg::obj} #' [text][fun()] text F \\link[=fun]{text} #' [text][obj] text F \\link[=obj]{text} #' [text][pkg::fun()] text F \\link[pkg:file]{text} #' [text][pkg::obj] text F \\link[pkg:file]{text} #' [s4-class] s4 F \\linkS4class{s4} #' [pkg::s4-class] pkg::s4 F \\link[pkg:file]{pkg::s4} #' ``` #' #' The reference links will always look like `R:ref` for `[ref]` and #' `[text][ref]`. These are explicitly tested in `test-rd-markdown-links.R`. #' #' We add in a special `R:` marker to the URL. This way we don't #' pick 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 get_md_linkrefs <- function(text) { refs <- str_match_all( text, regex( comments = TRUE, " (?<=[^\\]\\\\]|^) # must not be preceded by ] or \ \\[([^\\]\\[]+)\\] # match anything inside of [] (?:\\[([^\\]\\[]+)\\])? # match optional second pair of [] (?=[^\\[{]|$) # must not be followed by [ or { " ) )[[1]] if (length(refs) == 0) { return(character()) } ## For the [fun] form the link text is the same as the destination. # Need to check both NA and "" for different versions of stringr refs[, 3] <- ifelse(is.na(refs[,3]) | refs[,3] == "", refs[, 2], refs[,3]) refs3encoded <- map_chr(refs[,3], URLencode) paste0("[", refs[, 3], "]: ", "R:", refs3encoded) } 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") } #' Parse a MarkDown link, to see if we should create an Rd link #' #' See the table above. #' #' @param destination string constant, the "url" of the link #' @param contents An XML node, containing the contents of the link. #' #' @noRd 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 (!all(xml_name(contents) %in% c("text", "softbreak", "linebreak"))) { incorrect <- setdiff(unique(xml_name(contents)), c("text", "softbreak", "linebreak")) warn_roxy_tag(state$tag, c( "markdown links must contain plain text", i = "Problematic link: {destination}" )) return("") } ## 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 obj (fun is with parens) ## `is_fun` is TRUE for fun(), FALSE for obj ## `obj` is fun or obj (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 ## `file` is the file name of the linked topic. thispkg <- roxy_meta_get("current_package") %||% "" is_code <- is_code || (grepl("[(][)]$", destination) && ! has_link_text) pkg <- str_match(destination, "^(.*)::")[1,2] pkg <- gsub("%", "\\\\%", pkg) if (!is.na(pkg) && pkg == thispkg) pkg <- NA_character_ fun <- utils::tail(strsplit(destination, "::", fixed = TRUE)[[1]], 1) fun <- gsub("%", "\\\\%", fun) is_fun <- grepl("[(][)]$", fun) obj <- sub("[(][)]$", "", fun) s4 <- str_detect(destination, "-class$") noclass <- str_match(fun, "^(.*)-class$")[1,2] file <- find_topic_filename(pkg, obj, state$tag) ## To understand this, look at the RD column of the table above if (!has_link_text) { paste0( if (is_code) "\\code{", if (s4 && is.na(pkg)) "\\linkS4class" else "\\link", if (is_fun || ! is.na(pkg)) "[", if (is_fun && is.na(pkg)) "=", if (! is.na(pkg)) paste0(pkg, ":"), if (is_fun || ! is.na(pkg)) paste0(if (is.na(pkg)) obj else file, "]"), "{", if (!is.na(pkg)) paste0(pkg, "::"), if (s4) noclass else fun, "}", if (is_code) "}" else "" ) } else { contents <- mdxml_link_text(contents, state) list( paste0( if (is_code) "\\code{", "\\link[", if (is.na(pkg)) "=" else paste0(pkg, ":"), if (is.na(pkg)) obj else file, "]{" ), contents, "}", if (is_code) "}" else "" ) } } #' 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/topo-sort.R0000644000176200001440000000252013540202201014305 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, "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-name-alias.R0000644000176200001440000000170314527240314014770 0ustar liggesusers#' @export roxy_tag_parse.roxy_tag_aliases <- function(x) tag_value(x) #' @export format.rd_section_alias <- function(x, ...) { x$value <- str_replace_all(x$value, fixed("%"), "\\%") 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 <- str_replace_all(x$value, fixed("%"), "\\%") format_first(x, ...) } topic_add_name_aliases <- function(topic, block, name) { tags <- block_get_tags(block, "aliases") if (length(tags) == 0) { aliases <- character() } else { vals <- map_chr(tags, "val") aliases <- unlist(str_split(vals, "\\s+")) } 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/roxygenize-setup.R0000644000176200001440000000462214527377516015741 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" ) } is_first <- first_time(path) if (is_first) { cli::cli_inform("First time using 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 = "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 roxygen2 is older than the version used with this package", i = "You have {.str {cur}} but you need {.str {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 roxygen2 7.0.0:", "* `%` is now escaped automatically in Markdown mode.", "Please carefully check .Rd files for changes" )) cli::cli_rule() } cli::cli_inform("Setting {.var RoxygenNote} to {.str {cur}}") desc::desc_set(RoxygenNote = cur, 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 = ".") { stringr::str_trim(desc::desc_get("RoxygenNote", path)[[1]]) } roxygen2/R/markdown-state.R0000644000176200001440000000224614262717326015331 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 <- purrr::map_chr(tags, "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/topics.R0000644000176200001440000000434214527377516013700 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, function(rd) rd$get_section(field)) lapply(compact(fields), "[[", "value") } )) roxygen2/R/object-from-call.R0000644000176200001440000002156714527377516015527 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.call(call)) { 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 (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 <- "data" } object(value, name, type) } # Parsers for individual calls -------------------------------------------- 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") } # 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 && str_detect(method, "::")) { generic <- strsplit(method, "::")[[1]][[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 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 = paste0(value@generic, ",", paste0(value@defined, collapse = ","), "-method"), s4class = paste0(value@className, "-class"), s4generic = value@generic, rcclass = paste0(value@className, "-class"), r6class = alias, rcmethod = value@name, s3generic = alias, s3method = alias, import = alias, `function` = alias, package = alias, data = alias, cli::cli_abort("Unsupported type {.str {type}}", .internal = TRUE) ) } 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/rd-find-link-files.R0000644000176200001440000000503114525720265015740 0ustar liggesusers #' Find the Rd file of a topic #' #' @param pkg Package to search in, or `NA` if no package was specified. #' If the same as the dev package, then we treat it as `NA`. #' @param topic Topic to search for. This is the escaped, so it is `"\%\%"` and #' not `"%%"`. #' @param tag The roxy tag object that contains the link. We use this for #' better warnings, that include the file name and line number (of the tag). #' @return String. File name to link to. #' #' @details #' If `pkg` is `NA` or the package being documented, we'll just leave the #' topic alone. #' #' If `pkg` is not `NA` and not the package being documented (the _dev_ #' package), then we need to be able to find the Rd file. If we can't, that's #' a warning and the link is left untouched. This typically happens when the #' linked package is not installed or cannot be loaded. #' #' @noRd find_topic_filename <- function(pkg, topic, tag = NULL) { if (is.na(pkg) || identical(roxy_meta_get("current_package"), pkg)) { topic } else { try_find_topic_in_package(pkg, topic, tag) } } #' Find a help topic in a package #' #' This is used by both `find_topic_filename()` and #' `format.rd_section_reexport()` that creates the re-exports page. The error #' messages are different for the two, so errors are not handled here. #' #' @param pkg Package name. This cannot be `NA`. #' @inheritParams find_topic_filename #' @return File name if the topic was found, `NA` if the package could be #' searched, but the topic was not found. Errors if the package cannot be #' searched. (Because it is not installed or cannot be loaded, etc.) #' #' @noRd find_topic_in_package <- function(pkg, topic) { # This is needed because we have the escaped text here, and parse_Rd will # un-escape it properly. on.exit(close(con), add = TRUE) con <- textConnection(topic) raw_topic <- str_trim(tools::parse_Rd(con)[[1]][1]) basename(utils::help((raw_topic), (pkg))[1]) } try_find_topic_in_package <- function(pkg, topic, tag) { path <- tryCatch( find_topic_in_package(pkg, topic), error = function(err) { warn_roxy_tag(tag, "refers to unavailable topic {pkg}::{topic}", parent = err) topic } ) if (is.na(path)) { warn_roxy_tag(tag, "refers to unavailable topic {pkg}::{topic}") topic } else { path } } resolve_qualified_link <- function(topic) { if (is_namespaced(topic)) { target <- str_split_fixed(topic, "::", n = 2) file <- find_topic_in_package(target[1], target[2]) paste0(target[1], ":", file) } else { paste0("=", topic) } } roxygen2/R/rd-usage.R0000644000176200001440000001202114535665174014076 0ustar liggesusers#' @export roxy_tag_parse.roxy_tag_usage <- function(x) { x <- tag_value(x) 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) { 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) } # 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 <- str_replace(name, fixed("<-"), "") 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) { str_detect(name, fixed("<-")) } is_infix_fun <- function(name) { ops <- c( "+", "-", "*", "^", "/", "==", ">", "<", "!=", "<=", ">=", "&", "|", "[[", "[", "$", ":", "::", ":::" ) str_detect(name, "^%.*%$") || name %in% ops } is_padded_infix_fun <- function(name) { ops <- c( "+", "-", "*", "/", "==", ">", "<", "!=", "<=", ">=", "&", "|" ) str_detect(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 (!str_detect(bare, "\n") && 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/safety.R0000644000176200001440000000072514262717326013664 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/rd-params.R0000644000176200001440000000446414531364526014262 0ustar liggesusers#' @export roxy_tag_parse.roxy_tag_param <- function(x) { tag_two_part(x, "an argument name", "a description") } #' @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/rd-include-rmd.R0000644000176200001440000000513614531364526015177 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" stopifnot(is.character(rmd), length(rmd) == 1, !is.na(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/options.R0000644000176200001440000001004414527136371014056 0ustar liggesusers#' Load roxygen2 options #' #' @description #' Options can be stored in the `Roxygen` field of the `DESCRIPTION`, or #' in `man/roxygen/meta.R`. In either case, the code is parsed and evaluated #' in a child of the base environment. 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 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_ vignette for details: `vignette("rd", package = "roxygen2")` #' #' * `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`: #' #' ``` #' Roxygen: list(markdown = TRUE, load = "installed") #' ``` #' #' Or if longer, you can put in `/man/roxygen/meta.R`: #' #' ``` #' list( #' markdown = TRUE, #' load = "installed" #' ) #' ``` #' #' @param base_path Path to package. #' @export #' @keywords internal load_options <- function(base_path = ".") { desc <- load_options_description(base_path) 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_, rd_family_title = list(), knitr_chunk_options = NULL, restrict_image_formats = TRUE ) 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_description <- function(base_path = ".") { desc_path <- file.path(base_path, "DESCRIPTION") dcf <- read.dcf(desc_path, fields = c("Roxygen", "Package")) 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 } 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 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/field.R0000644000176200001440000000433514526752211013450 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 #' @keywords internal 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("`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("") items <- paste0("\\item{\\code{", dt, "}}{", dd, "}", collapse = "\n\n") paste0("\\section{", name, "}{\n\n", "\\describe{\n", items, "\n}}\n" ) } roxygen2/R/rd-s4.R0000644000176200001440000000136614531364526013323 0ustar liggesusers#' @export roxy_tag_parse.roxy_tag_field <- function(x) { tag_two_part(x, "a field name", "a description") } #' @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") } #' @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/namespace.R0000644000176200001440000002626314553537636014341 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). #' #' 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) { #' } #' #' # You'll also often find global imports living in a file called #' # R/{package}-package.R. #' #' @@importFrom magrittr %>% #' #' @@import rlang #' NULL 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) 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, function(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, function(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_line(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 an 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 (!str_detect(x$val, "::")) { warn_roxy_tag(x, "must have form package::generic") return() } generic <- str_split(x$val, "::")[[1]] generic_re <- paste0("^", generic[[2]], "\\.") if (!str_detect(obj$alias, generic_re)) { warn_roxy_tag( x, "generic ({.str {generic[[2]]}}) doesn't match function ({.str {obj$alias}})", ) return() } class <- str_remove(obj$alias, generic_re) 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(blocks, function(block) block$object$value) undocumented <- methods[!methods %in% s3objects] 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-section.R0000644000176200001440000000222014262717326014430 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 <- str_split(x$val, ":", n = 2)[[1]] title <- str_split(pieces[1], "\n")[[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) { stopifnot(is.character(title), is.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/object-package.R0000644000176200001440000002472714527377516015247 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 <- paste0("\\url{", escape(strsplit(URL, ",\\s+")[[1]]), "}") 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 } 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) by_type <- split(desc, type) 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, " \\email{", paste(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 (length(x$comment) > 0) { desc <- paste0(desc, " (", x$comment, ")") } } 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. x <- str_replace_all(x, "<(doi|DOI):(.*?)>", function(match) { match <- str_remove_all(match, "^<(doi|DOI):|>$") paste0("\\doi{", escape(match), "}") }) # -> \url{http:XX.XXX} x <- str_replace_all(x, "<(http|https):\\/\\/(.*?)>", function(match) { match <- str_remove_all(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 <- str_replace_all(x, patt_arxiv, function(match) { match <- str_remove_all(match, "^<(arXiv:|arxiv:)|>$") # Special cases has . # See https://CRAN.R-project.org/package=ciccr # Extract arxiv id, split by space arxiv_id <- str_split_fixed(match, " ", n = 2)[, 1] paste0("\\href{https://arxiv.org/abs/", escape(arxiv_id), "}{arXiv:", match, "}") }) x } roxygen2/R/package_files.R0000644000176200001440000000205114262717326015140 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/tag-metadata.R0000644000176200001440000000500614520730024014702 0ustar liggesusers#' Access metadata about built-in tags #' #' @export #' @keywords internal 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, "name"), description = map_chr(meta, "description"), # \n not useful outside of RStudio template = sub("\n", "", map_chr(meta, "template", .default = "")), vignette = map_chr(meta, "vignette", .default = NA), recommend = map_lgl(meta, "recommend", .default = FALSE), stringsAsFactors = FALSE ) } yaml_path <- function() { system.file("roxygen2-tags.yml", package = "roxygen2") } tags_rd <- function(type) { tags <- tags_metadata() tags <- tags[tags$vignette == type & !is.na(tags$vignette), ] c( paste0("@name tags-", type), "@aliases", tags_rd_section(tags, "aliases"), "@description", paste0("Learn the full details in `vignette('", type, "')`."), "", if (any(tags$recommend)) c( "Key tags:", tags_rd_section(tags[tags$recommend, ], "description") ), if (any(!tags$recommend)) c( "Other less frequently used tags:", "", tags_rd_section(tags[!tags$recommend, ], "description") ), "@usage", tags_rd_section(tags, "usage") ) } tags_rd_section <- function(tags, section) { if (nrow(tags) == 0) return() switch(section, aliases = paste0(" @", tags$tag), usage = paste0("#' @", tags$tag, tags$template), description = paste0("* `@", tags$tag, tags$template, "`: ", tags$description) ) } #' Tags for documenting functions #' #' @eval tags_rd("rd") #' @family documentation tags NULL #' Tags for documenting datasets and classes #' #' @eval tags_rd("rd-other") #' @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/roxygenize.R0000644000176200001440000000547514527223374014602 0ustar liggesusers#' Process a package with the Rd, namespace and collate roclets #' #' This is the workhorse function that uses roclets, the built-in document #' transformation functions, to build all documentation for a package. See #' the documentation for the individual roclets, [rd_roclet()], #' [namespace_roclet()], and for [update_collate()], #' for more 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 [load] for #' details. #' #' @param package.dir Location of package top level directory. Default is #' working directory. #' @param roclets Character vector of roclet names to use with package. #' The default, `NULL`, uses the roxygen `roclets` option, #' which defaults to `c("collate", "namespace", "rd")`. #' @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) { base_path <- normalizePath(package.dir) is_first <- roxygen_setup(base_path) 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 a 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 load_code <- find_load_strategy(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) { purrr::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 <- purrr::map2( roclets, results, roclet_output, base_path = base_path, is_first = is_first ) invisible(out) } #' @rdname roxygenize #' @export roxygenise <- roxygenize roxygen2/R/utils-io.R0000644000176200001440000000140214262717326014127 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/object-r6.R0000644000176200001440000001134114262717326014160 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 `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( stringsAsFactors = FALSE, 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( stringsAsFactors = FALSE, 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( stringsAsFactors = FALSE, 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( stringsAsFactors = FALSE, 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( stringsAsFactors = FALSE, 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( stringsAsFactors = FALSE, package = rep(pkg, length(names)), classname = rep(classname , length(names)), type = types, name = names ), super_data$members ) list(classes = cls, members = mth) } roxygen2/R/block.R0000644000176200001440000001623214527172500013454 0ustar liggesusers#' Blocks #' #' @description #' A `roxy_block` represents a single roxygen2 block. #' #' The `block_*` functions provide a few helpers for common operations: #' * `block_has_tag(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 #' @keywords internal #' @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)) stopifnot(is.character(file), length(file) == 1) stopifnot(is.integer(line), length(line) == 1) 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, "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, "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) { tag_value(x) } parse_description <- function(tags) { if (length(tags) == 0) { return(tags) } tag_names <- vapply(tags, `[[`, "tag", FUN.VALUE = character(1)) if (tag_names[1] != "") { return(tags) } intro <- tags[[1]] intro$val <- str_trim(intro$raw) if (intro$val == "") { return(tags[-1]) } tags <- tags[-1] tag_names <- tag_names[-1] paragraphs <- str_split(intro$val, fixed('\n\n'))[[1]] lines <- str_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], "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.R0000644000176200001440000000161314262717326013562 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) #' @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-inherit.R0000644000176200001440000003141314536342212014424 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_value(x) #' @export roxy_tag_rd.roxy_tag_inheritParams <- function(x, base_path, env) { rd_section_inherit(x$val, list("params")) } #' @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) { stopifnot(is.character(source), 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), function(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) { stopifnot(is.character(source), is.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) { stopifnot(is.character(source), is.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)) } # Process inheritance ----------------------------------------------------- topics_process_inherit <- function(topics, env) { inherits <- function(type) { function(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) { inherited_params <- find_params(inheritor, topics, source = topic$get_name()) 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() # Need to find formals for each source funs <- lapply(inheritors$source, function(x) eval(parse(text = x), envir = env)) args <- map2(funs, inheritors$args, select_args_text, topic = topic) # Then pull out the ones we need docs <- lapply(inheritors$source, find_params, topics = topics) arg_matches <- function(args, docs) { match <- map_lgl(docs, function(x) all(x$name %in% args)) matched <- docs[match] setNames( lapply(matched, "[[", "value"), map_chr(matched, function(x) paste(x$name, 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] # Build the Rd # (1) Link to function(s) that was inherited from src <- inheritors$source dest <- map_chr(src, resolve_qualified_link) from <- paste0("\\code{\\link[", dest, "]{", src, "}}", collapse = ", ") # (2) Show each inherited argument arg_names <- paste0("\\code{", names(docs_selected), "}") args <- paste0(" \\item{", arg_names, "}{", docs_selected, "}", collapse = "\n") 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, 1) else documented <- unlist(documented) } documented[documented == "\\dots"] <- "..." documented } find_params <- function(name, topics, source) { topic <- get_rd(name, topics, source = source) if (is.null(topic)) { return() } params <- topic_params(topic) if (is.null(params)) return() param_names <- str_trim(names(params)) param_names[param_names == "\\dots"] <- "..." Map(list, name = strsplit(param_names, ",\\s*"), 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, function(y) rd2text(y[[2]], attr(x, "package"))) params <- map_chr(items, function(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, 1), rd2text, package = attr(topic, "package")) contents <- map_chr(map(tag, 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 str_trim(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 (length(x) > 0) { x[] <- map(x, tweak_links, package = package) } } x } # Find info in Rd or topic ------------------------------------------------ get_rd <- function(name, topics, source) { 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) } else { # Current package rd_name <- topics$find_filename(name) if (identical(rd_name, NA_character_)) { warn_roxy_topic(source, "@inherits failed to find topic {.str {name}}") } topics$get(rd_name) } } get_rd_from_help <- function(package, alias, source) { if (!is_installed(package)) { warn_roxy_topic(source, "@inherits failed because {.pkg {package}} is not installed") return() } help <- utils::help((alias), (package)) if (length(help) == 0) { warn_roxy_topic(source, "@inherits failed to find topic {package}::{alias}") return() } out <- internal_f("utils", ".getHelpFile")(help) attr(out, "package") <- package out } roxygen2/R/roxygen2-package.R0000644000176200001440000000143114264616201015522 0ustar liggesusers#' @useDynLib roxygen2, .registration=TRUE #' @keywords internal #' @import rlang "_PACKAGE" ## usethis namespace: start #' @importFrom knitr knit #' @importFrom knitr opts_chunk #' @importFrom purrr keep #' @importFrom purrr map #' @importFrom purrr map_chr #' @importFrom purrr map_int #' @importFrom purrr map_lgl #' @importFrom purrr map2 #' @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/rd-markdown.R0000644000176200001440000000727114262717326014621 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 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 (str_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], ~ list(title = .x, content = .y)) ) if (value[[1]] == "") { name <- name[-1] value <- value[-1] } } map2(name, value, rd_section) } roxygen2/R/cpp11.R0000644000176200001440000000125014527141272013302 0ustar liggesusers# Generated by cpp11: do not edit by hand escapeExamples <- function(x) { .Call(`_roxygen2_escapeExamples`, x) } findEndOfTag <- function(string, is_code) { .Call(`_roxygen2_findEndOfTag`, string, is_code) } 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/rd-examples.R0000644000176200001440000000514514527170577014620 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] tryCatch( suppressWarnings(parse(text = condition)), error = function(err) { warn_roxy_tag(x, "condition failed to parse", parent = err) } ) dontshow <- paste0( "\\dontshow{if (", condition, ") (if (getRversion() >= \"3.4\") withAutoprint else force)(\\{ # examplesIf}" ) x$raw <- paste( c(dontshow, lines[-1], "\\dontshow{\\}) # examplesIf}"), collapse = "\n" ) tag_examples(x) } #' @export roxy_tag_parse.roxy_tag_example <- function(x) { x <- tag_value(x) nl <- str_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)) } roxygen2/R/tag-parser.R0000644000176200001440000001457714531364526014447 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 #' @keywords internal NULL #' @export #' @rdname tag_parsers tag_value <- function(x) { if (str_trim(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 { x$val <- str_trim(x$raw) 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 (str_trim(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 <- str_split(str_trim(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 (str_trim(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 <- str_count(x$raw, "\\s+") if (n > 1) { warn_roxy_tag(x, "must have only one argument, not {n}") NULL } else { x$val <- str_trim(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) { if (str_trim(x$raw) == "") { if (!required) { warn_roxy_tag(x, "requires {first}") } else { warn_roxy_tag(x, "requires two parts: {first} and {second}") } NULL } else if (required && !str_detect(x$raw, "[[:space:]]+")) { 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 { pieces <- str_split_fixed(str_trim(x$raw), "[[:space:]]+", 2) pieces[is.na(pieces)] <- "" if (markdown) { pieces[,2] <- markdown_if_active(pieces[,2], x) } x$val <- list( pieces[, 1], trim_docstring(pieces[,2]) ) names(x$val) <- c("name", "description") x } } #' @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) { if (!rdComplete(x$raw, is_code = FALSE)) { warn_roxy_tag(x, "has mismatched braces or quotes") return(NULL) } words <- str_split(str_trim(x$raw), "\\s+")[[1]] 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) { x$val <- str_trim(x$raw) n_lines <- str_count(x$val, "\n") if (n_lines >= 1) { first_line <- str_split(x$val, "\n")[[1]][[1]] warn_roxy_tag(x, c( "must be a single line, not {n_lines + 1}", i = "The first line is {.str {first_line}}" )) NULL } else if (!rdComplete(x$raw, is_code = FALSE)) { warn_roxy_tag(x, "has mismatched braces or quotes") NULL } else { x$val <- str_split(x$val, "\\s+")[[1]] x } } #' @export #' @rdname tag_parsers tag_toggle <- function(x) { x$val <- str_trim(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 (str_trim(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 (str_trim(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 (str_trim(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 (str_trim(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]] <- str_trim(out[[i]]) } } out } else { if (!rdComplete(text, is_code = FALSE)) { warn_roxy_tag(tag, "has mismatched braces or quotes") "" } else { str_trim(text) } } } roxygen2/R/rd-backref.R0000644000176200001440000000113214224315005014344 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 <- stringi::stri_wrap( paste0("Please edit documentation in ", paste(filename, collapse = ", ")), initial = "% ", prefix = "% ", width = 80, whitespace_only = TRUE ) paste0(paste0(lines, collapse = "\n")) } roxygen2/R/rd-template.R0000644000176200001440000000320614531364526014603 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") } process_templates <- function(block, base_path) { tags <- block_get_tags(block, "template") if (length(tags) == 0) return(block) templates <- map_chr(tags, "val") paths <- map_chr(templates, template_find, base_path = base_path) var_tags <- block_get_tags(block, "templateVar") vars <- set_names( map(var_tags, c("val", "description")), map_chr(var_tags, c("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/utils-warn.R0000644000176200001440000000262514527410325014470 0ustar liggesusers #' @export #' @rdname roxy_tag warn_roxy_tag <- function(tag, message, parent = NULL, envir = parent.frame()) { 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]] 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/rd-simple.R0000644000176200001440000000204513544473137014263 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_value(x) #' @export roxy_tag_rd.roxy_tag_keywords <- function(x, base_path, env) { rd_section("keyword", str_split(x$val, "\\s+")[[1]]) } #' @export format.rd_section_keyword <- function(x, ...) { format_rd(x, ...) } roxygen2/R/topic.R0000644000176200001440000001161714536371735013515 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, function(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/object-s3.R0000644000176200001440000000462114536371735014165 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()) { pieces <- str_split(name, fixed("."))[[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) { stopifnot(is.function(f)) stopifnot(length(method) == 2, is.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/collate.R0000644000176200001440000000621014527172500014000 0ustar liggesusers#' Update Collate field in DESCRIPTION #' #' @description #' By default, R loads files in alphabetical order. Unfortunately not every #' alphabet puts letters in the same order, so you can't rely on alphabetic #' ordering if you need one file loaded before another. (This usually doesn't #' matter but is important for S4, where you need to make sure that classes are #' loaded before subclasses and generics are defined before methods.). #' You can override the default alphabetical ordering with `@include before.R`, #' which specify that `before.R` must be loaded before the current file. #' #' Generally, you will not need to run this function yourself; it should be #' run automatically by any package that needs to load your R files in #' collation order. #' #' @section Collate: #' This is not a roclet because roclets need the values of objects in a package, #' and those values can not be generated unless you've sourced the files, #' and you can't source the files unless you know the correct order. #' #' If there are no `@include` tags, roxygen2 will leave collate 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. #' #' @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 statement #' #' 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) { 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 = "/") str_replace(path, fixed(paste0(base, "/")), "") } roxygen2/R/object-import.R0000644000176200001440000000275514262717326015154 0ustar liggesusers# Re-export ---------------------------------------------------------------- rd_section_reexport <- function(pkg, fun, file) { stopifnot(is.character(pkg), is.character(fun), is.character(file)) stopifnot(length(pkg) == length(fun)) rd_section("reexport", list(pkg = pkg, fun = fun, file = file)) } #' @export roxy_tag_rd.roxy_tag_.reexport <- function(x, base_path, env) { file <- find_topic_filename(x$val$pkg, x$val$fun, tag = x) rd_section_reexport(x$val$pkg, x$val$fun, file) } #' @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), c(x$value$file, y$value$file) ) } #' @export format.rd_section_reexport <- function(x, ...) { info <- data.frame( pkg = x$value$pkg, fun = x$value$fun, file = x$value$file, stringsAsFactors = FALSE ) pkgs <- split(info, x$value$pkg) pkg_links <- map(pkgs, function(pkg) { pkg <- pkg[order(pkg$fun), ] links <- paste0( "\\code{\\link[", pkg$pkg, ifelse(pkg$file == pkg$fun, "", paste0(":", pkg$file)), "]{", escape(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" ) } roxygen2/R/object-format.R0000644000176200001440000000243014264614443015116 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{", 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)) } } # 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/utils-rd.R0000644000176200001440000000540314525720265014130 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("\\", "\\\\", x, fixed = TRUE, useBytes = TRUE) x2 <- gsub("%", "\\%", 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 <- str_trim(c(...)) } paste0("\\", field, paste0("{", values, "}", collapse = "")) } # Input ------------------------------------------------------------------- get_tags <- function(rd, tag) { Filter(function(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 <- purrr::detect_index(body, ~ 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/object-rc.R0000644000176200001440000000476314262717326014247 0ustar liggesusers#' @export roxy_tag_rd.roxy_tag_.methods <- function(x, base_path, env) { desc <- lapply(x$val, function(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) { stopifnot(is.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/markdown-escaping.R0000644000176200001440000001601514265257400015774 0ustar liggesusers#' Escape Rd markup, to avoid interpreting it as markdown #' #' This is needed, if we want to stay compatible with #' existing markup, even if markdown mode is switched on. #' Fragile Rd tags (tags that may contain markup that #' can be picked up by the markdown parser), are replaced #' by placeholders. After the markdown to Rd conversion #' is done, the original text is put back in place of the #' placeholders. #' #' The list of protected Rd tags is 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. #' #' See also `roclet-rd.R` for the list of tags that #' uses the markdown-enabled parser. Some tags, e.g. #' `@aliases`, `@backref`, etc. only use the #' standard Roxygen parser. #' #' @param text Input text. Potentially contains Rd and/or #' markdown markup. #' @return For `escape_rd_for_md`: #' A \dQuote{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. #' @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" )) #' @description #' It puts back the protected fragile Rd commands into #' the text after the markdown parsing. #' #' @param rd_text The markdown parsed and interpreted text. #' @param esc_text The original escaped text from #' `escape_rd_for_md()`. #' @return For `unescape_rd_for_md`: Rd text. #' @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 <- str_sub(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 <- str_sub(text, tags$end[i], text_len) findEndOfTag(tag_plus, is_code = FALSE) + 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 tag_pos <- str_locate_all(text, "\\\\[a-zA-Z][a-zA-Z0-9]*")[[1]] data.frame( stringsAsFactors = FALSE, tag = str_sub(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 <- str_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 str_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_sub(str, repl$start[i], repl$argend[i]) <- new_text ## 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("\\", "\\\\", text, fixed = TRUE) # De-dup escaping used to avoid [] creating a link text <- gsub("\\\\[", "\\[", text, fixed = TRUE) text <- gsub("\\\\]", "\\]", text, fixed = TRUE) text } roxygen2/R/markdown.R0000644000176200001440000003504714525720741014215 0ustar liggesusersmarkdown <- function(text, tag = NULL, sections = FALSE) { tag <- tag %||% list(file = NA, line = NA) expanded_text <- tryCatch( markdown_pass1(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) str_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 <- str_split(text, fixed("\n"))[[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 (str_sub(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. indent <- nchar(str_extract(line, "^[ ]+")) if (str_sub(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") str_sub(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, 1), start_column = map_int(nums, 2), end_line = map_int(nums, 3), end_column = map_int(nums, 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 ) } str_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(str_split(text, fixed("\n"))[[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 <- str_trim(paste0(out, collapse = "")) if (state$has_sections) { secs <- strsplit(rd, state$section_tag, fixed = TRUE)[[1]] %||% "" titles <- c("", state$titles) rd <- structure(str_trim(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"), hrule = 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) # See escaping details at # https://cran.rstudio.com/doc/manuals/r-devel/R-exts.html#Insertions if (can_parse(code) || code %in% special) { 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, ~ 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)) { paste0(link, collapse = "") } else if (dest == "" || dest == xml_text(xml)) { paste0("\\url{", escape_comment(xml_text(xml)), "}") } else { paste0( "\\href{", escape_comment(dest), "}", "{", 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/rd-describe-in.R0000644000176200001440000001600514531364526015155 0ustar liggesusers#' @export roxy_tag_parse.roxy_tag_describeIn <- function(x) { if (!is.na(x$raw) && !str_detect(x$raw, "[[:space:]]+")) { 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") } } 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 = "") { stopifnot(is_string(name)) stopifnot(is_character(desc)) rlang::arg_match(extends) stopifnot(is_string(generic)) stopifnot(is_string(class)) data <- data.frame( name = name, desc = desc, extends = extends, generic = generic, class = class, stringsAsFactors = FALSE ) 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 <- purrr::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/vignette.R0000644000176200001440000000447614525234127014220 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/NEWS.md0000644000176200001440000016016314553543134013143 0ustar liggesusers# 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")` 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")` 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")` 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/MD50000644000176200001440000004345114553554653012365 0ustar liggesusers3d353d67ef38e4c52646a01cfbb840b6 *DESCRIPTION b936b80d5074f0d9f8f3414089e14957 *LICENSE dc8100f26ea157a6c3c77442846d4e04 *NAMESPACE 9bad60b8d39de3e080d63f0eb4a423c1 *NEWS.md ce9818e1e922391eac39b5c1bcedb61f *R/block.R 16dd17f1621883c2efcdebc18b5a9b77 *R/collate.R 1642db96ca01adbe0645eac51d44c7f7 *R/cpp11.R e6eae7d8c8de2829e5e0f2f0bb5f804c *R/field.R 7ad1e7c28ac184c6daf8c9fca76a3bfe *R/load.R ed56cd575ac5a6b99bb342cca07c1e38 *R/markdown-escaping.R 64a25d58741e32f9dbb7af3f13192a17 *R/markdown-link.R b6340678aee76596b7a06cdce4a538d5 *R/markdown-state.R 28bfb426ec402f4c3bdf714cd5fce0ec *R/markdown.R d1d9cdb40835dcc79b4d07ae872bcf10 *R/namespace.R 9634d2cc7be4f3d88421db7113486c5e *R/object-defaults.R 6fdf1c02cad4179f08806666f8450c6a *R/object-format.R 4f52ea184d5e7dbe615abc7e4256e5e0 *R/object-from-call.R e24bc4ef75f51bd592e96dcfd1abf8ac *R/object-import.R babccd08cc8cf773ec6e50c15d9f6e6a *R/object-package.R d4be798239db89bb1342676a0ebebc9d *R/object-r6.R a49a7da3027fc0fb0b815a1fe95f3d1c *R/object-rc.R af5cb8aacda503b81ccf2175a50161b9 *R/object-s3.R 31e21f2df4872e42e35540aea66f8a66 *R/options.R eb5e52cfeaff45d394903350fe29fc56 *R/package_files.R 2ae7355a3c121858b3190b47ad4830d0 *R/parse.R 87c69905a0d8bfad17e7b4fe8e1d7eb7 *R/rd-backref.R 7c723443bdd718b21d0614893b30641e *R/rd-describe-in.R 1811728cd2e2ebea33b250ba24053e69 *R/rd-eval.R ca4888f86e61f326bf95aea9acdc6666 *R/rd-examples.R 91663f5b42aa43ec2935b85754ab5891 *R/rd-family.R da36d181214865878b734e8092bc9e4e *R/rd-find-link-files.R 99685a6859a9464c697b5d472b1155fc *R/rd-include-rmd.R d544594036f5ced652d4112224e4fa69 *R/rd-inherit.R 0f42e3c2cc2220477d93de8ecbe7de06 *R/rd-markdown.R 2292d47ed4931d532a7de376e6cf2798 *R/rd-name-alias.R 8ea7c416cc31fa2f4d8887ae0a7e6c7b *R/rd-params.R d7638d3fcba3c58cb13b6042954f9ab2 *R/rd-r6.R b0a27b502d5b3b7d6e1104ba61ad9028 *R/rd-raw.R cbfa7965dc6988eb718339e859db397f *R/rd-s4.R 31ef827dada1ec61173ab54f31a97790 *R/rd-section.R 7dc37a98ee9b55dc82ef7cd1b6276a07 *R/rd-simple.R c3ed7920bf046812e986b8ab8441c6a1 *R/rd-template.R 020f403e2d39a4ce8107e63eac66ba67 *R/rd-usage.R 4ab31db4f02ff86185852200f891de46 *R/rd.R e4e070629747aec0159dc2830e2f9fca *R/roclet.R b9babc2ab0d62d89c2aefa01f9c4712a *R/roxygen2-package.R 2fdf1220cdfbdfca61573ec73f015ebe *R/roxygenize-setup.R 566d1b002e6b7cdd2b7ce04b5aeccdb4 *R/roxygenize.R 00ed4c8678e9067f3acefdcdca17cf72 *R/safety.R e679aaac95bf38b10fd0fbdbccd4bd05 *R/select-args.R c2eb19278f05648def3833b31307c7c9 *R/tag-metadata.R 2a03b67d6591cc647488dfbfb576d18e *R/tag-parser.R 19a3b5b1bc03857e791656ad75a3ce0d *R/tag.R 873d809f2a89732901cd72ec0f0e7b05 *R/tokenize.R 1458195178dc7e53937b58d32f2fbf29 *R/topic.R 4625008db154e839be31b004e697849a *R/topics.R 3ab3ddcc81eb5436dcc3e85f3455e5a7 *R/topo-sort.R bd19600f42f96971c72ea9bbffe733a4 *R/util-locale.R cc381169cc11714a489a5186869bc25d *R/utils-io.R 59a162a7dd1e8bedd00cbccb797047fc *R/utils-rd.R 2c063e44ee4f7d03709a74234604c86e *R/utils-warn.R a84f21a5cdc4fb6b0a56817acc57596d *R/utils.R bb3a210cfdfd4d80c0804689d9b6af93 *R/vignette.R 4df50b4923df32583a121ab3f3c4c368 *R/zzz.R bd9a98e6e37aaf48bd0be19231e90063 *README.md edcde08273fb30e1c950bd39341209c6 *build/vignette.rds 3fda61df63f442cbbd347a02d370c385 *inst/doc/extending.R fe66061e9656b8d29fd2a80400d8fe5e *inst/doc/extending.Rmd 2585c1876fd1b1b2d3c81ede2556c32e *inst/doc/extending.html 2ee228c545a87f0e1aba75838842fbd3 *inst/doc/index-crossref.R 6bfc9797a563b2301f76629478a838ea *inst/doc/index-crossref.Rmd 0eb3bb3be2ed4dc3e590382c4a38f4d2 *inst/doc/index-crossref.html 88788f70ef860beace52a08de2969f8c *inst/doc/namespace.R 39345ccabe8d89b3420cbdd43caa6874 *inst/doc/namespace.Rmd 9a4a3f95a062127ffce90be3130ada4f *inst/doc/namespace.html f1f54f4b2d5c7ca1387c4a499e3dd282 *inst/doc/rd-formatting.R c2395ab3fe51ff8827359a1b45701d7c *inst/doc/rd-formatting.Rmd c6a89ae88b2ac64aa7d018bd01a328aa *inst/doc/rd-formatting.html 57974be9faa1b073b90ea9d5636ae3d7 *inst/doc/rd-other.R d2aa03e3a856ccf35ca448ac92cfe328 *inst/doc/rd-other.Rmd aa5c1cb144435b27dd8fa9bccbe62a9f *inst/doc/rd-other.html d4ce12ce5fd25a289e42b24070a48ba9 *inst/doc/rd.R fd3c1a6eae2e9f84719e392f125aa645 *inst/doc/rd.Rmd bc0f59e31caf8a124182f4980e521ae6 *inst/doc/rd.html 2b156964d0e619d3e4d4736d9db02da7 *inst/doc/reuse.R ab9f17dad55f22fab006d240d4d506fb *inst/doc/reuse.Rmd 633ca8cd8761371a4ee04704646b687a *inst/doc/reuse.html 4c787d164d1f4197f462845acffe17fb *inst/doc/roxygen2.R 65b1ea1b8593c2d2f72ed255b56c74c3 *inst/doc/roxygen2.Rmd 64c982cb49cdd18fbb76ce403058ccab *inst/doc/roxygen2.html e14da3b8390733a5cc6c259161b9e5af *inst/roxygen2-tags.yml fd5df6bd65196d39e07cc546e196b790 *man/RoxyTopic.Rd 52664c282da032ca2717fa97076ede2c *man/double_escape_md.Rd 7a90f135e7865de509a8d7518e3938f8 *man/escape_examples.Rd 495cd86c483b2786a06bd04931ba42b3 *man/figures/logo.png 2970c99d6a9942eada933591d0006c3f *man/figures/test-figure-1.png ebfe1045186354a59f641c3f0c6127fd *man/is_s3_generic.Rd a69627fbb9aad7681c7003f71274d5d7 *man/load.Rd ec8264ebc83754d32137a89e589236dd *man/load_options.Rd fe342bc1885bc7111186179abb8ab5a0 *man/markdown-internals.Rd cc8376ad8716f3038d73527b561c2af7 *man/markdown-test.Rd 649145201f9e37c8ad66146f8bff71e3 *man/markdown_pass1.Rd 8a397d020ee8af289a6b581b586254e3 *man/namespace_roclet.Rd 83620c541a0719217c5c7e92b191d5e1 *man/object.Rd ddef0c00ca079d6ef0b46a8c80fa1737 *man/object_format.Rd 53e4d4decf9174c9b14edf8f859bf940 *man/parse_package.Rd cdf9fac8cb311f42d0af0dd44b8a38fb *man/rd_roclet.Rd e143910d6e854b195b567cce4062c9e6 *man/rd_section.Rd 392e5e0917a57d164676a650c9ca8272 *man/roc_proc_text.Rd 3b638616f3d31b277acb7d6718aaa9c8 *man/roclet.Rd 4bf772a00a29e470532cd70b895ba9cb *man/roclet_find.Rd f4ad9c46a39ff54d89f7d8b8769b1ccc *man/roxy_block.Rd e7f15b80efbe8e709674bb62a2887df3 *man/roxy_tag.Rd cbaeb4b8f5c628adf2d57b085db1c291 *man/roxy_tag_rd.Rd 395c368dd9072412973bdc5dcb565270 *man/roxygen2-package.Rd 3a5faaaf787e2646188a2d7b1aac1e5b *man/roxygenize.Rd a1f680309aab8e1167f739462d822c10 *man/tag_parsers.Rd a85df030d2b7e3b113f00773840f0da7 *man/tags-index-crossref.Rd c9f263d6eaba226696a6ef635e1233d0 *man/tags-namespace.Rd 7210931562523b652e07741ff810a4a3 *man/tags-rd-formatting.Rd b98ce91b335f49f86019709e8f982097 *man/tags-rd-other.Rd 5754ca5cd0b59fb66cf5a4710e5e57a4 *man/tags-rd.Rd ba66c130e56806b1229fea9dcd2258e2 *man/tags-reuse.Rd dec3c0f402bca3f9387a4b222b9bd5ae *man/tags_list.Rd 343edc77b4cc1a1c8e758aabded5fe4a *man/update_collate.Rd 6ede81b72e63d439a987644f66e62786 *man/vignette_roclet.Rd 26cb087013cca3417e2894977b5eda2f *src/cpp11.cpp fecfbfb6f902fe7c544a801e5d94f43e *src/escapeExamples.cpp c2a88da4a40f1916edc96a5a393a1eb5 *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 688eacffdd8c5d46730d2104e5b597d6 *tests/testthat/_snaps/collate.md 8637411f59d69aaaf20e8c611b7d25ec *tests/testthat/_snaps/markdown-code.md fafcead6b4b1970f1e16ba3efa9ab398 *tests/testthat/_snaps/markdown-link.md 5d011e2db10a5b5d687b7a71680f8a2e *tests/testthat/_snaps/markdown-state.md 9ea9626186795b239d3587b7332ae1d3 *tests/testthat/_snaps/markdown.md 42124ab1c54d1d9e0ee5b7bc60ee4d55 *tests/testthat/_snaps/namespace.md 7a1a19c017087afcbdc90ca1dc0caaeb *tests/testthat/_snaps/object-format.md 9d5b5eeaa1d9a97687a8fd9fe5b56a18 *tests/testthat/_snaps/object-from-call.md bc5f27aa60db311358a0b116b1531303 *tests/testthat/_snaps/object-import.md 07e719f82a0b76c69b5506d3c98b6980 *tests/testthat/_snaps/object-package.md 095c17161c66d3cc9c8d347ea2bf15fb *tests/testthat/_snaps/object-r6.md 9a24e2647f2de3184b7137bc022dedd1 *tests/testthat/_snaps/rd-describe-in.md f715d5807304965ab0e17c3c5c9c399a *tests/testthat/_snaps/rd-examples.md 0c71f4374eb191331536ef62a6d859c4 *tests/testthat/_snaps/rd-family.md ac71e01944cb838a82a0d06312f866f0 *tests/testthat/_snaps/rd-find-link-files.md cfd2e8f3728827357ebea4688995d6a1 *tests/testthat/_snaps/rd-include-rmd.md b6f7a5bcc0116cef0ff247dc9c7a809a *tests/testthat/_snaps/rd-inherit.md 53e47f8ecb31eb7346377c3bd0f5c3f3 *tests/testthat/_snaps/rd-markdown.md cae6d19fe83f3b9b5cc25cc6e1118095 *tests/testthat/_snaps/rd-params.md a433afa1c65b58800a38c068de5cbdf1 *tests/testthat/_snaps/rd-r6.md 50648766181aa026088a46b1969f1d58 *tests/testthat/_snaps/rd-raw.md 4c093a0c2b03c611458676adf3ed48df *tests/testthat/_snaps/rd-s4.md 8ee8dab346e24bb59229e45001c73428 *tests/testthat/_snaps/rd-section.md ad4112ad799fcc950a62bbbaf92564d9 *tests/testthat/_snaps/rd-template.md 8418cbaabe49facc175ba169f1fb0c12 *tests/testthat/_snaps/rd-usage.md f44b3cbaa01d3e374622cb7b3fbb193a *tests/testthat/_snaps/rd.md 4b17a4370dbc4f3f859f20830a8fa340 *tests/testthat/_snaps/roxygenize-setup.md 2bed32627a994f55e06d4c2c87086eb1 *tests/testthat/_snaps/roxygenize.md f321b8f302e79bb27ec463e80cb2e5d8 *tests/testthat/_snaps/select-args.md 774aff2c29ec1d35387d00d1dc827be3 *tests/testthat/_snaps/tag-parser.md 9648a2fa905626ac83e5c691b270f3e9 *tests/testthat/_snaps/tag.md 196a134a7b778498e4e8fdd8bd183754 *tests/testthat/_snaps/topics.md b16397976928292996a07f88d2fd06e4 *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 dc392fff6f4c4a1035da6e70b228627c *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 c6335cc2a7ba870fdc7e402c38cf991d *tests/testthat/roxygen-block-1.R 821cbd88b6d2399710bd0fdaa7b26243 *tests/testthat/roxygen-block-2.R 0b63055601e34b1fca004e36cafd26de *tests/testthat/roxygen-block-3-A.Rd 03a8c351917a08941e0d878282af8384 *tests/testthat/roxygen-block-3-B.Rd cc4190f6e45cb63072aaf7bb5e56cc85 *tests/testthat/roxygen-block-3-C.Rd 0afd340b523acd4b5b39c0792258cde3 *tests/testthat/roxygen-block-3.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 12f39a949a6a854318815198f0400a60 *tests/testthat/test-block.R 37f56048845717cfb08ec81e82c5f789 *tests/testthat/test-collate.R 373681070db48b905e27593aaef4c337 *tests/testthat/test-load.R 441deae2d3cc6fab814222419888c368 *tests/testthat/test-markdown-code.R 81b600370e91b54d030cda5022bbe798 *tests/testthat/test-markdown-link.R c133a0e617714fa240d349886cbd7a5f *tests/testthat/test-markdown-state.R 9cc523236a2c9f41c7d1da5ec79d6a82 *tests/testthat/test-markdown.R 9c8c21b4638b2bb42b0a2f505c1406c9 *tests/testthat/test-namespace.R 4f250b39f8be73c0fa957dc8ea284045 *tests/testthat/test-object-defaults.R 5b9b9226abe275a309ff5b1c91d86551 *tests/testthat/test-object-format.R bf4be305be4c224a6356ed1bd5c0bd12 *tests/testthat/test-object-from-call.R 4a89ae328851f6cb548653b0543233c1 *tests/testthat/test-object-import.R 8e8692fa7fcc3c651072400bbb631016 *tests/testthat/test-object-package.R f3bf9b9490d3325169068ab0029716e7 *tests/testthat/test-object-r6.R 422c2838bd28bd6f4d19856fb1413586 *tests/testthat/test-object-rc.R 6dbbcba158dd1db725575f6306d13a95 *tests/testthat/test-object-s3.R 559b2d8e20cd2274fa21030f571a2182 *tests/testthat/test-options.R 7571cf2b48cc60b5918057a5fe0f2724 *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 a6e88deab0de552973600a24f2213638 *tests/testthat/test-parse.R 24b40226b9d55967a9c023e0a1979ec6 *tests/testthat/test-rd-backref.R bb27740624f48a6efddd9ab7a989c684 *tests/testthat/test-rd-describe-in.R 5527824969e8603c4c39774ddef73761 *tests/testthat/test-rd-examples.R b3b8b7ccdaa83c0e9547db944f384ed0 *tests/testthat/test-rd-family.R 90f77a46f8235dffb10462d6c29337e4 *tests/testthat/test-rd-find-link-files.R d9216fd9ebab7aa9093e5ab3b0bc8fc0 *tests/testthat/test-rd-include-rmd.R 1da8f1b7294ada66f33bafba3b0efca3 *tests/testthat/test-rd-inherit.R f1c3859ca21b51a7b7f7953ef56b7d0d *tests/testthat/test-rd-markdown-escaping.R 603318e486ea6713176dbab153d8cd1d *tests/testthat/test-rd-markdown.R 8202f554b09398679b1a6ea960c54193 *tests/testthat/test-rd-name-alias.R 1d8478c6603683f63690161f9d2fe726 *tests/testthat/test-rd-params.R ad8a882a6bbb467d3ba5248b9030271f *tests/testthat/test-rd-r6.R a52abf9552232033235080a3802f875a *tests/testthat/test-rd-raw.R 2759f1e921a9a2daf312f169d1e2c881 *tests/testthat/test-rd-s4.R 24d874e91069d8733adf5a909c8262a1 *tests/testthat/test-rd-section.R c08e8abe6f7dacfb2ec99433bd08a627 *tests/testthat/test-rd-simple.R e70e20be09a13ec01d55e4512a4ef86c *tests/testthat/test-rd-template.R 44a1512c4c73f024971fc740263c800d *tests/testthat/test-rd-usage.R e27030f9af7a79bfa82cfdb560222273 *tests/testthat/test-rd.R e987e659ab1df5a3ddc5c9772d09a8b2 *tests/testthat/test-roxygenize-setup.R 1bb94eaf389703762788f39f7734c86e *tests/testthat/test-roxygenize.R 597557e126f6d36d1dabaf2db7d33dbd *tests/testthat/test-safety.R d6e0f8e94579a1fd49073ae2813c24f8 *tests/testthat/test-select-args.R d5e6893560295b7eaa7f95889265523b *tests/testthat/test-tag-metadata.R 5304ee44b9bb1c6e3421b3968b75bbcd *tests/testthat/test-tag-parser.R d6f2292efa7c05d9563be66346b8ea56 *tests/testthat/test-tag.R 4a343cd148cce6970b6722669f643ab0 *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 58b83600d945f890241a1c455f308490 *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 d119cb97e3acb0ddacf52c658960de8c *tests/testthat/testCollateOverwrite/R/b.R 09795627d4c31c38e6b7e32364e1a3b8 *tests/testthat/testCollateParse/DESCRIPTION 62853d4d57a96b75cd7ffb7b7be73281 *tests/testthat/testCollateParse/R/b.R d4dffe82e14a669b1369195a3bb4be11 *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 5d1a4a93b7a858d3d5af877cc03be184 *tests/testthat/testNamespace/DESCRIPTION b1b61b677cc8c9a85d50edffc7d21e74 *tests/testthat/testNamespace/NAMESPACE 38d13539cf9635b93a0810e1f452f44c *tests/testthat/testNamespace/R/a.R f79b5e26f0b92d1545bd22db4084a0ef *tests/testthat/testNonASCII/DESCRIPTION 618164f5cb29e361864aaf642310a068 *tests/testthat/testNonASCII/R/a.R 02708dbe8d50f622b815ad19a77ad2c9 *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 fe66061e9656b8d29fd2a80400d8fe5e *vignettes/extending.Rmd 6bfc9797a563b2301f76629478a838ea *vignettes/index-crossref.Rmd 39345ccabe8d89b3420cbdd43caa6874 *vignettes/namespace.Rmd c2395ab3fe51ff8827359a1b45701d7c *vignettes/rd-formatting.Rmd d2aa03e3a856ccf35ca448ac92cfe328 *vignettes/rd-other.Rmd fd3c1a6eae2e9f84719e392f125aa645 *vignettes/rd.Rmd ab9f17dad55f22fab006d240d4d506fb *vignettes/reuse.Rmd 65b1ea1b8593c2d2f72ed255b56c74c3 *vignettes/roxygen2.Rmd roxygen2/inst/0000755000176200001440000000000014553543157013020 5ustar liggesusersroxygen2/inst/doc/0000755000176200001440000000000014553543157013565 5ustar liggesusersroxygen2/inst/doc/rd.R0000644000176200001440000000176614553543156014326 0ustar liggesusers## ----include = FALSE---------------------------------------------------------- knitr::opts_chunk$set(comment = "#>", collapse = TRUE) ## ----------------------------------------------------------------------------- #' 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) {} ## ----------------------------------------------------------------------------- #' 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. roxygen2/inst/doc/extending.html0000644000176200001440000012530214553543155016441 0ustar liggesusers Extending roxygen2

Extending roxygen2

Basics

Roxygen is extensible with user-defined roclets. It means that you can take advantage of Roxygen’s parser and extend it with your own @tags.

There are two primary ways to extend roxygen2:

  • Add a new tag that generates a new top-level section in .Rd files.

  • Add a new roclet that does anything you like.

This vignette will introduce you to the key data structures in roxygen2, and then show you how to use these two extension points. This vignette is very rough, so you are expected to have also read some roxygen2 source code to understand all the extension points. Hopefully, it’s useful enough to help you get started, and if you have problems, please file an issue!

library(roxygen2)

Key data structures

Before we talk about 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, 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

Adding a new .Rd tag

The easiest way to extend roxygen2 is to create a new tag that adds output to .Rd files. This requires two steps:

  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.

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!

And generate 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 it’s 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)
}

We 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 roxygen options).

Next, we define a method for roxy_tag_rd(), which must create an rd_section(). We’re going to create a new 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 the 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 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.

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. Second, you define a roclet that tells roxygen how to process an entire package.

Custom tags

In this example we will make a new @memo tag to enable printing the memos at the console when the roclet runs. We choose that the @memo has this syntax:

@memo [Headline] Description

As an example:

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

As above, we first define a parse method:

roxy_tag_parse.roxy_tag_memo <- function(x) {
  if (!grepl("^\\[.*\\].*$", x$raw)) {
    roxy_tag_warning(x, "Invalid memo format")
    return()
  }

  parsed <- stringi::stri_match(str = x$raw, regex = "\\[(.*)\\](.*)")[1, ]

  x$val <- list(
    header = parsed[[2]], 
    message = parsed[[3]]
  )
  x
}

Then check if 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"

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() just 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:

Roxygen: list(roclets = c("collate", "rd", "namespace", "yourPackage::roclet")) 

Optionally, you can add your roclet package to the target package as a Suggests: dependency:

usethis::use_dev_package("yourPackage", type = "Suggests", remote = "yourGithubID/yourPackage")

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

roxygen2/inst/doc/roxygen2.Rmd0000644000176200001440000001063214525233523016000 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`. ## Learn more This vignette provides a high-level description of roxygen2 and the overall workflow you'll use with it. The other vignettes provide more detail on the most important individual components: - Start with `vignette("rd")` to learn how document your functions with roxygen2. - `vignette("rd-other")` discusses how to document other things like datasets, the package itself, and the various pieces used by R's 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. ## Running roxygen There are three main ways to run roxygen: - `roxygen2::roxygenise()`. - `devtools::document()`. - `Ctrl + Shift + D`, if you're using RStudio. You can mix handwritten Rd and roxygen2; roxygen2 will never overwrite a file it didn't create. ## Basic process 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. #' @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. roxygen2/inst/doc/rd-formatting.Rmd0000644000176200001440000003453114525212067017004 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: Roxygen: list(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 two basic forms: - `[topic]`: The link text is automatically generated from the topic. - `[text][topic]`: You supply the link text. ### Default link text First we explore the simplest form: `[ref]`. The presence of trailing parentheses, e.g., `[func()]`, signals that the target `func` is a function, which causes two things to happen: - The link text `func()` is automatically typeset as code. - The parentheses are stripped in the derived Rd link target. +----------------------+---------------------+---------------------------------------------+ | Markdown | Links to help\ | Notes | | | topic for ... | | +:=====================+:====================+:============================================+ | `[func()]`\ | a function in same\ | Always typeset as code.\ | | `[pkg::func()]` | package or in `pkg` | Produces Rd: `\code{\link[=func]{func()}}`\ | | | | or `\code{\link[pkg:func]{pkg::func()}}` | +----------------------+---------------------+---------------------------------------------+ | `[thing]`\ | a topic in same\ | Use for a dataset or general doc page.\ | | `[pkg::thing]` | package or in `pkg` | Not typeset as code.\ | | | | Produces Rd: `\link{thing}` or\ | | | | `\link[pkg:thing]{pkg::thing}` | +----------------------+---------------------+---------------------------------------------+ | `` [`thing`] ``\ | a topic in same\ | Same as above, but explicit backticks\ | | `` [`pkg::thing`] `` | package or in `pkg` | mean that it **is** typeset as code.\ | | | | Good for documenting a class.\ | | | | Produces Rd: `\code{\link{thing}}` or\ | | | | `\code{\link[pkg:thing]{pkg::thing}}` | +----------------------+---------------------+---------------------------------------------+ ### Custom link text Use the second form `[text][ref]` to link to the topic specified by `ref`, but with `text` as the link text. +----------------------------+---------------------+-----------------------------------------+ | Markdown | Links to help\ | Notes | | | topic for ... | | +:===========================+:====================+:========================================+ | `[text][func()]`\ | a function in same\ | Text is not typeset as code.\ | | `[text][pkg::func()]` | package or in `pkg` | Produces Rd: `\link[=func]{text}` or\ | | | | `\link[pkg:func]{text}` | +----------------------------+---------------------+-----------------------------------------+ | `[text][thing]`\ | a topic in same\ | Text is not typeset as code.\ | | `[text][pkg::thing]` | package or in `pkg` | Use for a topic that documents `NULL`\ | | | | and name is set via `@name`,\ | | | | e.g., a dataset or concept.\ | | | | Produces Rd: `\link[=thing]{text}` or\ | | | | `\link[pkg:thing]{text}` | +----------------------------+---------------------+-----------------------------------------+ | `` [`text`][thing] ``\ | a topic in same\ | Same as above, but explicit backticks\ | | `` [`text`][pkg::thing] `` | package or in `pkg` | mean that text is typeset as code.\ | | | | Produces Rd: `\code{\link{=thing}}` or\ | | | | `\code{\link[pkg:thing]{pkg::thing}}` | +----------------------------+---------------------+-----------------------------------------+ ### Operators Links to operators or objects that contain special characters do not currently work. So to link to (e.g.) the `%>%` operator in the `magrittr` package, instead of `[magrittr::%>%]`, you will need to use the `Rd` notation: `\code{\link[magrittr]{\%>\%}}`. ## 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()`. ### 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/rd-formatting.R0000644000176200001440000000145114553543155016464 0ustar liggesusers## ----include = FALSE---------------------------------------------------------- knitr::opts_chunk$set(comment = "#>", collapse = TRUE) ## ----echo=FALSE--------------------------------------------------------------- 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 ## ----echo=FALSE, results="asis"----------------------------------------------- cat( "\x60\x60\x60rd\n", format(roxygen2:::roc_proc_text(roxygen2::rd_roclet(), simple_fenced)[[1]]), "\n\x60\x60\x60" ) roxygen2/inst/doc/extending.Rmd0000644000176200001440000002346614525212067016221 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) ``` ## Basics Roxygen is extensible with user-defined **roclets**. It means that you can take advantage of Roxygen's parser and extend it with your own `@tags`. There are two primary ways to extend roxygen2: - Add a new tag that generates a new top-level section in `.Rd` files. - Add a new roclet that does anything you like. This vignette will introduce you to the key data structures in roxygen2, and then show you how to use these two extension points. This vignette is very rough, so you are expected to have also read some roxygen2 source code to understand all the extension points. Hopefully, it's useful enough to help you get started, and if you have problems, please [file an issue](https://github.com/r-lib/roxygen2/issues/new)! ```{r setup} library(roxygen2) ``` ## Key data structures Before we talk about 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, 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 ``` ## Adding a new `.Rd` tag The easiest way to extend roxygen2 is to create a new tag that adds output to `.Rd` files. This requires two steps: 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. 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! ``` And generate 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 it's 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) } ``` ```{r, include = FALSE} # Needed for vignette registerS3method("roxy_tag_parse", "roxy_tag_tip", roxy_tag_parse.roxy_tag_tip) ``` We 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 roxygen options). Next, we define a method for `roxy_tag_rd()`, which must create an `rd_section()`. We're going to create a new 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 the `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 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. ## 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. Second, you define a roclet that tells roxygen how to process an entire package. ### Custom tags In this example we will make a new `@memo` tag to enable printing the memos at the console when the roclet runs. We choose that the `@memo` has this syntax: @memo [Headline] Description As an example: @memo [EFFICIENCY] Currently brute-force; find better algorithm. As above, we first define a parse method: ```{r} roxy_tag_parse.roxy_tag_memo <- function(x) { if (!grepl("^\\[.*\\].*$", x$raw)) { roxy_tag_warning(x, "Invalid memo format") return() } parsed <- stringi::stri_match(str = x$raw, regex = "\\[(.*)\\](.*)")[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 check if 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]]) ``` ### 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()` just 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 Roxygen: list(roclets = c("collate", "rd", "namespace", "yourPackage::roclet")) ``` Optionally, you can add your roclet package to the target package as a `Suggests:` dependency: ```r usethis::use_dev_package("yourPackage", type = "Suggests", remote = "yourGithubID/yourPackage") ``` You don't have to do this, but it will help other developers working on the target package. roxygen2/inst/doc/rd.Rmd0000644000176200001440000001416614525212067014636 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 introduces the basics of roxygen2 for documenting functions. See `vignette("rd-other")` for documenting other types of objects and `vignette("reuse")` for reusing documentation across topics. ## 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. ## 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. You can also use explicit `@title`, `@description`, and `@details` tags. This is unnecessary unless you want to have a multi-paragraph description, bulleted list, or other more exotic structure. ```{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. ``` ## Functions 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 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. 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. ### 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`. For the purpose of illustration, it's often useful to include code that causes an error. You can do this by wrapping the code in `try()` or using `\dontrun{}` to exclude from the executed example code. For finer control, you can 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. 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 case, the function usage (which appears beneath the description in the generates 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/rd-other.html0000644000176200001440000005720114553543156016203 0ustar liggesusers Documenting other objects

Documenting other objects

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.

#' 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.

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.

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.

S3

  • S3 generics are regular functions, so document them as such. If necessary, include a section that provides additional details for developers implementing methods.

  • S3 classes have no formal definition, so document the constructor.

  • 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 should, however, always @export S3 methods).

    If your method is more complicated, you should document it by setting @rdname or @describeIn. For complicated methods, you might document in their own file (i.e. @rdname generic.class; for simpler methods you might document with the generic (i.e. @describeIn generic). Learn more about these tags in vignette("reuse").

  • Generally, roxygen2 will automatically figure out the generic that the method belongs to, and you should only need to use @method if there is ambiguity. 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 (e.g.) @method all.equal data.frame.

S4

S4 generics are also functions, so document them as such.

Document S4 classes by adding a roxygen block before setClass(). 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
Account <- setClass("Account",
  slots = list(balance = "numeric")
)

S4 methods are a little more complicated. Unlike S3 methods, all S4 methods must be documented. You can document them 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 the next section for more details.

R6

  • R6 methods can be documented in-line, i.e. the method’s documentation comments come right before the definition of the method.

  • Method documentation can use the @description, @details, @param, @return and @examples tags. These are used to create a subsection for the method, within a separate ‘Methods’ section. All roxygen comment lines of a method documentation must appear after a tag.

  • @param tags that appear before the class definition are automatically inherited by all methods, if needed.

  • R6 fields and active bindings can make use of the @field tag. Their documentation should also be in-line.

  • roxygen2 checks that all public methods, public fields, active bindings and all method arguments are documented, and issues warnings otherwise.

  • To turn off the special handling of R6 classes and go back to the roxygen2 6.x.x behavior, use the r6 = FALSE option in DESCRIPTION, in the Roxygen entry: Roxygen: list(r6 = FALSE).

roxygen2 automatically generates additional sections for an R6 class:

  • 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.

An example from the R6 tutorial:

#' R6 Class Representing a Person
#'
#' @description
#' A person has a name and a hair color.
#'
#' @details
#' A person can also greet you.

Person <- R6::R6Class("Person",
public = list(

    #' @field name First or full name of the person.
    name = NULL,

    #' @field hair Hair color of the person.
    hair = NULL,

    #' @description
    #' Create a new person object.
    #' @param name Name.
    #' @param hair Hair color.
    #' @return 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.
    #' @examples
    #' P <- Person("Ann", "black")
    #' P$hair
    #' P$set_hair("red")
    #' P$hair
    set_hair = function(val) {
      self$hair <- val
    },

    #' @description
    #' Say hi.
    greet = function() {
      cat(paste0("Hello, my name is ", self$name, ".\n"))
    }
  )
)
roxygen2/inst/doc/namespace.html0000644000176200001440000006210214553543155016406 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 in 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(), exportS3method(), exportClasses(), orexportMethods()) you need.

Note that datasets should never be exported as they are not found in NAMESPACE. Instead, datasets will either be automatically exported if you set LazyData: true in your DESCRIPTION, or made available after calling data() if not.

Functions

A function should be exported if it is user facing; it should not be exported if it’s for internal use only. 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.

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

S3

An S3 generic works like a regular R function so export it following the advice above: if you want users to call it, export; otherwise, don’t.

#' Take an object to bizarro world
#' 
#' @param x A vector.
#' @export
bizarro <- function(x, ...) {
  UseMethod("bizarro")
}

While S3 methods are regular functions with a special naming scheme, their “export” works a bit differently. S3 methods are exported only in the sense that calling the generic with the appropriate class will call the method; a user can’t directly access the method definition by typing its name. A more technically correctly term would be to say that the method is registered so that the generics can find it.

You must register, i.e. @export, every S3 method regardless of whether or not the generic is exported. 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.

You have four options for documenting an S3 method:

  • Don’t document it; it’s not required, and not needed for simple generics where the user won’t care about the details.
  • If the method is particularly complex or has many arguments that the generic does not, you can document it in its own file. In this case, just document it as if it’s a function.
  • You can use @rdname to document it with other methods for the generic. This is a good option if it’s your generic, and you’re providing a bunch of methods for different classes.
  • You can use @rdname to document it with other methods for the class. This is typically the least appealing option because the different generics will have different arguments, leading to a cluttered and potentially confusing page.
#' Take an object to bizarro world
#' 
#' @description
#' This is an S3 generic. This package provides methods for the 
#' following classes:
#' 
#' * `character`: reverses the order of the letters in each element of 
#'    the vector.
#' 
#' @param x A vector.
#' @export
bizarro <- function(x, ...) {
  UseMethod("bizarro")
}

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

Typically, you will write methods for generics that are either defined in the current package or a package that is a hard dependency2 of your package. Sometimes, however, you will want to write a method for a suggested dependency. In this case, @export will not work because it assumes the generic is included or imported in your NAMESPACE. Instead, use @exportS3Method. This will use “delayed” method registration, which means the method will only be registered when the suggested package is loaded.

To use @exportS3Method you must provide the package and generic name in the following format:

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

S4

  • Classes: export the class object if you want others to be able to extend it.

  • Generics: treat it like a function and @export if user facing.

  • Methods: you only need to @export a method, if the generic lives in another package. Unlike S3, you must document S4 methods. Because method details are often not that important, it’s common to use @rdname to put the documentation for unimportant methods into a single topic with @keywords internal.

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)

# From backports:
#' @rawNamespace if (getRversion() < "4.0.0") export(stopifnot)

If you need to automate this, @evalNamespace fun() will evaluate fun() in the 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 place holder.

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 (either implicitly or explicitly), the methods will also be available. In other words, you don’t need to do anything special for S3 methods. As long as you’ve imported the generic, all the methods will also be available.

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 and S4 generics and methods.↩︎

  2. i.e. it is listed in either the Imports or Depends fields in your DESCRIPTION.↩︎

roxygen2/inst/doc/rd.html0000644000176200001440000004205614553543156015066 0ustar liggesusers Documenting functions

Documenting functions

This vignette introduces the basics of roxygen2 for documenting functions. See vignette("rd-other") for documenting other types of objects and vignette("reuse") for reusing documentation across topics.

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.

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.

You can also use explicit @title, @description, and @details tags. This is unnecessary unless you want to have a multi-paragraph description, bulleted list, or other more exotic structure.

#' 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.

Functions

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 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.

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.

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.

For the purpose of illustration, it’s often useful to include code that causes an error. You can do this by wrapping the code in try() or using \dontrun{} to exclude from the executed example code.

For finer control, you can 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.

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 case, the function usage (which appears beneath the description in the generates 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/namespace.Rmd0000644000176200001440000002144314531364526016166 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] in 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()`, `exportS3method()`, `exportClasses()`, or`exportMethods()`) you need. [^1]: Including S3 and S4 generics and methods. Note that datasets should never be exported as they are not found in `NAMESPACE`. Instead, datasets will either be automatically exported if you set `LazyData: true` in your `DESCRIPTION`, or made available after calling `data()` if not. ### Functions A function should be exported if it is user facing; it should not be exported if it's for internal use only. 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. ```{r} #' Add two numbers together #' #' @param x,y A pair of numbers. #' @export add <- function(x, y) { x + y } ``` ### S3 An S3 generic works like a regular R function so export it following the advice above: if you want users to call it, export; otherwise, don't. ```{r} #' Take an object to bizarro world #' #' @param x A vector. #' @export bizarro <- function(x, ...) { UseMethod("bizarro") } ``` While S3 methods are regular functions with a special naming scheme, their "export" works a bit differently. S3 methods are exported only in the sense that calling the generic with the appropriate class will call the method; a user can't directly access the method definition by typing its name. A more technically correctly term would be to say that the method is **registered** so that the generics can find it. You must register, i.e. `@export`, every S3 method regardless of whether or not the generic is exported. roxygen2 will warn you if you have forgotten. ```{r} #' @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. You have four options for documenting an S3 method: - Don't document it; it's not required, and not needed for simple generics where the user won't care about the details. - If the method is particularly complex or has many arguments that the generic does not, you can document it in its own file. In this case, just document it as if it's a function. - You can use `@rdname` to document it with other methods for the generic. This is a good option if it's your generic, and you're providing a bunch of methods for different classes. - You can use `@rdname` to document it with other methods for the class. This is typically the least appealing option because the different generics will have different arguments, leading to a cluttered and potentially confusing page. ```{r} #' Take an object to bizarro world #' #' @description #' This is an S3 generic. This package provides methods for the #' following classes: #' #' * `character`: reverses the order of the letters in each element of #' the vector. #' #' @param x A vector. #' @export bizarro <- function(x, ...) { UseMethod("bizarro") } #' @export #' @rdname bizarro bizarro.character <- function(x, ...) { letters <- strsplit(x, "") letters_rev <- lapply(letters, rev) vapply(letters_rev, paste, collapse = "", FUN.VALUE = character(1)) } ``` Typically, you will write methods for generics that are either defined in the current package or a package that is a hard dependency[^2] of your package. Sometimes, however, you will want to write a method for a suggested dependency. In this case, `@export` will not work because it assumes the generic is included or imported in your `NAMESPACE`. Instead, use `@exportS3Method`. This will use "delayed" method registration, which means the method will only be registered when the suggested package is loaded. [^2]: i.e. it is listed in either the `Imports` or `Depends` fields in your `DESCRIPTION`. To use `@exportS3Method` you must provide the package and generic name in the following format: ```{r} #' @exportS3Method pkg::generic generic.foo <- function(x, ...) { } ``` ### S4 - **Classes**: export the class object if you want others to be able to extend it. - **Generics:** treat it like a function and `@export` if user facing. - **Methods**: you only need to `@export` a method, if the generic lives in another package. Unlike S3, you must document S4 methods. Because method details are often not that important, it's common to use `@rdname` to put the documentation for unimportant methods into a single topic with `@keywords internal`. ### 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) # From backports: #' @rawNamespace if (getRversion() < "4.0.0") export(stopifnot) ``` If you need to automate this, `@evalNamespace fun()` will evaluate `fun()` in the 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 place holder. 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 (either implicitly or explicitly), the methods will also be available. In other words, you don't need to do anything special for S3 methods. As long as you've imported the generic, all the methods will also be available. ### 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/namespace.R0000644000176200001440000000367114553543155015651 0ustar liggesusers## ----include = FALSE---------------------------------------------------------- knitr::opts_chunk$set(comment = "#>", collapse = TRUE) ## ----------------------------------------------------------------------------- #' Add two numbers together #' #' @param x,y A pair of numbers. #' @export add <- function(x, y) { x + y } ## ----------------------------------------------------------------------------- #' Take an object to bizarro world #' #' @param x A vector. #' @export bizarro <- function(x, ...) { UseMethod("bizarro") } ## ----------------------------------------------------------------------------- #' @export bizarro.character <- function(x, ...) { letters <- strsplit(x, "") letters_rev <- lapply(letters, rev) vapply(letters_rev, paste, collapse = "", FUN.VALUE = character(1)) } ## ----------------------------------------------------------------------------- #' Take an object to bizarro world #' #' @description #' This is an S3 generic. This package provides methods for the #' following classes: #' #' * `character`: reverses the order of the letters in each element of #' the vector. #' #' @param x A vector. #' @export bizarro <- function(x, ...) { UseMethod("bizarro") } #' @export #' @rdname bizarro bizarro.character <- function(x, ...) { letters <- strsplit(x, "") letters_rev <- lapply(letters, rev) vapply(letters_rev, paste, collapse = "", FUN.VALUE = character(1)) } ## ----------------------------------------------------------------------------- #' @exportS3Method pkg::generic generic.foo <- function(x, ...) { } ## ----------------------------------------------------------------------------- # From dplyr: #' @rawNamespace import(vctrs, except = data_frame) # From backports: #' @rawNamespace if (getRversion() < "4.0.0") export(stopifnot) ## ----------------------------------------------------------------------------- #' @importFrom pkg fun1 fun2 #' @importFrom pkg2 fun3 #' @importFrom pkg3 fun4 NULL roxygen2/inst/doc/rd-other.Rmd0000644000176200001440000001672614553531033015757 0ustar liggesusers--- title: "Documenting other objects" description: > How to document datasets, packages, and the classes, generics, and methods of S3, S4, and R6. output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Documenting other objects} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ``` ## 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. ```{r} #' 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. ``` 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. ## 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: ```{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. ## S3 - S3 **generics** are regular functions, so document them as such. If necessary, include a section that provides additional details for developers implementing methods. - S3 **classes** have no formal definition, so document the [constructor](https://adv-r.hadley.nz/s3.html#s3-constructor). - 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 should, however, always `@export` S3 methods). If your method is more complicated, you should document it by setting `@rdname` or `@describeIn`. For complicated methods, you might document in their own file (i.e. `@rdname generic.class`; for simpler methods you might document with the generic (i.e. `@describeIn generic)`. Learn more about these tags in `vignette("reuse")`. - Generally, roxygen2 will automatically figure out the generic that the method belongs to, and you should only need to use `@method` if there is ambiguity. 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 (e.g.) `@method all.equal data.frame`. ## S4 S4 **generics** are also functions, so document them as such. Document **S4 classes** by adding a roxygen block before `setClass()`. Use `@slot` to document the slots of the class. Here's a simple example: ```{r} #' An S4 class to represent a bank account #' #' @slot balance A length-one numeric vector Account <- setClass("Account", slots = list(balance = "numeric") ) ``` S4 **methods** are a little more complicated. Unlike S3 methods, all S4 methods must be documented. You can document them 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 the next section for more details. ## R6 - R6 methods can be documented in-line, i.e. the method's documentation comments come right before the definition of the method. - Method documentation can use the `@description`, `@details`, `@param`, `@return` and `@examples` tags. These are used to create a subsection for the method, within a separate 'Methods' section. All roxygen comment lines of a method documentation must appear after a tag. - `@param` tags that appear before the class definition are automatically inherited by all methods, if needed. - R6 fields and active bindings can make use of the `@field` tag. Their documentation should also be in-line. - roxygen2 checks that all public methods, public fields, active bindings and all method arguments are documented, and issues warnings otherwise. - To turn off the special handling of R6 classes and go back to the roxygen2 6.x.x behavior, use the `r6 = FALSE` option in `DESCRIPTION`, in the `Roxygen` entry: `Roxygen: list(r6 = FALSE)`. roxygen2 automatically generates additional sections for an R6 class: - 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. An example from the R6 tutorial: ```{r} #' R6 Class Representing a Person #' #' @description #' A person has a name and a hair color. #' #' @details #' A person can also greet you. Person <- R6::R6Class("Person", public = list( #' @field name First or full name of the person. name = NULL, #' @field hair Hair color of the person. hair = NULL, #' @description #' Create a new person object. #' @param name Name. #' @param hair Hair color. #' @return 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. #' @examples #' P <- Person("Ann", "black") #' P$hair #' P$set_hair("red") #' P$hair set_hair = function(val) { self$hair <- val }, #' @description #' Say hi. greet = function() { cat(paste0("Hello, my name is ", self$name, ".\n")) } ) ) ``` roxygen2/inst/doc/roxygen2.html0000644000176200001440000003427014553543157016236 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.

Learn more

This vignette provides a high-level description of roxygen2 and the overall workflow you’ll use with it. The other vignettes provide more detail on the most important individual components:

  • Start with vignette("rd") to learn how document your functions with roxygen2.

  • vignette("rd-other") discusses how to document other things like datasets, the package itself, and the various pieces used by R’s 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.

Running roxygen

There are three main ways to run roxygen:

  • roxygen2::roxygenise().

  • devtools::document().

  • Ctrl + Shift + D, if you’re using RStudio.

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

Basic process

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.
#' @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.

roxygen2/inst/doc/roxygen2.R0000644000176200001440000000154414553543157015471 0ustar liggesusers## ----include = FALSE---------------------------------------------------------- knitr::opts_chunk$set(comment = "#>", collapse = TRUE) ## ----echo = FALSE------------------------------------------------------------- example <- "#' Add together two numbers #' #' @param x A number. #' @param y A number. #' @return A number. #' @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. #' @examples #' add(1, 1) #' add(10, 1) add <- function(x, y) { x + y } ## ----echo=FALSE, results="asis"----------------------------------------------- cat( "\x60\x60\x60rd\n", format(roxygen2::roc_proc_text(roxygen2::rd_roclet(), example)[[1]]), "\n\x60\x60\x60", sep = "" ) roxygen2/inst/doc/rd-other.R0000644000176200001440000000502714553543156015437 0ustar liggesusers## ----include = FALSE---------------------------------------------------------- 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. ## ----eval = FALSE------------------------------------------------------------- # #' @keywords internal # "_PACKAGE" ## ----------------------------------------------------------------------------- #' An S4 class to represent a bank account #' #' @slot balance A length-one numeric vector Account <- setClass("Account", slots = list(balance = "numeric") ) ## ----------------------------------------------------------------------------- #' R6 Class Representing a Person #' #' @description #' A person has a name and a hair color. #' #' @details #' A person can also greet you. Person <- R6::R6Class("Person", public = list( #' @field name First or full name of the person. name = NULL, #' @field hair Hair color of the person. hair = NULL, #' @description #' Create a new person object. #' @param name Name. #' @param hair Hair color. #' @return 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. #' @examples #' P <- Person("Ann", "black") #' P$hair #' P$set_hair("red") #' P$hair set_hair = function(val) { self$hair <- val }, #' @description #' Say hi. greet = function() { cat(paste0("Hello, my name is ", self$name, ".\n")) } ) ) roxygen2/inst/doc/reuse.R0000644000176200001440000000725214553543156015040 0ustar liggesusers## ----include = FALSE---------------------------------------------------------- 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. ## ----include = FALSE---------------------------------------------------------- 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 = ", ") } ## ----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 " ## ----code = backtick---------------------------------------------------------- #' Title #' #' @param x A string. Must be one of `r alphabet(5)` foo <- function(x) NULL ## ----code = roxygen2:::markdown_pass1(backtick)------------------------------- #' Title #' #' @param x A string. Must be one of `a`, `b`, `c`, `d`, `e` foo <- function(x) NULL roxygen2/inst/doc/index-crossref.R0000644000176200001440000000122714553543155016643 0ustar liggesusers## ----include = FALSE---------------------------------------------------------- knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ## ----------------------------------------------------------------------------- #' @seealso [prod()] for products, [cumsum()] for cumulative sums, and #' [colSums()]/[rowSums()] marginal sums over high-dimensional arrays. ## ----eval = FALSE------------------------------------------------------------- # list( # rd_family_title = list(aggregations = "Aggregation functions") # ) ## ----------------------------------------------------------------------------- #' @backref src/file.cpp #' @backref src/file.h roxygen2/inst/doc/rd-formatting.html0000644000176200001440000010510614553543156017232 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:

Roxygen: list(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 Links to help
topic for …
Notes
[func()]
[pkg::func()]
a function in same
package or in pkg
Always typeset as code.
Produces Rd: \code{\link[=func]{func()}}
or \code{\link[pkg:func]{pkg::func()}}
[thing]
[pkg::thing]
a topic in same
package or in pkg
Use for a dataset or general doc page.
Not typeset as code.
Produces Rd: \link{thing} or
\link[pkg:thing]{pkg::thing}
[`thing`]
[`pkg::thing`]
a topic in same
package or in pkg
Same as above, but explicit backticks
mean that it is typeset as code.
Good for documenting a class.
Produces Rd: \code{\link{thing}} or
\code{\link[pkg:thing]{pkg::thing}}

Operators

Links to operators or objects that contain special characters do not currently work. So to link to (e.g.) the %>% operator in the magrittr package, instead of [magrittr::%>%], you will need to use the Rd notation: \code{\link[magrittr]{\%>\%}}.

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().

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.R0000644000176200001440000001141714553543154015676 0ustar liggesusers## ----include = FALSE---------------------------------------------------------- knitr::opts_chunk$set(comment = "#>", collapse = TRUE) ## ----setup-------------------------------------------------------------------- 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) } ## ----include = FALSE---------------------------------------------------------- # 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) } ## ----include = FALSE---------------------------------------------------------- # 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" ) } ## ----include = FALSE---------------------------------------------------------- # Needed for vignette registerS3method("format", "rd_section_tip", format.rd_section_tip) ## ----------------------------------------------------------------------------- topic <- roc_proc_text(rd_roclet(), text)[[1]] topic$get_section("tip") ## ----------------------------------------------------------------------------- roxy_tag_parse.roxy_tag_memo <- function(x) { if (!grepl("^\\[.*\\].*$", x$raw)) { roxy_tag_warning(x, "Invalid memo format") return() } parsed <- stringi::stri_match(str = x$raw, regex = "\\[(.*)\\](.*)")[1, ] x$val <- list( header = parsed[[2]], message = parsed[[3]] ) x } ## ----include = FALSE---------------------------------------------------------- # 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) } ## ----include = FALSE---------------------------------------------------------- # 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/index-crossref.html0000644000176200001440000003123014553543155017403 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/reuse.html0000644000176200001440000010547114553543157015606 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 all of each function, along with its description. It uses a number of heuristics to determine the heading of this section, depending on 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 general 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.

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 @inherits foo to inherit the documentation for every supported tag from another topic. Currently, @inherits 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.

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.

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/reuse.Rmd0000644000176200001440000003533614527204227015357 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} #' 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 all of each function, along with its description. It uses a number of heuristics to determine the heading of this section, depending on 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 general 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. ### 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 `@inherits foo` to inherit the documentation for every supported tag from another topic. Currently, `@inherits` 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. ## 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_pass1(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. ## 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/index-crossref.Rmd0000644000176200001440000000630214520721622017152 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/roxygen2-tags.yml0000644000176200001440000002670514525476560016270 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 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 - 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 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 recommend: true - name: examplesIf description: > Run examples only when `condition` is `TRUE`. template: " ${1:condition}\n${2:# example code}\n" vignette: rd 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-other 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-other 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. 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. template: ' ${1:source}' 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: > Force a function to be recognised as an S3 method. This affects the default usage and the `NAMESPACE` directive produced by `@export`. Only needed if automatic detection fails. template: ' ${1:generic} ${2:class}' vignette: rd-other 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' 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 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 - 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 - 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 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-other 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-other 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 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 recommend: true - name: useDynLib description: > Import compiled code from another package. template: ' ${1:package}' vignette: namespace recommend: true