├── .Rbuildignore ├── .github ├── .gitignore └── workflows │ ├── R-CMD-check.yaml │ └── pkgdown.yaml ├── .gitignore ├── DESCRIPTION ├── LICENSE ├── LICENSE.md ├── NAMESPACE ├── NEWS.md ├── R ├── check_linter.R ├── config.R ├── errors.R ├── linters.R ├── register.R ├── roclet.R ├── roxy_alerts.R └── utils.R ├── README.md ├── man ├── add_linters.Rd ├── check_linter.Rd ├── config.Rd ├── config_tag_accepts_linters.Rd ├── demo_tag.Rd ├── format_tag_prefix.Rd ├── if-not-null-else.Rd ├── into_roxy_alert.Rd ├── linters.Rd ├── register_linters.Rd ├── roxygen │ └── meta.R ├── roxylint.Rd └── roxylint │ └── meta.R └── pkgdown └── _pkgdown.yml /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^LICENSE\.md$ 2 | ^_pkgdown\.yml$ 3 | ^docs$ 4 | ^pkgdown$ 5 | ^\.github$ 6 | -------------------------------------------------------------------------------- /.github/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | -------------------------------------------------------------------------------- /.github/workflows/R-CMD-check.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | push: 5 | branches: [main, master] 6 | pull_request: 7 | branches: [main, master] 8 | 9 | name: R-CMD-check 10 | 11 | jobs: 12 | R-CMD-check: 13 | runs-on: ${{ matrix.config.os }} 14 | 15 | name: ${{ matrix.config.os }} (${{ matrix.config.r }}) 16 | 17 | strategy: 18 | fail-fast: false 19 | matrix: 20 | config: 21 | - {os: macos-latest, r: 'release'} 22 | - {os: windows-latest, r: 'release'} 23 | - {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'} 24 | - {os: ubuntu-latest, r: 'release'} 25 | - {os: ubuntu-latest, r: 'oldrel-1'} 26 | 27 | env: 28 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 29 | R_KEEP_PKG_SOURCE: yes 30 | 31 | steps: 32 | - uses: actions/checkout@v3 33 | 34 | - uses: r-lib/actions/setup-pandoc@v2 35 | 36 | - uses: r-lib/actions/setup-r@v2 37 | with: 38 | r-version: ${{ matrix.config.r }} 39 | http-user-agent: ${{ matrix.config.http-user-agent }} 40 | use-public-rspm: true 41 | 42 | - uses: r-lib/actions/setup-r-dependencies@v2 43 | with: 44 | extra-packages: any::rcmdcheck 45 | needs: check 46 | 47 | - uses: r-lib/actions/check-r-package@v2 48 | with: 49 | upload-snapshots: true 50 | -------------------------------------------------------------------------------- /.github/workflows/pkgdown.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | 4 | permissions: 5 | contents: write 6 | 7 | on: 8 | push: 9 | branches: [main, master] 10 | pull_request: 11 | branches: [main, master] 12 | release: 13 | types: [published] 14 | workflow_dispatch: 15 | 16 | name: pkgdown 17 | 18 | jobs: 19 | pkgdown: 20 | runs-on: ubuntu-latest 21 | # Only restrict concurrency for non-PR jobs 22 | concurrency: 23 | group: pkgdown-${{ github.event_name != 'pull_request' || github.run_id }} 24 | env: 25 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 26 | steps: 27 | - uses: actions/checkout@v3 28 | 29 | - uses: r-lib/actions/setup-pandoc@v2 30 | 31 | - uses: r-lib/actions/setup-r@v2 32 | with: 33 | use-public-rspm: true 34 | 35 | - uses: r-lib/actions/setup-r-dependencies@v2 36 | with: 37 | extra-packages: any::pkgdown, local::. 38 | needs: website 39 | 40 | - name: Build site 41 | run: pkgdown::build_site_github_pages(new_process = FALSE, install = FALSE) 42 | shell: Rscript {0} 43 | 44 | - name: Deploy to GitHub pages 🚀 45 | if: github.event_name != 'pull_request' 46 | uses: JamesIves/github-pages-deploy-action@v4.4.1 47 | with: 48 | clean: false 49 | branch: gh-pages 50 | folder: docs 51 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | docs 2 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: roxylint 2 | Title: Lint 'roxygen2'-Generated Documentation 3 | Version: 0.1.0 4 | Authors@R: 5 | c( 6 | person( 7 | "Doug", "Kelkhoff", 8 | email = "doug.kelkhoff@gmail.com", 9 | role = c("aut", "cre") 10 | ) 11 | ) 12 | Description: Provides formatting linting to 'roxygen2' tags. Linters report 13 | 'roxygen2' tags that do not conform to a standard style. These linters 14 | can be a helpful check for building more consistent documentation and 15 | to provide reminders about best practices or checks for typos. Default 16 | linting suites are provided for common style guides such as the one 17 | followed by the 'tidyverse', though custom linters can be registered by 18 | other packages or be custom-tailored to a specific package. 19 | License: MIT + file LICENSE 20 | Encoding: UTF-8 21 | URL: 22 | https://github.com/openpharma/roxylint, 23 | https://openpharma.github.io/roxylint/ 24 | BugReports: 25 | https://github.com/openpharma/roxylint/issues 26 | Imports: 27 | utils, 28 | cli, 29 | roxygen2 30 | RoxygenNote: 7.2.3 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | YEAR: 2023 2 | COPYRIGHT HOLDER: roxylint authors 3 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | Copyright (c) 2023 roxylint authors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | S3method(check_linter,"function") 4 | S3method(check_linter,character) 5 | S3method(check_linter,default) 6 | S3method(check_linter,list) 7 | S3method(lint_full_stop,character) 8 | S3method(lint_full_stop,default) 9 | S3method(lint_full_stop,roxy_tag) 10 | S3method(lint_no_full_stop,character) 11 | S3method(lint_no_full_stop,default) 12 | S3method(lint_no_full_stop,roxy_tag) 13 | S3method(lint_sentence_case,character) 14 | S3method(lint_sentence_case,default) 15 | S3method(lint_sentence_case,roxy_tag) 16 | S3method(lint_starts_lowercase,character) 17 | S3method(lint_starts_lowercase,default) 18 | S3method(lint_starts_lowercase,roxy_tag) 19 | S3method(lint_title_case,character) 20 | S3method(lint_title_case,default) 21 | S3method(lint_title_case,roxy_tag) 22 | S3method(roxygen2::roclet_output,roclet_roxylint) 23 | S3method(roxygen2::roclet_process,roclet_roxylint) 24 | export(check_linter) 25 | export(demo_tag) 26 | export(lint_full_stop) 27 | export(lint_no_full_stop) 28 | export(lint_sentence_case) 29 | export(lint_starts_lowercase) 30 | export(lint_title_case) 31 | export(register_linters) 32 | export(roxylint) 33 | export(tidy) 34 | export(tidy_param) 35 | export(tidy_return) 36 | export(tidy_seealso) 37 | export(tidy_title) 38 | importFrom(utils,capture.output) 39 | importFrom(utils,packageName) 40 | -------------------------------------------------------------------------------- /NEWS.md: -------------------------------------------------------------------------------- 1 | # roxylint v0.1.0 2 | 3 | * first CRAN release 4 | -------------------------------------------------------------------------------- /R/check_linter.R: -------------------------------------------------------------------------------- 1 | #' Apply 'roxygen2' linters 2 | #' 3 | #' Provided a list of linters for a given tag, iterate over linters to raise 4 | #' alerts during the documentation process. 5 | #' 6 | #' @param linters A linters specification, either a `function` or 7 | #' `character` regular expression that should be matched, or a `list` 8 | #' of either. See details for more information. 9 | #' @param tag A [roxygen2::roxy_tag()]. 10 | #' @param message An optional message to use for an alert. 11 | #' @param ... Additional arguments unused. 12 | #' 13 | #' @return `TRUE`, invisibly. However, this function is primarily used for its 14 | #' side-effect of raising alerts during documentation. 15 | #' 16 | #' @export 17 | check_linter <- function(linters, tag, ...) { 18 | UseMethod("check_linter") 19 | } 20 | 21 | 22 | #' @describeIn check_linter 23 | #' By default, no linting is performed 24 | #' 25 | #' @export 26 | check_linter.default <- function(linters, tag, ...) { 27 | invisible(TRUE) 28 | } 29 | 30 | 31 | #' @describeIn check_linter 32 | #' A `list` of `function`s or `character` regular expressions. 33 | #' 34 | #' If a `character` value is named, the name is used as the message for a alert 35 | #' that is raised when the expression does not match. 36 | #' 37 | #' @export 38 | check_linter.list <- function(linters, tag, ...) { 39 | for (i in seq_along(linters)) { 40 | linter <- linters[[i]] 41 | message <- names(linters[i]) 42 | message <- if (!is.null(message) && nchar(message) > 0) message else NULL 43 | check_linter(linter, tag, message = message) 44 | } 45 | invisible(TRUE) 46 | } 47 | 48 | 49 | #' @describeIn check_linter 50 | #' A `function` to evaluate for the given tag 51 | #' 52 | #' `function`'s are evaluated with the following arguments: 53 | #' 54 | #' 1. The [roxygen2::roxy_tag()] 55 | #' 2. The contents of the tag's `$val`, as named arguments 56 | #' 57 | #' Because the number of arguments might not be readily apparent, any function 58 | #' should accept a trailing `...` argument. 59 | #' 60 | #' Provided `function`s may print lint output, or signal lint output with 61 | #' messages or warnings. A [cli::cli_alert()] will reflect the severity of the 62 | #' function used to emit the output. 63 | #' 64 | #' @export 65 | check_linter.function <- function(linters, tag, ...) { 66 | into_roxy_alert(tag, do.call(linters, append(list(tag), tag$val))) 67 | invisible(TRUE) 68 | } 69 | 70 | 71 | #' @describeIn check_linter 72 | #' A `character` regular expressions. 73 | #' 74 | #' If a `character` value is found, its value is assumed to be a regular 75 | #' expression which must match a given tag's `raw` content (the text as it 76 | #' appears in the `roxygen2` header). 77 | #' 78 | #' @export 79 | check_linter.character <- function(linters, tag, message = NULL, ...) { 80 | into_roxy_alert(tag, { 81 | if (!grepl(linters, tag$raw, perl = TRUE)) { 82 | message <- message %||% paste0("raw value does not match '", linters, "'") 83 | message(message) 84 | } 85 | }) 86 | invisible(TRUE) 87 | } 88 | -------------------------------------------------------------------------------- /R/config.R: -------------------------------------------------------------------------------- 1 | #' Configuration 2 | #' 3 | #' Various functions for loading, caching and performing configured behaviors 4 | #' using a user-supplied configuration file. 5 | #' 6 | #' @param path A file path to use when searching for a config file. Either the 7 | #' file path to a `DESCRIPTION` or the root path of a package, depending on 8 | #' the context of the function. 9 | #' 10 | #' @name config 11 | .registered <- new.env(parent = baseenv()) 12 | 13 | 14 | 15 | #' @describeIn config 16 | #' Load the contents of a config into an environment 17 | #' 18 | #' @keywords internal 19 | config_load <- function(path = getwd(), cache = TRUE) { 20 | if (!is.null(roxylint_config <- roxygen2::roxy_meta_get("roxylint"))) 21 | return(roxylint_config) 22 | 23 | roxylint <- new.env(parent = baseenv()) 24 | local_config <- config_find_from(path) 25 | 26 | # config linters 27 | for (tag in names(local_config$linters)) { 28 | add_linters( 29 | roxylint, 30 | tag, 31 | local_config$linters[[tag]], 32 | overwrite = TRUE 33 | ) 34 | } 35 | 36 | # add non-linter config 37 | local_config$lintesr <- NULL 38 | for (n in names(local_config)) { 39 | roxylint[[n]] <- local_config[[n]] 40 | } 41 | 42 | # add any registered linters 43 | for (n in names(.registered)) { 44 | regconfig <- .registered[[n]] 45 | for (tag in names(regconfig$linters)) { 46 | overwrite <- isTRUE(regconfig$overwrite) 47 | new_linters <- regconfig$linters[[tag]] 48 | add_linters(roxylint, tag, new_linters, overwrite = overwrite) 49 | } 50 | } 51 | 52 | # store roxylint in roxygen2 environment 53 | roxy_meta_set <- getNamespace("roxygen2")[["roxy_meta_set"]] 54 | if (cache) roxy_meta_set("roxylint", roxylint) 55 | 56 | roxylint 57 | } 58 | 59 | 60 | #' @describeIn config 61 | #' Load a configuration from a path 62 | #' 63 | #' @keywords internal 64 | config_find_from <- function(path) { 65 | repeat { 66 | if (file.exists(file.path(path, "DESCRIPTION"))) break 67 | if (dirname(path) == path) return(list()) 68 | path <- dirname(path) 69 | } 70 | 71 | config_desc <- config_from_desc(path) 72 | config_file <- config_from_file(path) 73 | 74 | if (!is.null(config_desc) && !is.null(config_file)) 75 | stop(errors$redundant_config) 76 | 77 | config_desc %||% config_file 78 | } 79 | 80 | 81 | #' @describeIn config 82 | #' Load a configuration from a DESCRIPTION file 83 | #' 84 | #' @importFrom utils packageName 85 | #' @keywords internal 86 | config_from_desc <- function(path) { 87 | path <- file.path(path, "DESCRIPTION") 88 | 89 | field <- paste0("Config/", utils::packageName()) 90 | config_desc <- read.dcf(path, fields = field)[1, field] 91 | 92 | result <- tryCatch( 93 | eval(parse(text = config_desc)), 94 | error = function(e) stop(errors$description_parse_failure(e$message)) 95 | ) 96 | 97 | if (length(result) == 0 || is.na(result)) return(NULL) 98 | result 99 | } 100 | 101 | 102 | #' @describeIn config 103 | #' Load a configuration from a dotfile 104 | #' 105 | #' @importFrom utils packageName 106 | #' @keywords internal 107 | config_from_file <- function(path) { 108 | pattern <- "^meta\\.[rR]" 109 | 110 | path <- file.path(path, "man", utils::packageName()) 111 | config_files <- list.files( 112 | path, 113 | pattern = pattern, 114 | all.files = TRUE, 115 | full.names = TRUE 116 | ) 117 | 118 | if (length(config_files) == 0) 119 | return(NULL) 120 | 121 | res <- new.env() 122 | source(config_files[[1]], local = res)$value 123 | } 124 | -------------------------------------------------------------------------------- /R/errors.R: -------------------------------------------------------------------------------- 1 | #' Errors used internally 2 | #' 3 | #' @importFrom utils packageName 4 | #' @noRd 5 | errors <- list( 6 | # Multiple config options were used, likely accidentally 7 | redundant_config = paste0( 8 | "Redundant roxytypes configs found:\n", 9 | " * DESCRIPTION [Config/", utils::packageName(), "]\n", 10 | " * ./man/", utils::packageName(), "/meta.R" 11 | ), 12 | 13 | # While parsing a config from DESCRIPTION, a parse or eval failure occured 14 | description_parse_failure = function(msg) { 15 | paste0( 16 | "Could not parse DESCRIPTION contents at Config/", utils::packageName(), "\n", 17 | msg 18 | ) 19 | } 20 | ) 21 | -------------------------------------------------------------------------------- /R/linters.R: -------------------------------------------------------------------------------- 1 | #' Assorted linters 2 | #' 3 | #' Preconfigured linters, either as a collective list of linters or 4 | #' individually. "tidy" linters implement guidelines from the tidyverse style 5 | #' guide. 6 | #' 7 | #' Refer to the individual [roxygen2::roxy_tag()] for the respective tag for 8 | #' argument details. 9 | #' 10 | #' @param x A [roxygen2::roxy_tag()] that is the subject of linting. 11 | #' @param name,description Used for [roxygen2::roxy_tag()]-specific linters. 12 | #' @param ... Additional arguments unused. 13 | #' 14 | #' @name linters 15 | NULL 16 | 17 | 18 | #' @describeIn linters 19 | #' Lowercase start linting. (uses `$raw` for [roxygen2::roxy_tag()]s) 20 | #' 21 | #' @export 22 | lint_starts_lowercase <- function(x, ...) { 23 | UseMethod("lint_sentence_case") 24 | } 25 | 26 | #' @export 27 | lint_starts_lowercase.default <- function(x, ...) { 28 | } 29 | 30 | #' @export 31 | lint_starts_lowercase.roxy_tag <- function(x, ...) { 32 | lint_starts_lowercase(x$raw, ...) 33 | } 34 | 35 | #' @export 36 | lint_starts_lowercase.character <- function(x, ...) { 37 | re <- "^[^[:upper:]]" 38 | if (!grepl(re, trimws(x))) 39 | message("should not start with an uppercase letter") 40 | } 41 | 42 | 43 | #' @describeIn linters 44 | #' Ends in a full stop. (uses `$raw` for [roxygen2::roxy_tag()]s) 45 | #' 46 | #' @export 47 | lint_full_stop <- function(x, ...) { 48 | UseMethod("lint_full_stop") 49 | } 50 | 51 | #' @export 52 | lint_full_stop.default <- function(x, ...) { 53 | } 54 | 55 | #' @export 56 | lint_full_stop.roxy_tag <- function(x, ...) { 57 | lint_full_stop(x$raw, ...) 58 | } 59 | 60 | #' @export 61 | lint_full_stop.character <- function(x, ...) { 62 | re <- "\\.$" 63 | if (!grepl(re, trimws(x))) 64 | message("should terminate with a full stop, `.`") 65 | } 66 | 67 | 68 | #' @describeIn linters 69 | #' Does not end in a full stop. (uses `$raw` for [roxygen2::roxy_tag()]s) 70 | #' 71 | #' @export 72 | lint_no_full_stop <- function(x, ...) { 73 | UseMethod("lint_no_full_stop") 74 | } 75 | 76 | #' @export 77 | lint_no_full_stop.default <- function(x, ...) { 78 | } 79 | 80 | #' @export 81 | lint_no_full_stop.roxy_tag <- function(x, ...) { 82 | lint_no_full_stop(x$raw, ...) 83 | } 84 | 85 | #' @export 86 | lint_no_full_stop.character <- function(x, ...) { 87 | re <- "[^.]$" 88 | if (!grepl(re, trimws(x))) 89 | message("should not terminate with a full stop, `.`") 90 | } 91 | 92 | 93 | #' @describeIn linters 94 | #' Sentence case linting (uses `$raw` for [roxygen2::roxy_tag()]s) 95 | #' 96 | #' @export 97 | lint_sentence_case <- function(x, ...) { 98 | UseMethod("lint_sentence_case") 99 | } 100 | 101 | #' @export 102 | lint_sentence_case.default <- function(x, ...) { 103 | } 104 | 105 | #' @export 106 | lint_sentence_case.roxy_tag <- function(x, ...) { 107 | lint_sentence_case(x$raw, ...) 108 | } 109 | 110 | #' @export 111 | lint_sentence_case.character <- function(x, ...) { 112 | words <- strsplit(trimws(x), " ")[[1L]] 113 | 114 | # find any first words in sentences (at start, or after full stop) 115 | has_stop <- grepl("\\.$", words) 116 | is_start <- rep_len(FALSE, length.out = length(words)) 117 | is_start[[1]] <- TRUE 118 | is_start[-1] <- has_stop[-length(words)] 119 | 120 | first_cap <- all(grepl("^[^[:lower:]]", words[is_start])) 121 | rest_lower <- all(grepl("^[^[:upper:]]", words[!is_start])) 122 | 123 | if (!(first_cap && rest_lower)) 124 | message("should be 'Sentence case'") 125 | } 126 | 127 | 128 | #' @describeIn linters 129 | #' Title case linting 130 | #' 131 | #' @export 132 | lint_title_case <- function(x, ...) { 133 | UseMethod("lint_title_case") 134 | } 135 | 136 | #' @export 137 | lint_title_case.default <- function(x, ...) { 138 | } 139 | 140 | #' @export 141 | lint_title_case.roxy_tag <- function(x, ...) { 142 | lint_title_case(x$raw, ...) 143 | } 144 | 145 | #' @export 146 | lint_title_case.character <- function(x, ...) { 147 | # AP style title case rules 148 | words <- strsplit(x, " ")[[1L]] 149 | exceptions <- c( 150 | "a", "an", "the", # articles 151 | "and", "but", "for", # coordinating conjunctions 152 | "at", "by", "to", "of", "on", "off", "out" # prepositions 153 | ) 154 | 155 | is_exception <- tolower(words) %in% exceptions 156 | is_exception[[1]] <- FALSE 157 | is_exception[[length(words)]] <- FALSE 158 | 159 | if (any(grepl("^[[:lower:]]", words) & !is_exception)) 160 | message("should be 'Title Case'") 161 | } 162 | 163 | 164 | #' @describeIn linters 165 | #' Tidy 'Sentence case' titles 166 | #' 167 | #' @export 168 | tidy_title <- function(x, ...) { 169 | lint_sentence_case(x$raw) 170 | lint_no_full_stop(x$raw) 171 | } 172 | 173 | 174 | #' @describeIn linters 175 | #' Tidy 'Sentence case' `@param` definitions 176 | #' 177 | #' @export 178 | tidy_param <- function(x, name, description, ...) { 179 | lint_sentence_case(description) 180 | lint_full_stop(description) 181 | } 182 | 183 | 184 | #' @describeIn linters 185 | #' Tidy 'Sentence case' `@return` definitions 186 | #' 187 | #' @export 188 | tidy_return <- function(x, ...) { 189 | lint_sentence_case(x$val) 190 | lint_full_stop(x$val) 191 | } 192 | 193 | 194 | #' @describeIn linters 195 | #' Tidy 'Sentence case' `@seealso` definitions 196 | #' 197 | #' @export 198 | tidy_seealso <- function(x, ...) { 199 | lint_sentence_case(x$val) 200 | lint_full_stop(x$val) 201 | } 202 | 203 | 204 | #' @describeIn linters 205 | #' A list of all tidyverse style guide inspired linters 206 | #' 207 | #' @export 208 | tidy <- list( 209 | title = tidy_title, 210 | param = tidy_param, 211 | return = tidy_return, 212 | seealso = tidy_seealso 213 | ) 214 | -------------------------------------------------------------------------------- /R/register.R: -------------------------------------------------------------------------------- 1 | #' Register linters for new tags 2 | #' 3 | #' A registration mechanism for other packages to provide linters, either for 4 | #' custom tags or to implement their own linting styles. 5 | #' 6 | #' @param ... Linters to register, with parameter names used to identify the 7 | #' associated tag. 8 | #' @param linters Optionally, provide a named list of linters directly. 9 | #' @param .overwrite Whether existing linters should be overwritten. Intended to 10 | #' be used sparingly. For example, this could be used to implement an entire 11 | #' new style guide without inheriting defaults. 12 | #' 13 | #' @return `TRUE`, invisibly. 14 | #' 15 | #' @export 16 | register_linters <- function(..., linters = list(...), .overwrite = FALSE) { 17 | pkg <- eval(quote(packageName()), parent.frame()) 18 | 19 | # don't cache, wait for registration to compile all config settings 20 | config <- config_load(cache = FALSE) 21 | if (isTRUE(config$verbose)) { 22 | cli::cli_alert_info("Registering roxylints from {.pkg {pkg}}") 23 | } 24 | 25 | reg_config <- list( 26 | source = pkg, 27 | overwrite = .overwrite, 28 | linters = linters 29 | ) 30 | 31 | .registered[[pkg]] <- reg_config 32 | invisible(TRUE) 33 | } 34 | 35 | 36 | #' Add linters 37 | #' 38 | #' @param config A config object to add linters to. 39 | #' @param tag The name of the tag to which to add linters. 40 | #' @param linters New linters to add. 41 | #' @param overwrite A logical value indicating whether existing linters should 42 | #' be overwritten. 43 | #' 44 | #' @return `NULL`. 45 | #' 46 | #' @keywords internal 47 | add_linters <- function(config, tag, linters, overwrite = FALSE) { 48 | if (overwrite) { 49 | config$linters[[tag]] <- linters 50 | } else if (config_tag_accepts_linters(config, tag)) { 51 | config$linters[[tag]] <- append(config$linters[[tag]], linters) 52 | } 53 | } 54 | 55 | 56 | #' Check whether a given tag accepts new linters 57 | #' 58 | #' Linters can be configured to disallow new linter additions by providing a 59 | #' trailing `NULL` value to a list, or setting the value itself to `NULL`. 60 | #' 61 | #' @inheritParams add_linters 62 | #' 63 | #' @keywords internal 64 | config_tag_accepts_linters <- function(config, tag) { 65 | tag_cfg <- config$linters[[tag]] 66 | 67 | is_null <- tag %in% names(config$linters) && is.null(tag_cfg) 68 | ends_in_null <- is.list(tag_cfg) && is.null(tag_cfg[[length(tag_cfg)]]) 69 | 70 | !is_null && !ends_in_null 71 | } 72 | -------------------------------------------------------------------------------- /R/roclet.R: -------------------------------------------------------------------------------- 1 | #' Roclet for 'roxygen2' style linting 2 | #' 3 | #' Roclets used to embed linters during documentation. To use, add the roclet in 4 | #' your `DESCRIPTION` file. 5 | #' 6 | #' ``` 7 | #' Config/Needs/documentation: roxylint 8 | #' Roxygen: 9 | #' list( 10 | #' markdown = TRUE, 11 | #' roclets = c("namespace", "rd", "roxylint::roxylint") 12 | #' ) 13 | #' ``` 14 | #' 15 | #' @return A `roxylint` [roxygen2::roclet()]. 16 | #' 17 | #' @export 18 | roxylint <- function() { 19 | roxygen2::roclet("roxylint") 20 | } 21 | 22 | 23 | #' @exportS3Method roxygen2::roclet_process roclet_roxylint 24 | #' @noRd 25 | roclet_process.roclet_roxylint <- function(x, blocks, env, base_path) { # nolint 26 | config <- config_load() 27 | 28 | for (block in blocks) { 29 | for (x in block$tags) { 30 | linters <- config$linters[[x$tag]] 31 | check_linter(linters, x) 32 | } 33 | } 34 | 35 | invisible(NULL) 36 | } 37 | 38 | 39 | #' @exportS3Method roxygen2::roclet_output roclet_roxylint 40 | #' @noRd 41 | roclet_output.roclet_roxylint <- function(...) { # nolint 42 | invisible(NULL) 43 | } 44 | -------------------------------------------------------------------------------- /R/roxy_alerts.R: -------------------------------------------------------------------------------- 1 | #' Capture output and re-emit as a cli alert 2 | #' 3 | #' @param tag A [roxygen2::roxy_tag()] to use as context for the alert. 4 | #' @param expr An expression to evaluate. 5 | #' 6 | #' @return `TRUE`, invisibly. 7 | #' 8 | #' @importFrom utils capture.output 9 | #' @keywords internal 10 | into_roxy_alert <- function(tag, expr) { 11 | res <- withCallingHandlers( 12 | utils::capture.output({ 13 | eval(expr) 14 | invisible(NULL) 15 | }), 16 | message = function(m) { 17 | cli::cli_alert_info(paste(format_tag_prefix(tag), m$message)) 18 | invokeRestart("muffleMessage") 19 | }, 20 | warning = function(w) { 21 | cli::cli_alert_danger(paste(format_tag_prefix(tag), w$message)) 22 | invokeRestart("muffleWarning") 23 | } 24 | ) 25 | 26 | if (length(res) > 0) { 27 | cli::cli_alert(paste(format_tag_prefix(tag), paste0(res, collapse = " "))) 28 | } 29 | 30 | invisible(TRUE) 31 | } 32 | 33 | 34 | #' Format a tag for alert context 35 | #' 36 | #' @param x A [roxygen2::roxy_tag()]. 37 | #' 38 | #' @return A formatted string. 39 | #' 40 | #' @keywords internal 41 | format_tag_prefix <- function(x) { 42 | # inspired largely by roxygen2::link_to and roxygen2::warn_roxy_tag 43 | 44 | link <- cli::style_hyperlink( 45 | paste0(basename(x$file), ":", x$line), 46 | paste0("file://", x$file), 47 | params = c(line = x$line, col = 1) 48 | ) 49 | 50 | paste0("[", link, "] @", x$tag) 51 | } 52 | -------------------------------------------------------------------------------- /R/utils.R: -------------------------------------------------------------------------------- 1 | #' If-not-null-else 2 | #' 3 | #' @name if-not-null-else 4 | #' @keywords internal 5 | `%||%` <- function(lhs, rhs) if (is.null(lhs)) rhs else lhs 6 | 7 | 8 | #' Get that tag! 9 | #' 10 | #' Tools for inspecting [roxygen2::roxy_tag()]s. This can be helpful for 11 | #' exploring the intermediate tag objects. For example, you can use this 12 | #' function to generate a tag and explore the named values in `$val`. 13 | #' 14 | #' @param str A 'roxygen2' tag string. 15 | #' 16 | #' @return A [roxygen2::roxy_tag()]. 17 | #' 18 | #' @examples 19 | #' demo_tag("@param var abc") 20 | #' 21 | #' @export 22 | demo_tag <- function(str) { 23 | str <- strsplit(str, "\n")[[1]] 24 | code <- paste0(paste0("#' ", str, collapse = "\n"), "\nNULL") 25 | res <- roxygen2::parse_text(code) 26 | res[[1]]$tags[[1]] 27 | } 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # `roxylint` 2 | 3 | 4 | [![R-CMD-check](https://github.com/openpharma/roxylint/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/openpharma/roxylint/actions/workflows/R-CMD-check.yaml) 5 | 6 | 7 | Lint 'roxygen2'-generated documentation 8 | 9 | ## Quick Start 10 | 11 | Modify your package's description file to add required package for your 12 | `roxygen2` documentation 13 | 14 | `DESCRIPTION` 15 | ``` 16 | Config/Needs/documentation: roxylint 17 | Roxygen: list(markdown = TRUE, roclets = c("namespace", "rd", "roxylint::roxylint")) 18 | ``` 19 | 20 | By default, this will add linters for the tidyverse style guide. If you'd like 21 | to be explicit, you can declare this style yourself: 22 | 23 | `DESCRIPTION` 24 | ``` 25 | Config/roxylint: list(linters = roxylint::tidy) 26 | ``` 27 | 28 | Now, the next time you document your package, you might see some messages about 29 | your formatting! 30 | 31 | ## Tune your Linting 32 | 33 | You can add in your own linters easily. Simply modify the `linters` field in the 34 | configuration list. Of course, if you feel that your linter would be more widely 35 | useful, feel free to open up a pull request to introduce it. 36 | 37 | However, since structuring code in the `DESCRIPTION` file is not very pleasant, 38 | you can instead store your configurations in `man/roxylint/meta.R`. 39 | 40 | For example, it might look something like this: 41 | 42 | `man/roxytypes/meta.R` 43 | ```r 44 | list( 45 | linters = list( 46 | # pick and choose your tidy lints 47 | return = roxylint::lint_sentence_case, 48 | 49 | # use regular expressions to set simple rules 50 | title = "!!!$", 51 | 52 | # add custom messages with named lists 53 | details = list( 54 | "should be in 'ALL CAPS'" = "^[[:upper:] ]*$" 55 | ), 56 | 57 | # use functions for fine-grained control 58 | param = function(x, name, description, ...) { 59 | n_caps <- nchar(gsub("[^[:upper:]]", "", description)) 60 | if (n_caps < nchar(description) / 2) 61 | warning("descriptions should be at least 50% CaPiTALiZeD") 62 | }, 63 | 64 | # use multiple rules in a list 65 | section = list( 66 | "should not have frowny faces" = ":\\(", 67 | function(x, ...) if (grepl(":\\)", x$raw)) message("nice! very happy :)") 68 | ), 69 | 70 | # prevent other packages from registering rules with a `NULL` 71 | yell = NULL, 72 | 73 | # or prevent aditional rules by ending a list with a `NULL` 74 | return = list( 75 | "^Returns ", 76 | NULL 77 | ) 78 | ) 79 | ) 80 | ``` 81 | 82 | With these in place, you'll start getting alerts when you're deviating from your 83 | style. 84 | 85 | ``` 86 | ℹ [check_linter.R:1] @title raw value does not match '!!!$' 87 | ℹ [check_linter.R:6] @param descriptions should be at least 50% CaPiTALiZeD 88 | ℹ [check_linter.R:13] @return descriptions should be 'Sentence case' and end in a period 89 | ``` 90 | 91 | ## Register Linters 92 | 93 | If you're building a package that provides its own `roxygen2` tags, you can also 94 | register default linters. 95 | 96 | `DESCRIPTION` 97 | ``` 98 | Enhances: 99 | roxylint 100 | ``` 101 | 102 | ```r 103 | .onLoad <- function(libname, pkgname) { 104 | if (requireNamespace("roxylint", quietly = TRUE)) { 105 | roxylint::register_linters( 106 | yell = "^[[:upper:] ]*$" 107 | ) 108 | } 109 | } 110 | ``` 111 | -------------------------------------------------------------------------------- /man/add_linters.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/register.R 3 | \name{add_linters} 4 | \alias{add_linters} 5 | \title{Add linters} 6 | \usage{ 7 | add_linters(config, tag, linters, overwrite = FALSE) 8 | } 9 | \arguments{ 10 | \item{config}{A config object to add linters to.} 11 | 12 | \item{tag}{The name of the tag to which to add linters.} 13 | 14 | \item{linters}{New linters to add.} 15 | 16 | \item{overwrite}{A logical value indicating whether existing linters should 17 | be overwritten.} 18 | } 19 | \value{ 20 | \code{NULL}. 21 | } 22 | \description{ 23 | Add linters 24 | } 25 | \keyword{internal} 26 | -------------------------------------------------------------------------------- /man/check_linter.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/check_linter.R 3 | \name{check_linter} 4 | \alias{check_linter} 5 | \alias{check_linter.default} 6 | \alias{check_linter.list} 7 | \alias{check_linter.function} 8 | \alias{check_linter.character} 9 | \title{Apply 'roxygen2' linters} 10 | \usage{ 11 | check_linter(linters, tag, ...) 12 | 13 | \method{check_linter}{default}(linters, tag, ...) 14 | 15 | \method{check_linter}{list}(linters, tag, ...) 16 | 17 | \method{check_linter}{`function`}(linters, tag, ...) 18 | 19 | \method{check_linter}{character}(linters, tag, message = NULL, ...) 20 | } 21 | \arguments{ 22 | \item{linters}{A linters specification, either a \code{function} or 23 | \code{character} regular expression that should be matched, or a \code{list} 24 | of either. See details for more information.} 25 | 26 | \item{tag}{A \code{\link[roxygen2:roxy_tag]{roxygen2::roxy_tag()}}.} 27 | 28 | \item{...}{Additional arguments unused.} 29 | 30 | \item{message}{An optional message to use for an alert.} 31 | } 32 | \value{ 33 | \code{TRUE}, invisibly. However, this function is primarily used for its 34 | side-effect of raising alerts during documentation. 35 | } 36 | \description{ 37 | Provided a list of linters for a given tag, iterate over linters to raise 38 | alerts during the documentation process. 39 | } 40 | \section{Methods (by class)}{ 41 | \itemize{ 42 | \item \code{check_linter(default)}: By default, no linting is performed 43 | 44 | \item \code{check_linter(list)}: A \code{list} of \code{function}s or \code{character} regular expressions. 45 | 46 | If a \code{character} value is named, the name is used as the message for a alert 47 | that is raised when the expression does not match. 48 | 49 | \item \code{check_linter(`function`)}: A \code{function} to evaluate for the given tag 50 | 51 | \code{function}'s are evaluated with the following arguments: 52 | \enumerate{ 53 | \item The \code{\link[roxygen2:roxy_tag]{roxygen2::roxy_tag()}} 54 | \item The contents of the tag's \verb{$val}, as named arguments 55 | } 56 | 57 | Because the number of arguments might not be readily apparent, any function 58 | should accept a trailing \code{...} argument. 59 | 60 | Provided \code{function}s may print lint output, or signal lint output with 61 | messages or warnings. A \code{\link[cli:cli_alert]{cli::cli_alert()}} will reflect the severity of the 62 | function used to emit the output. 63 | 64 | \item \code{check_linter(character)}: A \code{character} regular expressions. 65 | 66 | If a \code{character} value is found, its value is assumed to be a regular 67 | expression which must match a given tag's \code{raw} content (the text as it 68 | appears in the \code{roxygen2} header). 69 | 70 | }} 71 | -------------------------------------------------------------------------------- /man/config.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/config.R 3 | \docType{data} 4 | \name{config} 5 | \alias{config} 6 | \alias{.registered} 7 | \alias{config_load} 8 | \alias{config_find_from} 9 | \alias{config_from_desc} 10 | \alias{config_from_file} 11 | \title{Configuration} 12 | \format{ 13 | An object of class \code{environment} of length 0. 14 | } 15 | \usage{ 16 | .registered 17 | 18 | config_load(path = getwd(), cache = TRUE) 19 | 20 | config_find_from(path) 21 | 22 | config_from_desc(path) 23 | 24 | config_from_file(path) 25 | } 26 | \arguments{ 27 | \item{path}{A file path to use when searching for a config file. Either the 28 | file path to a \code{DESCRIPTION} or the root path of a package, depending on 29 | the context of the function.} 30 | } 31 | \description{ 32 | Various functions for loading, caching and performing configured behaviors 33 | using a user-supplied configuration file. 34 | } 35 | \section{Functions}{ 36 | \itemize{ 37 | \item \code{config_load()}: Load the contents of a config into an environment 38 | 39 | \item \code{config_find_from()}: Load a configuration from a path 40 | 41 | \item \code{config_from_desc()}: Load a configuration from a DESCRIPTION file 42 | 43 | \item \code{config_from_file()}: Load a configuration from a dotfile 44 | 45 | }} 46 | \keyword{datasets} 47 | \keyword{internal} 48 | -------------------------------------------------------------------------------- /man/config_tag_accepts_linters.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/register.R 3 | \name{config_tag_accepts_linters} 4 | \alias{config_tag_accepts_linters} 5 | \title{Check whether a given tag accepts new linters} 6 | \usage{ 7 | config_tag_accepts_linters(config, tag) 8 | } 9 | \arguments{ 10 | \item{config}{A config object to add linters to.} 11 | 12 | \item{tag}{The name of the tag to which to add linters.} 13 | } 14 | \description{ 15 | Linters can be configured to disallow new linter additions by providing a 16 | trailing \code{NULL} value to a list, or setting the value itself to \code{NULL}. 17 | } 18 | \keyword{internal} 19 | -------------------------------------------------------------------------------- /man/demo_tag.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/utils.R 3 | \name{demo_tag} 4 | \alias{demo_tag} 5 | \title{Get that tag!} 6 | \usage{ 7 | demo_tag(str) 8 | } 9 | \arguments{ 10 | \item{str}{A 'roxygen2' tag string.} 11 | } 12 | \value{ 13 | A \code{\link[roxygen2:roxy_tag]{roxygen2::roxy_tag()}}. 14 | } 15 | \description{ 16 | Tools for inspecting \code{\link[roxygen2:roxy_tag]{roxygen2::roxy_tag()}}s. This can be helpful for 17 | exploring the intermediate tag objects. For example, you can use this 18 | function to generate a tag and explore the named values in \verb{$val}. 19 | } 20 | \examples{ 21 | demo_tag("@param var abc") 22 | 23 | } 24 | -------------------------------------------------------------------------------- /man/format_tag_prefix.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/roxy_alerts.R 3 | \name{format_tag_prefix} 4 | \alias{format_tag_prefix} 5 | \title{Format a tag for alert context} 6 | \usage{ 7 | format_tag_prefix(x) 8 | } 9 | \arguments{ 10 | \item{x}{A \code{\link[roxygen2:roxy_tag]{roxygen2::roxy_tag()}}.} 11 | } 12 | \value{ 13 | A formatted string. 14 | } 15 | \description{ 16 | Format a tag for alert context 17 | } 18 | \keyword{internal} 19 | -------------------------------------------------------------------------------- /man/if-not-null-else.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/utils.R 3 | \name{if-not-null-else} 4 | \alias{if-not-null-else} 5 | \alias{\%||\%} 6 | \title{If-not-null-else} 7 | \usage{ 8 | lhs \%||\% rhs 9 | } 10 | \description{ 11 | If-not-null-else 12 | } 13 | \keyword{internal} 14 | -------------------------------------------------------------------------------- /man/into_roxy_alert.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/roxy_alerts.R 3 | \name{into_roxy_alert} 4 | \alias{into_roxy_alert} 5 | \title{Capture output and re-emit as a cli alert} 6 | \usage{ 7 | into_roxy_alert(tag, expr) 8 | } 9 | \arguments{ 10 | \item{tag}{A \code{\link[roxygen2:roxy_tag]{roxygen2::roxy_tag()}} to use as context for the alert.} 11 | 12 | \item{expr}{An expression to evaluate.} 13 | } 14 | \value{ 15 | \code{TRUE}, invisibly. 16 | } 17 | \description{ 18 | Capture output and re-emit as a cli alert 19 | } 20 | \keyword{internal} 21 | -------------------------------------------------------------------------------- /man/linters.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/linters.R 3 | \docType{data} 4 | \name{linters} 5 | \alias{linters} 6 | \alias{lint_starts_lowercase} 7 | \alias{lint_full_stop} 8 | \alias{lint_no_full_stop} 9 | \alias{lint_sentence_case} 10 | \alias{lint_title_case} 11 | \alias{tidy_title} 12 | \alias{tidy_param} 13 | \alias{tidy_return} 14 | \alias{tidy_seealso} 15 | \alias{tidy} 16 | \title{Assorted linters} 17 | \format{ 18 | An object of class \code{list} of length 4. 19 | } 20 | \usage{ 21 | lint_starts_lowercase(x, ...) 22 | 23 | lint_full_stop(x, ...) 24 | 25 | lint_no_full_stop(x, ...) 26 | 27 | lint_sentence_case(x, ...) 28 | 29 | lint_title_case(x, ...) 30 | 31 | tidy_title(x, ...) 32 | 33 | tidy_param(x, name, description, ...) 34 | 35 | tidy_return(x, ...) 36 | 37 | tidy_seealso(x, ...) 38 | 39 | tidy 40 | } 41 | \arguments{ 42 | \item{x}{A \code{\link[roxygen2:roxy_tag]{roxygen2::roxy_tag()}} that is the subject of linting.} 43 | 44 | \item{...}{Additional arguments unused.} 45 | 46 | \item{name, description}{Used for \code{\link[roxygen2:roxy_tag]{roxygen2::roxy_tag()}}-specific linters.} 47 | } 48 | \description{ 49 | Preconfigured linters, either as a collective list of linters or 50 | individually. "tidy" linters implement guidelines from the tidyverse style 51 | guide. 52 | } 53 | \details{ 54 | Refer to the individual \code{\link[roxygen2:roxy_tag]{roxygen2::roxy_tag()}} for the respective tag for 55 | argument details. 56 | } 57 | \section{Functions}{ 58 | \itemize{ 59 | \item \code{lint_starts_lowercase()}: Lowercase start linting. (uses \verb{$raw} for \code{\link[roxygen2:roxy_tag]{roxygen2::roxy_tag()}}s) 60 | 61 | \item \code{lint_full_stop()}: Ends in a full stop. (uses \verb{$raw} for \code{\link[roxygen2:roxy_tag]{roxygen2::roxy_tag()}}s) 62 | 63 | \item \code{lint_no_full_stop()}: Does not end in a full stop. (uses \verb{$raw} for \code{\link[roxygen2:roxy_tag]{roxygen2::roxy_tag()}}s) 64 | 65 | \item \code{lint_sentence_case()}: Sentence case linting (uses \verb{$raw} for \code{\link[roxygen2:roxy_tag]{roxygen2::roxy_tag()}}s) 66 | 67 | \item \code{lint_title_case()}: Title case linting 68 | 69 | \item \code{tidy_title()}: Tidy 'Sentence case' titles 70 | 71 | \item \code{tidy_param()}: Tidy 'Sentence case' \verb{@param} definitions 72 | 73 | \item \code{tidy_return()}: Tidy 'Sentence case' \verb{@return} definitions 74 | 75 | \item \code{tidy_seealso()}: Tidy 'Sentence case' \verb{@seealso} definitions 76 | 77 | \item \code{tidy}: A list of all tidyverse style guide inspired linters 78 | 79 | }} 80 | \keyword{datasets} 81 | -------------------------------------------------------------------------------- /man/register_linters.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/register.R 3 | \name{register_linters} 4 | \alias{register_linters} 5 | \title{Register linters for new tags} 6 | \usage{ 7 | register_linters(..., linters = list(...), .overwrite = FALSE) 8 | } 9 | \arguments{ 10 | \item{...}{Linters to register, with parameter names used to identify the 11 | associated tag.} 12 | 13 | \item{linters}{Optionally, provide a named list of linters directly.} 14 | 15 | \item{.overwrite}{Whether existing linters should be overwritten. Intended to 16 | be used sparingly. For example, this could be used to implement an entire 17 | new style guide without inheriting defaults.} 18 | } 19 | \value{ 20 | \code{TRUE}, invisibly. 21 | } 22 | \description{ 23 | A registration mechanism for other packages to provide linters, either for 24 | custom tags or to implement their own linting styles. 25 | } 26 | -------------------------------------------------------------------------------- /man/roxygen/meta.R: -------------------------------------------------------------------------------- 1 | list( 2 | markdown = TRUE, 3 | packages = c( 4 | "roxylint" 5 | ), 6 | roclets = c( 7 | "namespace", 8 | "rd", 9 | "roxylint::roxylint" 10 | ) 11 | ) 12 | -------------------------------------------------------------------------------- /man/roxylint.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/roclet.R 3 | \name{roxylint} 4 | \alias{roxylint} 5 | \title{Roclet for 'roxygen2' style linting} 6 | \usage{ 7 | roxylint() 8 | } 9 | \value{ 10 | A \code{roxylint} \code{\link[roxygen2:roclet]{roxygen2::roclet()}}. 11 | } 12 | \description{ 13 | Roclets used to embed linters during documentation. To use, add the roclet in 14 | your \code{DESCRIPTION} file. 15 | } 16 | \details{ 17 | \if{html}{\out{
}}\preformatted{Config/Needs/documentation: roxylint 18 | Roxygen: 19 | list( 20 | markdown = TRUE, 21 | roclets = c("namespace", "rd", "roxylint::roxylint") 22 | ) 23 | }\if{html}{\out{
}} 24 | } 25 | -------------------------------------------------------------------------------- /man/roxylint/meta.R: -------------------------------------------------------------------------------- 1 | list( 2 | verbose = TRUE, 3 | linters = roxylint::tidy 4 | ) 5 | -------------------------------------------------------------------------------- /pkgdown/_pkgdown.yml: -------------------------------------------------------------------------------- 1 | url: ~ 2 | template: 3 | bootstrap: 5 4 | 5 | reference: 6 | - title: Roclet 7 | - contents: 8 | - roxylint 9 | - title: Linters 10 | - contents: 11 | - linters 12 | - register_linters 13 | - title: Utilities 14 | - contents: 15 | - check_linter 16 | - demo_tag 17 | --------------------------------------------------------------------------------