diff --git a/NAMESPACE b/NAMESPACE index a32ba73f20..7e05e77be6 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -19,6 +19,7 @@ export(Lint) export(Linter) export(T_and_F_symbol_linter) export(absolute_path_linter) +export(all_linters) export(all_undesirable_functions) export(all_undesirable_operators) export(any_duplicated_linter) diff --git a/NEWS.md b/NEWS.md index 5267e1d47b..397429ebd1 100644 --- a/NEWS.md +++ b/NEWS.md @@ -91,6 +91,8 @@ * Added `format()` functions for `lint` and `lints` (#1784, @AshesITR) +* `all_linters()` function provides an easy way to access all available linters (#1843, @IndrajeetPatil) + ### New linters * `unnecessary_lambda_linter()`: detect unnecessary lambdas (anonymous functions), e.g. diff --git a/R/use_lintr.R b/R/use_lintr.R index b1d48eef3c..2a40311c46 100644 --- a/R/use_lintr.R +++ b/R/use_lintr.R @@ -37,7 +37,7 @@ use_lintr <- function(path = ".", type = c("tidyverse", "full")) { encoding = '"UTF-8"' ), full = list( - linters = 'linters_with_tags(tags = NULL, packages = "lintr") # see vignette("lintr")', + linters = 'all_linters(packages = "lintr") # see vignette("lintr")', encoding = '"UTF-8"', exclusions = 'list("renv", "packrat") # see ?lintr::exclude' ) diff --git a/R/with.R b/R/with.R index 65b27c256c..fd5a770090 100644 --- a/R/with.R +++ b/R/with.R @@ -8,7 +8,14 @@ #' @param defaults named list of elements to modify. #' @return A modified list of elements, sorted by name. To achieve this sort in a platform-independent way, two #' transformations are applied to the names: (1) replace `_` with `0` and (2) convert [tolower()]. -#' @seealso [linters_with_tags], [linters_with_defaults] for creating linter lists. +#' +#' @seealso +#' - [linters_with_defaults] for basing off lintr's set of default linters. +#' - [all_linters] for basing off all available linters in lintr. +#' - [linters_with_tags] for basing off tags attached to linters, possibly across multiple packages. +#' - [available_linters] to get a data frame of available linters. +#' - [linters] for a complete list of linters available in lintr. +#' #' @examples #' # custom list of undesirable functions: #' # remove `sapply` (using `NULL`) @@ -65,19 +72,17 @@ modify_defaults <- function(defaults, ...) { #' #' @return A modified list of linters. #' @seealso -#' [linters_with_defaults] for basing off lintr's set of default linters. -#' [available_linters] to get a data frame of available linters. -#' [linters] for a complete list of linters available in lintr. +#' - [linters_with_defaults] for basing off lintr's set of default linters. +#' - [all_linters] for basing off all available linters in lintr. +#' - [available_linters] to get a data frame of available linters. +#' - [linters] for a complete list of linters available in lintr. +#' #' @examples #' # `linters_with_defaults()` and `linters_with_tags("default")` are the same: #' all.equal(linters_with_defaults(), linters_with_tags("default")) #' #' # Get all linters useful for package development -#' linters <- linters_with_tags(tags = "package_development") -#' names(linters) -#' -#' # Get all linters provided by lintr -#' linters <- linters_with_tags(tags = NULL) +#' linters <- linters_with_tags(tags = c("package_development", "style")) #' names(linters) #' #' # Get all linters tagged as "default" from lintr and mypkg @@ -117,6 +122,23 @@ linters_with_tags <- function(tags, ..., packages = "lintr", exclude_tags = "dep modify_defaults(..., defaults = tagged_linters) } +#' Create a linter configuration based on all available linters +#' +#' @inheritParams linters_with_tags +#' +#' @examples +#' names(all_linters()) +#' +#' @seealso +#' - [linters_with_defaults] for basing off lintr's set of default linters. +#' - [linters_with_tags] for basing off tags attached to linters, possibly across multiple packages. +#' - [available_linters] to get a data frame of available linters. +#' - [linters] for a complete list of linters available in lintr. +#' @export +all_linters <- function(packages = "lintr", ...) { + linters_with_tags(tags = NULL, packages = packages, ...) +} + #' Create a linter configuration based on defaults #' #' Make a new list based on \pkg{lintr}'s default linters. @@ -148,6 +170,7 @@ linters_with_tags <- function(tags, ..., packages = "lintr", exclude_tags = "dep #' #' @seealso #' - [linters_with_tags] for basing off tags attached to linters, possibly across multiple packages. +#' - [all_linters] for basing off all available linters in lintr. #' - [available_linters] to get a data frame of available linters. #' - [linters] for a complete list of linters available in lintr. #' @export @@ -180,6 +203,8 @@ with_defaults <- function(..., default = default_linters) { linters_with_defaults(..., defaults = default) } +#' @keywords internal +#' @noRd call_linter_factory <- function(linter_factory, linter_name, package) { linter <- tryCatch( linter_factory(), @@ -192,6 +217,8 @@ call_linter_factory <- function(linter_factory, linter_name, package) { linter } +#' @keywords internal +#' @noRd guess_names <- function(..., missing_index) { args <- as.character(eval(substitute(alist(...)[missing_index]))) # foo_linter(x=1) => "foo" diff --git a/README.md b/README.md index b09c15e46e..29b034ac08 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,14 @@ # lintr + [![R build status](https://github.com/r-lib/lintr/workflows/R-CMD-check/badge.svg)](https://github.com/r-lib/lintr/actions) [![codecov.io](https://codecov.io/github/r-lib/lintr/coverage.svg?branch=main)](https://codecov.io/github/r-lib/lintr?branch=main) [![CRAN_Status_Badge](https://www.r-pkg.org/badges/version/lintr)](https://cran.r-project.org/package=lintr) -`lintr` provides [static code analysis for R](https://en.wikipedia.org/wiki/Static_program_analysis). It checks for adherence to a given style, identifying syntax errors and possible semantic issues, then reports them to you so you can take action. Watch lintr in action in the following animation: +`{lintr}` provides [static code analysis for R](https://en.wikipedia.org/wiki/Static_program_analysis). It checks for adherence to a given style, identifying syntax errors and possible semantic issues, then reports them to you so you can take action. Watch lintr in action in the following animation: ![](man/figures/demo.gif "lintr demo") -`lintr` is complementary to [the `styler` package](https://github.com/r-lib/styler) which automatically restyles code, eliminating some of the problems that `lintr` can detect. +`{lintr}` is complementary to [the `{styler}` package](https://github.com/r-lib/styler) which automatically restyles code, eliminating some of the problems that `{lintr}` can detect. ## Installation @@ -26,15 +27,15 @@ remotes::install_github("r-lib/lintr") ## Usage +And then you can create a configuration file and run selected linters: + ```R -# in a project: lintr::use_lintr(type = "tidyverse") -usethis::use_github_action("lint-project") + +# in a project: lintr::lint_dir() # in a package: -lintr::use_lintr(type = "tidyverse") -usethis::use_github_action("lint") lintr::lint_package() ``` @@ -45,7 +46,19 @@ To see a list of linters included for each configuration: names(lintr::linters_with_defaults()) # full -names(lintr::linters_with_tags(tags = NULL)) +names(lintr::all_linters()) +``` + +### Setting up GitHub Actions + +`{usethis}` provides helper functions to generate lint workflows for GitHub Actions: + +```R +# in a project: +usethis::use_github_action("lint-project") + +# in a package: +usethis::use_github_action("lint") ``` You can also run lintr during continuous integration or within your IDE or text editor. See `vignette("continuous-integration")` and `vignette("editors")` for more details. diff --git a/man/all_linters.Rd b/man/all_linters.Rd new file mode 100644 index 0000000000..56db4a814f --- /dev/null +++ b/man/all_linters.Rd @@ -0,0 +1,30 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/with.R +\name{all_linters} +\alias{all_linters} +\title{Create a linter configuration based on all available linters} +\usage{ +all_linters(packages = "lintr", ...) +} +\arguments{ +\item{packages}{A character vector of packages to search for linters.} + +\item{...}{Arguments of elements to change. If unnamed, the argument is automatically named. +If the named argument already exists in the list of linters, it is replaced by the new element. +If it does not exist, it is added. If the value is \code{NULL}, the linter is removed.} +} +\description{ +Create a linter configuration based on all available linters +} +\examples{ +names(all_linters()) + +} +\seealso{ +\itemize{ +\item \link{linters_with_defaults} for basing off lintr's set of default linters. +\item \link{linters_with_tags} for basing off tags attached to linters, possibly across multiple packages. +\item \link{available_linters} to get a data frame of available linters. +\item \link{linters} for a complete list of linters available in lintr. +} +} diff --git a/man/linters_with_defaults.Rd b/man/linters_with_defaults.Rd index 00186c6706..f53bf72b9b 100644 --- a/man/linters_with_defaults.Rd +++ b/man/linters_with_defaults.Rd @@ -47,6 +47,7 @@ names(my_linters) \seealso{ \itemize{ \item \link{linters_with_tags} for basing off tags attached to linters, possibly across multiple packages. +\item \link{all_linters} for basing off all available linters in lintr. \item \link{available_linters} to get a data frame of available linters. \item \link{linters} for a complete list of linters available in lintr. } diff --git a/man/linters_with_tags.Rd b/man/linters_with_tags.Rd index 19078404cc..82c41598fa 100644 --- a/man/linters_with_tags.Rd +++ b/man/linters_with_tags.Rd @@ -33,11 +33,7 @@ or to be put in your configuration file. all.equal(linters_with_defaults(), linters_with_tags("default")) # Get all linters useful for package development -linters <- linters_with_tags(tags = "package_development") -names(linters) - -# Get all linters provided by lintr -linters <- linters_with_tags(tags = NULL) +linters <- linters_with_tags(tags = c("package_development", "style")) names(linters) # Get all linters tagged as "default" from lintr and mypkg @@ -46,7 +42,10 @@ if (FALSE) { } } \seealso{ -\link{linters_with_defaults} for basing off lintr's set of default linters. -\link{available_linters} to get a data frame of available linters. -\link{linters} for a complete list of linters available in lintr. +\itemize{ +\item \link{linters_with_defaults} for basing off lintr's set of default linters. +\item \link{all_linters} for basing off all available linters in lintr. +\item \link{available_linters} to get a data frame of available linters. +\item \link{linters} for a complete list of linters available in lintr. +} } diff --git a/man/modify_defaults.Rd b/man/modify_defaults.Rd index 33c7cf8528..1913f84e0c 100644 --- a/man/modify_defaults.Rd +++ b/man/modify_defaults.Rd @@ -35,5 +35,11 @@ my_undesirable_functions <- modify_defaults( names(my_undesirable_functions) } \seealso{ -\link{linters_with_tags}, \link{linters_with_defaults} for creating linter lists. +\itemize{ +\item \link{linters_with_defaults} for basing off lintr's set of default linters. +\item \link{all_linters} for basing off all available linters in lintr. +\item \link{linters_with_tags} for basing off tags attached to linters, possibly across multiple packages. +\item \link{available_linters} to get a data frame of available linters. +\item \link{linters} for a complete list of linters available in lintr. +} } diff --git a/tests/testthat/test-with.R b/tests/testthat/test-with.R index 2208910849..87a2232634 100644 --- a/tests/testthat/test-with.R +++ b/tests/testthat/test-with.R @@ -92,3 +92,17 @@ test_that("linters_with_defaults(default = .) is supported with a deprecation wa expect_silent(linters <- linters_with_defaults(defaults = list(), default = default)) expect_named(linters, "default") }) + +test_that("all_linters contains all available linters", { + all_linters <- all_linters(packages = "lintr") + + expect_identical(linters_with_tags(NULL, packages = "lintr"), all_linters) + expect_length(all_linters, nrow(available_linters())) +}) + +test_that("all_linters respects ellipsis argument", { + expect_identical( + linters_with_tags(tags = NULL, implicit_integer_linter = NULL), + all_linters(packages = "lintr", implicit_integer_linter = NULL) + ) +}) diff --git a/vignettes/lintr.Rmd b/vignettes/lintr.Rmd index 4e738993a0..a413433bb1 100644 --- a/vignettes/lintr.Rmd +++ b/vignettes/lintr.Rmd @@ -150,6 +150,7 @@ If an Encoding is found in a `.Rproj` file or a `DESCRIPTION` file, that encodin If you only want to customize some linters, you can use the helper function `linters_with_defaults()`, which will keep all unnamed linters with the default settings. Disable a linter by passing `NULL`. + For example, to set the line length limit to 120 characters and globally disable the `no_tab_linter`, you can put this into your `.lintr`: ``` r @@ -194,29 +195,49 @@ defaults_table <- data.frame( knitr::kable(defaults_table) ``` +Another way to customize linters is by specifying tags in `linters_with_tags()`. +The available tags are listed below: + +```{r} +lintr::available_tags(packages = "lintr") +``` + +You can select tags of interest to see which linters are included: + +```{r} +linters <- lintr::linters_with_tags(tags = c("package_development", "readability")) +names(linters) +``` + +You can include tag-based linters in the configuration file, and customize them further: + +```yaml +linters: linters_with_tags( + tags = c("package_development", "readability"), + yoda_test_linter = NULL + ) +``` + #### Using all available linters The default lintr configuration includes only linters relevant to the tidyverse style guide, but there are many other linters available in `{lintr}`. You can see a list of all available linters using ```{r} -names(lintr::linters_with_tags(tags = NULL)) +names(lintr::all_linters()) ``` If you want to use all available linters, you can include this in your `.lintr` file: -```r -linters: linters_with_defaults( - defaults = linters_with_tags(tags = NULL) - ) +```yaml +linters: all_linters() ``` If you want to use all available linters *except* a few, you can exclude them using `NULL`: -```r -linters: linters_with_defaults( +```yaml +linters: all_linters( commented_code_linter = NULL, - implicit_integer_linter = NULL, - defaults = linters_with_tags(tags = NULL) + implicit_integer_linter = NULL ) ```