Coding is an art, but most of the code we write is more or less dreadful. Many tried to teach us how to write code less dreadful, be it implicitly as Kernighan and Ritchie (1988) did, be it explicitly as Martin (2008) did.
If you are concerned about your coding style, you will probably want to lint your codes with lintr or even reformat it using formatR or styler (the latter gives better results in my opinion).
This package tries to help you with your code`s layout: it checks our code for files too long or wide, functions with too many lines, too wide lines, too many arguments or too many levels of nesting. Note that it’s not a static code analyser like lintr.
You can use cleanr to check the layout of a file:
path <- system.file("runit_tests", "runit_wrappers.R", package = "cleanr")
print(cleanr::check_file_layout(path))
## [1] TRUE
Okay, looks good. Let’s make it look worse.
cleanr is controlled by a list of options named “cleanr”. You can retrieve it either via options("cleanr")
or via a convenience function:
cleanr::get_cleanr_options(flatten_list = FALSE)
## $max_file_width
## [1] 80
##
## $max_file_length
## [1] 300
##
## $max_lines
## [1] 65
##
## $max_lines_of_code
## [1] 50
##
## $max_num_arguments
## [1] 5
##
## $max_nesting_depth
## [1] 3
##
## $max_line_width
## [1] 80
##
## $check_return
## [1] TRUE
You can change any of those options on the fly (leaving the options set untouched). The real life
cleanr::check_file_layout(path, max_file_width = 79)
produces conditions that would stop this vignette from getting compiled, this is why we here assert these conditions:
print(tools::assertCondition(cleanr::check_file_layout(path,
max_file_width = 79),
c("cleanr", "error", "condition"))
)
## [[1]]
## <error in cleanr::check_file_layout(path, max_file_width = 79): /tmp/RtmpfBzpiT/Rinst2e2c49c9879f/cleanr/runit_tests/runit_wrappers.R: line 14 counts 80 characters.
## /tmp/RtmpfBzpiT/Rinst2e2c49c9879f/cleanr/runit_tests/runit_wrappers.R: line 43 counts 80 characters.>
##
## [[2]]
## <error in cleanr::check_file_layout(path, max_file_width = 79): /tmp/RtmpfBzpiT/Rinst2e2c49c9879f/cleanr/runit_tests/runit_wrappers.R: line 14 counts 80 characters.
## /tmp/RtmpfBzpiT/Rinst2e2c49c9879f/cleanr/runit_tests/runit_wrappers.R: line 43 counts 80 characters.>
You can set options using the convenience function:
cleanr::set_cleanr_options(max_file_width = 75,
max_file_length = 20)
print(tools::assertCondition(cleanr::check_file_layout(path),
c("cleanr", "error", "condition"))
)
## [[1]]
## <error in cleanr::check_file_layout(path): /tmp/RtmpfBzpiT/Rinst2e2c49c9879f/cleanr/runit_tests/runit_wrappers.R: line 13 counts 76 characters.
## /tmp/RtmpfBzpiT/Rinst2e2c49c9879f/cleanr/runit_tests/runit_wrappers.R: line 14 counts 80 characters.
## /tmp/RtmpfBzpiT/Rinst2e2c49c9879f/cleanr/runit_tests/runit_wrappers.R: line 32 counts 77 characters.
## /tmp/RtmpfBzpiT/Rinst2e2c49c9879f/cleanr/runit_tests/runit_wrappers.R: line 36 counts 78 characters.
## /tmp/RtmpfBzpiT/Rinst2e2c49c9879f/cleanr/runit_tests/runit_wrappers.R: line 42 counts 78 characters.
## /tmp/RtmpfBzpiT/Rinst2e2c49c9879f/cleanr/runit_tests/runit_wrappers.R: line 43 counts 80 characters.
## /tmp/RtmpfBzpiT/Rinst2e2c49c9879f/cleanr/runit_tests/runit_wrappers.R: 44 lines in file.>
##
## [[2]]
## <error in cleanr::check_file_layout(path): /tmp/RtmpfBzpiT/Rinst2e2c49c9879f/cleanr/runit_tests/runit_wrappers.R: line 13 counts 76 characters.
## /tmp/RtmpfBzpiT/Rinst2e2c49c9879f/cleanr/runit_tests/runit_wrappers.R: line 14 counts 80 characters.
## /tmp/RtmpfBzpiT/Rinst2e2c49c9879f/cleanr/runit_tests/runit_wrappers.R: line 32 counts 77 characters.
## /tmp/RtmpfBzpiT/Rinst2e2c49c9879f/cleanr/runit_tests/runit_wrappers.R: line 36 counts 78 characters.
## /tmp/RtmpfBzpiT/Rinst2e2c49c9879f/cleanr/runit_tests/runit_wrappers.R: line 42 counts 78 characters.
## /tmp/RtmpfBzpiT/Rinst2e2c49c9879f/cleanr/runit_tests/runit_wrappers.R: line 43 counts 80 characters.
## /tmp/RtmpfBzpiT/Rinst2e2c49c9879f/cleanr/runit_tests/runit_wrappers.R: 44 lines in file.>
Files too long or too wide are hard to read. You can check for a file’s layout, i.e. for its lines’ width and for the number of its lines. You have already seen how to check for file layout:
cleanr::set_cleanr_options(reset = TRUE)
path <- system.file("source", "R", "checks.R", package = "cleanr")
print(cleanr::check_file_layout(path))
## [1] TRUE
We override some option by argument:
print(tools::assertCondition(cleanr::check_file_layout(path,
max_file_length = 100),
c("cleanr", "error", "condition"))
)
## [[1]]
## <error in cleanr::check_file_layout(path, max_file_length = 100): /tmp/RtmpfBzpiT/Rinst2e2c49c9879f/cleanr/source/R/checks.R: 256 lines in file.>
##
## [[2]]
## <error in cleanr::check_file_layout(path, max_file_length = 100): /tmp/RtmpfBzpiT/Rinst2e2c49c9879f/cleanr/source/R/checks.R: 256 lines in file.>
Functions should
You can check all functions defined in a file:
print(suppressWarnings(cleanr::check_functions_in_file(path)))
## [1] TRUE
And again we override some option by argument:
print(tools::assertCondition(suppressWarnings(cleanr::check_functions_in_file(path,
max_num_arguments = 1)),
c("cleanr", "error", "condition"))
)
## [[1]]
## <error in cleanr::check_functions_in_file(path, max_num_arguments = 1): /tmp/RtmpfBzpiT/Rinst2e2c49c9879f/cleanr/source/R/checks.R check_file_length found 2 arguments, max_num_arguments was 1
## /tmp/RtmpfBzpiT/Rinst2e2c49c9879f/cleanr/source/R/checks.R check_file_width found 2 arguments, max_num_arguments was 1
## /tmp/RtmpfBzpiT/Rinst2e2c49c9879f/cleanr/source/R/checks.R check_line_width found 2 arguments, max_num_arguments was 1
## /tmp/RtmpfBzpiT/Rinst2e2c49c9879f/cleanr/source/R/checks.R check_nesting_depth found 2 arguments, max_num_arguments was 1
## /tmp/RtmpfBzpiT/Rinst2e2c49c9879f/cleanr/source/R/checks.R check_num_arguments found 2 arguments, max_num_arguments was 1
## /tmp/RtmpfBzpiT/Rinst2e2c49c9879f/cleanr/source/R/checks.R check_num_lines found 2 arguments, max_num_arguments was 1
## /tmp/RtmpfBzpiT/Rinst2e2c49c9879f/cleanr/source/R/checks.R check_num_lines_of_code found 2 arguments, max_num_arguments was 1
## /tmp/RtmpfBzpiT/Rinst2e2c49c9879f/cleanr/source/R/checks.R check_return found 2 arguments, max_num_arguments was 1>
##
## [[2]]
## <error in cleanr::check_functions_in_file(path, max_num_arguments = 1): /tmp/RtmpfBzpiT/Rinst2e2c49c9879f/cleanr/source/R/checks.R check_file_length found 2 arguments, max_num_arguments was 1
## /tmp/RtmpfBzpiT/Rinst2e2c49c9879f/cleanr/source/R/checks.R check_file_width found 2 arguments, max_num_arguments was 1
## /tmp/RtmpfBzpiT/Rinst2e2c49c9879f/cleanr/source/R/checks.R check_line_width found 2 arguments, max_num_arguments was 1
## /tmp/RtmpfBzpiT/Rinst2e2c49c9879f/cleanr/source/R/checks.R check_nesting_depth found 2 arguments, max_num_arguments was 1
## /tmp/RtmpfBzpiT/Rinst2e2c49c9879f/cleanr/source/R/checks.R check_num_arguments found 2 arguments, max_num_arguments was 1
## /tmp/RtmpfBzpiT/Rinst2e2c49c9879f/cleanr/source/R/checks.R check_num_lines found 2 arguments, max_num_arguments was 1
## /tmp/RtmpfBzpiT/Rinst2e2c49c9879f/cleanr/source/R/checks.R check_num_lines_of_code found 2 arguments, max_num_arguments was 1
## /tmp/RtmpfBzpiT/Rinst2e2c49c9879f/cleanr/source/R/checks.R check_return found 2 arguments, max_num_arguments was 1>
cleanr comes with wrappers to check for file and function layout in one go: check_file
runs the file and function layout checks on a file, check_directory
runs them on all files (matching a given pattern) in a directory and check_package
runs them on a package source (you may find it helpful when developing a package).
print(suppressWarnings(cleanr::check_file(path)))
## [1] TRUE
path <- system.file("runit_tests", package = "cleanr")
print(suppressWarnings(cleanr::check_directory(path,
check_return = FALSE)))
## [1] TRUE
To run cleanr on its own codes, we need to load its internal functions first:
path <- system.file("source", "R", package = "cleanr")
cleanr::load_internal_functions("cleanr")
print(tools::assertCondition(suppressWarnings(cleanr::check_directory(path)),
c("cleanr", "error", "condition"))
)
## [[1]]
## <error in cleanr::check_directory(path): /tmp/RtmpfBzpiT/Rinst2e2c49c9879f/cleanr/source/R /tmp/RtmpfBzpiT/Rinst2e2c49c9879f/cleanr/source/R/internals.R throw found no return() statement at all.
## /tmp/RtmpfBzpiT/Rinst2e2c49c9879f/cleanr/source/R /tmp/RtmpfBzpiT/Rinst2e2c49c9879f/cleanr/source/R/options.R: line 91 counts 84 characters.
## /tmp/RtmpfBzpiT/Rinst2e2c49c9879f/cleanr/source/R /tmp/RtmpfBzpiT/Rinst2e2c49c9879f/cleanr/source/R/throw.R throw found no return() statement at all.
## /tmp/RtmpfBzpiT/Rinst2e2c49c9879f/cleanr/source/R /tmp/RtmpfBzpiT/Rinst2e2c49c9879f/cleanr/source/R/wrappers.R: 314 lines in file.
## /tmp/RtmpfBzpiT/Rinst2e2c49c9879f/cleanr/source/R/wrappers.R check_function_layout found 8 arguments, max_num_arguments was 5>
##
## [[2]]
## <error in cleanr::check_directory(path): /tmp/RtmpfBzpiT/Rinst2e2c49c9879f/cleanr/source/R /tmp/RtmpfBzpiT/Rinst2e2c49c9879f/cleanr/source/R/internals.R throw found no return() statement at all.
## /tmp/RtmpfBzpiT/Rinst2e2c49c9879f/cleanr/source/R /tmp/RtmpfBzpiT/Rinst2e2c49c9879f/cleanr/source/R/options.R: line 91 counts 84 characters.
## /tmp/RtmpfBzpiT/Rinst2e2c49c9879f/cleanr/source/R /tmp/RtmpfBzpiT/Rinst2e2c49c9879f/cleanr/source/R/throw.R throw found no return() statement at all.
## /tmp/RtmpfBzpiT/Rinst2e2c49c9879f/cleanr/source/R /tmp/RtmpfBzpiT/Rinst2e2c49c9879f/cleanr/source/R/wrappers.R: 314 lines in file.
## /tmp/RtmpfBzpiT/Rinst2e2c49c9879f/cleanr/source/R/wrappers.R check_function_layout found 8 arguments, max_num_arguments was 5>
To disable any check, set the corresponding option or argument to NULL
or FALSE
.
cleanr::set_cleanr_options(reset = TRUE)
cleanr::load_internal_functions("cleanr")
path <- system.file("source", "R", "wrappers.R", package = "cleanr")
# will produce a condition:
r <- tools::assertCondition(cleanr::check_functions_in_file(path,
check_return = FALSE),
c("cleanr", "error", "condition"))
print(r)
## [[1]]
## <error in cleanr::check_functions_in_file(path, check_return = FALSE): /tmp/RtmpfBzpiT/Rinst2e2c49c9879f/cleanr/source/R/wrappers.R check_function_layout found 8 arguments, max_num_arguments was 5>
##
## [[2]]
## <error in cleanr::check_functions_in_file(path, check_return = FALSE): /tmp/RtmpfBzpiT/Rinst2e2c49c9879f/cleanr/source/R/wrappers.R check_function_layout found 8 arguments, max_num_arguments was 5>
# set argument to disable the check causing the condition:
r <- cleanr::check_functions_in_file(path, check_return = FALSE,
max_num_arguments = FALSE)
print(r)
## [1] TRUE
To completely disable all checks use:
co <- get_cleanr_options(flatten_list = FALSE)
co <- lapply(co, function(x) x == FALSE)
options("cleanr" = list(cleanr = co))
get_cleanr_options()
## cleanr.max_file_width cleanr.max_file_length cleanr.max_lines
## FALSE FALSE FALSE
## cleanr.max_lines_of_code cleanr.max_num_arguments cleanr.max_nesting_depth
## FALSE FALSE FALSE
## cleanr.max_line_width cleanr.check_return
## FALSE FALSE
Now run on any directory, check_directory
will always return TRUE
:
path <- system.file("source", "R", package = "cleanr")
cleanr::load_internal_functions("cleanr")
print(cleanr::check_directory(path))
## [1] TRUE
Kernighan, B. W., and D. M. Ritchie. 1988. The C Programming Language, Second Edition. Englewood Cliffs, New Jersey: Prentice-Hall.
Martin, R.C. 2008. Clean Code: A Handbook of Agile Software Craftsmanship. Pearson Education.