Skip to content

Commit

Permalink
Commas trailing (#2104)
Browse files Browse the repository at this point in the history
  • Loading branch information
MEO265 authored Sep 5, 2023
1 parent 960d863 commit 02732a2
Show file tree
Hide file tree
Showing 7 changed files with 96 additions and 8 deletions.
1 change: 1 addition & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
* `keyword_quote_linter()` for finding unnecessary or discouraged quoting of symbols in assignment, function arguments, or extraction (part of #884, @MichaelChirico). Quoting is unnecessary when the target is a valid R name, e.g. `c("a" = 1)` can be `c(a = 1)`. The same goes to assignment (`"a" <- 1`) and extraction (`x$"a"`). Where quoting is necessary, the linter encourages doing so with backticks (e.g. `` x$`a b` `` instead of `x$"a b"`).
* `length_levels_linter()` for using the specific function `nlevels()` instead of checking `length(levels(x))` (part of #884, @MichaelChirico).
* `if_not_else_linter()` for encouraging `if` statements to be structured as `if (A) x else y` instead of `if (!A) y else x` (part of #884, @MichaelChirico).
* `commas_linter()` gains an option `allow_trailing` (default `FALSE`) to allow trailing commas while indexing. (#2104, @MEO265)

## Changes to defaults

Expand Down
21 changes: 19 additions & 2 deletions R/commas_linter.R
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
#'
#' Check that all commas are followed by spaces, but do not have spaces before them.
#'
#' @param allow_trailing If `TRUE`, the linter allows a comma to be followed
#' directly by a closing bracket without a space.
#'
#' @examples
#' # will produce lints
#' lint(
Expand All @@ -19,6 +22,11 @@
#' linters = commas_linter()
#' )
#'
#' lint(
#' text = "x[1,]",
#' linters = commas_linter()
#' )
#'
#' # okay
#' lint(
#' text = "switch(op, x = foo, y = bar)",
Expand All @@ -40,12 +48,17 @@
#' linters = commas_linter()
#' )
#'
#' lint(
#' text = "x[1,]",
#' linters = commas_linter(allow_trailing = TRUE)
#' )
#'
#' @evalRd rd_tags("commas_linter")
#' @seealso
#' - [linters] for a complete list of linters available in lintr.
#' - <https://style.tidyverse.org/syntax.html#commas>
#' @export
commas_linter <- function() {
commas_linter <- function(allow_trailing = FALSE) {
# conditions are in carefully-chosen order for performance --
# an expression like c(a,b,c,....) with many elements can have
# a huge number of preceding-siblings and the performance of
Expand All @@ -58,7 +71,11 @@ commas_linter <- function() {
@line1 = preceding-sibling::*[1]/@line1 and
not(preceding-sibling::*[1][self::OP-COMMA or self::EQ_SUB])
]"
xpath_after <- "//OP-COMMA[@line1 = following-sibling::*[1]/@line1 and @col1 = following-sibling::*[1]/@col1 - 1]"
xpath_after <- paste0(
"//OP-COMMA[@line1 = following-sibling::*[1]/@line1 and @col1 = following-sibling::*[1]/@col1 - 1 ",
if (allow_trailing) "and not(following-sibling::*[1][self::OP-RIGHT-BRACKET or self::RBB or self::OP-RIGHT-PAREN])",
"]"
)

Linter(function(source_expression) {
if (!is_lint_level(source_expression, "expression")) {
Expand Down
2 changes: 1 addition & 1 deletion inst/lintr/linters.csv
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ boolean_arithmetic_linter,efficiency best_practices readability
brace_linter,style readability default configurable
class_equals_linter,best_practices robustness consistency
closed_curly_linter,style readability deprecated configurable
commas_linter,style readability default
commas_linter,style readability default configurable
commented_code_linter,style readability best_practices default
condition_message_linter,best_practices consistency
conjunct_test_linter,package_development best_practices readability configurable pkg_testthat
Expand Down
18 changes: 16 additions & 2 deletions man/commas_linter.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions man/configurable_linters.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions man/linters.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

57 changes: 56 additions & 1 deletion tests/testthat/test-commas_linter.R
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
test_that("returns the correct linting", {
test_that("returns the correct linting (with default parameters)", {
linter <- commas_linter()
msg_after <- rex::rex("Commas should always have a space after.")
msg_before <- rex::rex("Commas should never have a space before.")
Expand All @@ -13,6 +13,9 @@ test_that("returns the correct linting", {
expect_lint("fun(1\n,1)", msg_after, linter)
expect_lint("fun(1,1)", msg_after, linter)
expect_lint("\nfun(1,1)", msg_after, linter)
expect_lint("a(1,)", msg_after, linter)
expect_lint("a[1,]", msg_after, linter)
expect_lint("a[[1,]]", msg_after, linter)
expect_lint(
"fun(1 ,1)",
list(
Expand Down Expand Up @@ -46,3 +49,55 @@ test_that("returns the correct linting", {
linter
)
})

test_that("returns the correct linting (with 'allow_trailing' set)", {
linter <- commas_linter(allow_trailing = TRUE)
msg_after <- rex::rex("Commas should always have a space after.")
msg_before <- rex::rex("Commas should never have a space before.")

expect_lint("blah", NULL, linter)
expect_lint("fun(1, 1)", NULL, linter)
expect_lint("fun(1,\n 1)", NULL, linter)
expect_lint("fun(1,\n1)", NULL, linter)
expect_lint("fun(1\n,\n1)", NULL, linter)
expect_lint("fun(1\n ,\n1)", NULL, linter)
expect_lint("a[1,]", NULL, linter)
expect_lint("a(1,)", NULL, linter)

expect_lint("fun(1\n,1)", msg_after, linter)
expect_lint("fun(1,1)", msg_after, linter)
expect_lint("\nfun(1,1)", msg_after, linter)
expect_lint(
"fun(1 ,1)",
list(
msg_before,
msg_after
),
linter
)

expect_lint("\"fun(1 ,1)\"", NULL, linter)
expect_lint("a[1, , 2]", NULL, linter)
expect_lint("a[1, , 2, , 3]", NULL, linter)
expect_lint("a[[1,]]", NULL, linter)

expect_lint("switch(op, x = foo, y = bar)", NULL, linter)
expect_lint("switch(op, x = , y = bar)", NULL, linter)
expect_lint("switch(op, \"x\" = , y = bar)", NULL, linter)
expect_lint("switch(op, x = ,\ny = bar)", NULL, linter)

expect_lint("switch(op, x = foo , y = bar)", msg_before, linter)
expect_lint("switch(op, x = foo , y = bar)", msg_before, linter)
expect_lint("switch(op , x = foo, y = bar)", msg_before, linter)
expect_lint("switch(op, x = foo, y = bar(a = 4 , b = 5))", msg_before, linter)
expect_lint("fun(op, x = foo , y = switch(bar, a = 4, b = 5))", msg_before, linter)

expect_lint(
"fun(op ,bar)",
list(
list(message = msg_before, column_number = 7L, ranges = list(c(7L, 10L))),
list(message = msg_after, column_number = 12L, ranges = list(c(12L, 12L)))
),
linter
)
})

0 comments on commit 02732a2

Please sign in to comment.