flint
is a small R package to find and replace lints in R code.
- Lints detection with
lint()
- Automatic replacement of lints with
fix()
- Compatibility with (some) lintr rules
- Fast
- Low-dependency
flint
is powered by astgrepr
, which is itself built on the Rust crate ast-grep
.
Installation
install.packages('flint', repos = c('https://etiennebacher.r-universe.dev'))
Usage
Optional setup:
-
setup_flint()
: creates the folderflint
and populates it with built-in rules as well as a cache file. You can modify those rules or add new ones if you want more control.
You can use flint
as-is, without any setup. However, running setup_flint()
enables the use of caching, meaning that the subsequent runs will be faster. It is also gives you a place where you can store custom rules for your project/package.
The everyday usage consists of two functions:
-
lint()
looks for lints in R files; -
fix()
looks for lints in R files and automatically applies their replacement (if any).
One can also experiment with flint::lint_text()
and flint::fix_text()
:
flint::lint_text("
any(is.na(x))
any(duplicated(y))
")
#> Original code: any(is.na(x))
#> Suggestion: anyNA(x) is better than any(is.na(x)).
#> Rule ID: any_na-1
#>
#> Original code: any(duplicated(y))
#> Suggestion: anyDuplicated(x, ...) > 0 is better than any(duplicated(x), ...).
#> Rule ID: any_duplicated-1
flint::fix_text("
any(is.na(x))
any(duplicated(y))
")
#> Old code:
#> any(is.na(x))
#> any(duplicated(y))
#>
#> New code:
#> anyNA(x)
#> anyDuplicated(y) > 0
Comparison with existing tools
The most used tool for lints detection in R is lintr
. However, lintr
’s performance is not optimal when it is applied on medium to large packages. Also, lintr
cannot perform automatic replacement of lints.
styler
is a package to clean code by fixing indentation and other things, but doesn’t perform code replacement based on lints.
flint
is quite performant. This is a small benchmark on 3.5k lines of code with only three linters:
file <- system.file("bench/test.R", package = "flint")
bench::mark(
lintr = lintr::lint(
file, linters = list(lintr::any_duplicated_linter(), lintr::any_is_na_linter(),
lintr::matrix_apply_linter(), lintr::function_return_linter(),
lintr::lengths_linter(), lintr::T_and_F_symbol_linter(),
lintr::undesirable_function_linter(), lintr::expect_length_linter())
),
flint = flint::lint(
file, linters = list(flint::any_duplicated_linter(), flint::any_is_na_linter(),
flint::matrix_apply_linter(), flint::function_return_linter(),
flint::lengths_linter(), flint::T_and_F_symbol_linter(),
flint::undesirable_function_linter(), flint::expect_length_linter()),
verbose = FALSE
),
check = FALSE
)
#> Warning: Some expressions had a GC in every iteration; so filtering is
#> disabled.
#> # A tibble: 2 × 6
#> expression min median `itr/sec` mem_alloc `gc/sec`
#> <bch:expr> <bch:tm> <bch:tm> <dbl> <bch:byt> <dbl>
#> 1 lintr 4.06s 4.06s 0.246 317.32MB 7.38
#> 2 flint 204.91ms 216.56ms 4.55 1.71MB 1.52
Contributing
Did you find some bugs or some errors in the documentation? Do you want flint
to support more rules?
Take a look at the contributing guide for instructions on bug report and pull requests.
Acknowledgements
The website theme was heavily inspired by Matthew Kay’s ggblend
package: https://mjskay.github.io/ggblend/.