Read R code from a file or the clipboard and reformat it. This function is
based on parse() and deparse(), but it does
several other things, such as preserving blank lines and comments,
substituting the assignment operator = with <-, and
re-indenting code with a specified number of spaces.
tidy_source(
source = "clipboard",
comment = getOption("formatR.comment", TRUE),
blank = getOption("formatR.blank", TRUE),
arrow = getOption("formatR.arrow", FALSE),
pipe = getOption("formatR.pipe", FALSE),
brace.newline = getOption("formatR.brace.newline", FALSE),
indent = getOption("formatR.indent", 4),
wrap = getOption("formatR.wrap", TRUE),
width.cutoff = getOption("formatR.width", getOption("width")),
args.newline = getOption("formatR.args.newline", FALSE),
output = TRUE,
text = NULL,
...
)A character string: file path to the source code (defaults to the clipboard).
Whether to keep comments.
Whether to keep blank lines.
Whether to substitute the assignment operator = with
<-.
Whether to substitute the magrittr pipe %>% with
R's native pipe operator |>.
Whether to put the left brace { to a new line.
Number of spaces to indent the code.
Whether to wrap comments to the linewidth determined by
width.cutoff (roxygen comments will never be wrapped).
An integer in [20, 500]: if a line's character
length is at or over this number, the function will try to break it into a
new line. In other words, this is the lower bound of the line width.
See ‘Details’ if an upper bound is desired instead.
Whether to start the arguments of a function call on a
new line instead of after the function name and ( when the arguments
cannot fit one line.
Whether to output to the console or a file using
cat().
An alternative way to specify the input: if NULL, the
function will use the source argument; if a character vector
containing the source code, the function will use this and ignore the
source argument.
Other arguments passed to cat(), e.g. file
(this can be useful for batch-processing R scripts, e.g.
tidy_source(source = 'input.R', file = 'output.R')).
A list with components
the reformatted code as a character vector
the code containing comments, which are masked in assignments or with the weird operator
.
A value of the argument width.cutoff wrapped in I()
(e.g., I(60)) will be treated as the upper bound of the line
width. The corresponding argument to deparse() is a lower bound, so
the function will perform a binary search for a width value that can make
deparse() return code with line width smaller than or equal to the
width.cutoff value. If the search fails, a warning will signal,
suppressible by global option options(formatR.width.warning = FALSE).
Be sure to read the reference to know other limitations.
https://yihui.org/formatR/ (an introduction to this package, with examples and further notes)
library(formatR)
## a messy R script
messy = system.file("format", "messy.R", package = "formatR")
tidy_source(messy)
#> # a single line of comments is preserved
#> 1 + 1
#>
#> if (TRUE) {
#> x = 1 # inline comments
#> } else {
#> x = 2
#> print("Oh no... ask the right bracket to go away!")
#> }
#> 1 * 3 # one space before this comment will become two!
#> 2 + 2 + 2 # 'short comments'
#>
#> # only 'single quotes' are allowed in comments
#> df = data.frame(y = rnorm(100), x1 = rnorm(100), x2 = rnorm(100))
#> lm(y ~ x1 + x2, data = df)
#> 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 +
#> 1 + 1 ## comments after a long line
#>
#> ## here is a long long long long long long long long long long long long long
#> ## long long long long long long long comment
## use the 'text' argument
src = readLines(messy)
## source code
cat(src, sep = "\n")
#> # a single line of comments is preserved
#> 1+1
#>
#> if(TRUE){
#> x=1 # inline comments
#> }else{
#> x=2;print('Oh no... ask the right bracket to go away!')}
#> 1*3 # one space before this comment will become two!
#> 2+2+2 # 'short comments'
#>
#> # only 'single quotes' are allowed in comments
#> df=data.frame(y=rnorm(100),x1=rnorm(100),x2=rnorm(100))
#> lm(y~x1+x2, data=df)
#> 1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1 ## comments after a long line
#>
#> ## here is a long long long long long long long long long long long long long long long long long long long long comment
## the formatted version
tidy_source(text = src)
#> # a single line of comments is preserved
#> 1 + 1
#>
#> if (TRUE) {
#> x = 1 # inline comments
#> } else {
#> x = 2
#> print("Oh no... ask the right bracket to go away!")
#> }
#> 1 * 3 # one space before this comment will become two!
#> 2 + 2 + 2 # 'short comments'
#>
#> # only 'single quotes' are allowed in comments
#> df = data.frame(y = rnorm(100), x1 = rnorm(100), x2 = rnorm(100))
#> lm(y ~ x1 + x2, data = df)
#> 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 +
#> 1 + 1 ## comments after a long line
#>
#> ## here is a long long long long long long long long long long long long long
#> ## long long long long long long long comment
## preserve blank lines
tidy_source(text = src, blank = TRUE)
#> # a single line of comments is preserved
#> 1 + 1
#>
#> if (TRUE) {
#> x = 1 # inline comments
#> } else {
#> x = 2
#> print("Oh no... ask the right bracket to go away!")
#> }
#> 1 * 3 # one space before this comment will become two!
#> 2 + 2 + 2 # 'short comments'
#>
#> # only 'single quotes' are allowed in comments
#> df = data.frame(y = rnorm(100), x1 = rnorm(100), x2 = rnorm(100))
#> lm(y ~ x1 + x2, data = df)
#> 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 +
#> 1 + 1 ## comments after a long line
#>
#> ## here is a long long long long long long long long long long long long long
#> ## long long long long long long long comment
## indent with 2 spaces
tidy_source(text = src, indent = 2)
#> # a single line of comments is preserved
#> 1 + 1
#>
#> if (TRUE) {
#> x = 1 # inline comments
#> } else {
#> x = 2
#> print("Oh no... ask the right bracket to go away!")
#> }
#> 1 * 3 # one space before this comment will become two!
#> 2 + 2 + 2 # 'short comments'
#>
#> # only 'single quotes' are allowed in comments
#> df = data.frame(y = rnorm(100), x1 = rnorm(100), x2 = rnorm(100))
#> lm(y ~ x1 + x2, data = df)
#> 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 +
#> 1 + 1 ## comments after a long line
#>
#> ## here is a long long long long long long long long long long long long long
#> ## long long long long long long long comment
## discard comments!
tidy_source(text = src, comment = FALSE)
#> 1 + 1
#>
#> if (TRUE) {
#> x = 1
#> } else {
#> x = 2
#> print("Oh no... ask the right bracket to go away!")
#> }
#> 1 * 3
#> 2 + 2 + 2
#>
#> df = data.frame(y = rnorm(100), x1 = rnorm(100), x2 = rnorm(100))
#> lm(y ~ x1 + x2, data = df)
#> 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 +
#> 1 + 1
#>
## wanna see the gory truth??
tidy_source(text = src, output = FALSE)$text.mask
#> [1] "# a single line of comments is preserved"
#> [2] "1 + 1"
#> [3] ""
#> [4] "if (TRUE) {\n x = 1 %\b% \"# inline comments\"\n} else {\n x = 2\n print(\"Oh no... ask the right bracket to go away!\")\n}"
#> [5] "1 * 3 %\b% \"# one space before this comment will become two!\""
#> [6] "2 + 2 + 2 %\b% \"# 'short comments'\""
#> [7] ""
#> [8] "# only 'single quotes' are allowed in comments"
#> [9] "df = data.frame(y = rnorm(100), x1 = rnorm(100), x2 = rnorm(100))"
#> [10] "lm(y ~ x1 + x2, data = df)"
#> [11] "1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 +\n 1 + 1 %\b% \"## comments after a long line\""
#> [12] ""
#> [13] "## here is a long long long long long long long long long long long long long\n## long long long long long long long comment"
## tidy up the source code of image demo
x = file.path(system.file(package = "graphics"), "demo", "image.R")
# to console
tidy_source(x)
#> # Copyright (C) 1997-2009 The R Core Team
#>
#> require(datasets)
#> require(grDevices)
#> require(graphics)
#>
#> x <- 10 * (1:nrow(volcano))
#> x.at <- seq(100, 800, by = 100)
#> y <- 10 * (1:ncol(volcano))
#> y.at <- seq(100, 600, by = 100)
#>
#> # Using Terrain Colors
#>
#> image(x, y, volcano, col = terrain.colors(100), axes = FALSE)
#> contour(x, y, volcano, levels = seq(90, 200, by = 5), add = TRUE, col = "brown")
#> axis(1, at = x.at)
#> axis(2, at = y.at)
#> box()
#> title(main = "Maunga Whau Volcano", sub = "col=terrain.colors(100)", font.main = 4)
#>
#> # Using Heat Colors
#>
#> image(x, y, volcano, col = heat.colors(100), axes = FALSE)
#> contour(x, y, volcano, levels = seq(90, 200, by = 5), add = TRUE, col = "brown")
#> axis(1, at = x.at)
#> axis(2, at = y.at)
#> box()
#> title(main = "Maunga Whau Volcano", sub = "col=heat.colors(100)", font.main = 4)
#>
#> # Using Gray Scale
#>
#> image(x, y, volcano, col = gray(100:200/200), axes = FALSE)
#> contour(x, y, volcano, levels = seq(90, 200, by = 5), add = TRUE, col = "black")
#> axis(1, at = x.at)
#> axis(2, at = y.at)
#> box()
#> title(main = "Maunga Whau Volcano \n col=gray(100:200/200)", font.main = 4)
#>
#> ## Filled Contours are even nicer sometimes :
#> example(filled.contour)
# to a file
f = tempfile()
tidy_source(x, blank = TRUE, file = f)
## check the original code here and see the difference
file.show(x)
file.show(f)
## use global options
options(comment = TRUE, blank = FALSE)
tidy_source(x)
#> # Copyright (C) 1997-2009 The R Core Team
#>
#> require(datasets)
#> require(grDevices)
#> require(graphics)
#>
#> x <- 10 * (1:nrow(volcano))
#> x.at <- seq(100, 800, by = 100)
#> y <- 10 * (1:ncol(volcano))
#> y.at <- seq(100, 600, by = 100)
#>
#> # Using Terrain Colors
#>
#> image(x, y, volcano, col = terrain.colors(100), axes = FALSE)
#> contour(x, y, volcano, levels = seq(90, 200, by = 5), add = TRUE, col = "brown")
#> axis(1, at = x.at)
#> axis(2, at = y.at)
#> box()
#> title(main = "Maunga Whau Volcano", sub = "col=terrain.colors(100)", font.main = 4)
#>
#> # Using Heat Colors
#>
#> image(x, y, volcano, col = heat.colors(100), axes = FALSE)
#> contour(x, y, volcano, levels = seq(90, 200, by = 5), add = TRUE, col = "brown")
#> axis(1, at = x.at)
#> axis(2, at = y.at)
#> box()
#> title(main = "Maunga Whau Volcano", sub = "col=heat.colors(100)", font.main = 4)
#>
#> # Using Gray Scale
#>
#> image(x, y, volcano, col = gray(100:200/200), axes = FALSE)
#> contour(x, y, volcano, levels = seq(90, 200, by = 5), add = TRUE, col = "black")
#> axis(1, at = x.at)
#> axis(2, at = y.at)
#> box()
#> title(main = "Maunga Whau Volcano \n col=gray(100:200/200)", font.main = 4)
#>
#> ## Filled Contours are even nicer sometimes :
#> example(filled.contour)
## if you've copied R code into the clipboard
if (interactive()) {
tidy_source("clipboard")
## write into clipboard again
tidy_source("clipboard", file = "clipboard")
}
## the if-else structure
tidy_source(text = c("{if(TRUE)1 else 2; if(FALSE){1+1", "## comments", "} else 2}"))
#> {
#> if (TRUE)
#> 1 else 2
#> if (FALSE) {
#> 1 + 1
#> ## comments
#> } else 2
#> }