The formatR package is 11 years old now. Interestingly, my very first English blog post was about this package. The original implementation was 8 lines of code in total, which still works today.
In the 11 years, the code size has grown to 784 lines, which is roughly 100 times bigger, and this package still has zero R package dependencies. It has some soft dependencies, but they do not affect the core functionality.
$ wc -l R/*.R
33 R/eval.R
10 R/shiny.R
253 R/tidy.R
187 R/usage.R
301 R/utils.R
784 total
To be honest, the package contains a lot of ugly and dirty hacks (if I were younger, I would also say “clever”), and I do not even have the courage of look at them sometimes, but they have been fun at times, too.
Support for pipe operators
I have not touched this package much in recent years until Alastair Rushworth
tweeted about it on Mar
15th this year, under which Philéas Condemine mentioned that it did not support
pipes. I had been aware of this
issue for years, but was not
motivated to work on it, partly because I thought it was a hard problem and
partly because I rarely use pipes myself. However, this time I thought for a few
more minutes, and came up with yet another dirty hack, which finally solved this
problem. Now I’m happy to say that both magrittr and R’s native pipes (e.g.,
%>%
and |>
) are supported in formatR.
You can play with the demo Shiny app below (click the demo button to use demo code):
Set the maximum line width
I have been long embarrassed by the width.cutoff
argument of
formatR::tidy_source()
, because it essentially sets the minimum width,
instead of the often desired maximum width. When I write a book, I definitely
want to control the maximum line width of code, so the code will not exceed the
page margin. In formatR 1.9, I used a binary search to try to set the
maximum width, which was initially contributed by Pavel N.
Krivitsky in 2017. Now if you want
the width to be the maximum width, you put the number in I()
and pass it to
the width.cutoff
argument, e.g.,
formatR::tidy_source(..., width.cutoff = I(60))
Please note that the maximum width cannot always be respected, i.e., the binary search will not always succeed. It depends on whether the code can be broken into relatively small pieces, so the lines can be wrapped more freely. When there is a super long element that cannot be broken in the code, such as a long variable name or a character string, setting the maximum width is likely to fail.
Start function arguments on a new line
Another feature that I have also desired for by myself is to reorganize the arguments of a function call, when the function name is lengthy, e.g.,
shiny::updateSelectizeInput(session, "foo", label = "New Label", selected = c("A",
"B"), choices = LETTERS, server = TRUE)
I hope it could be reformatted to:
shiny::updateSelectizeInput(
session, "foo", label = "New Label", selected = c("A", "B"),
choices = LETTERS, server = TRUE
)
This has become possible with the argument args.newline = TRUE
of
formatR::tidy_source()
in v1.11.
Above is a style that I personally prefer because it could fit more arguments on one line, after the lengthy function name is separated from its arguments. I know that perhaps more people love the style below more:
shiny::updateSelectizeInput(session, "foo", label = "New Label",
selected = c("A", "B"),
choices = LETTERS, server = TRUE)
Or strictly one argument per line:
shiny::updateSelectizeInput(session, "foo",
label = "New Label",
selected = c("A", "B"),
choices = LETTERS,
server = TRUE)
This should not be too hard to implement, but I do not have time now, so I will not consider it until perhaps a couple of years later, unless another hacker wants to tackle this problem and send a pull request on GitHub. Alternatively, you can also use the styler package, which has already supported this style of argument alignment.
Wrap comments with a more accurate width
In formatR v1.10, Siqi Zhang contributed a pull
request that fixed a pet peeve of
mine. That is, when re-wrapping blocks of comments with
formatR::tidy_source(wrap = TRUE)
, previous versions of formatR were not
accurate in terms of the width of comment lines, because they did not consider
the width of the possible indentation or comment characters. For example,
if (TRUE) {
# this is a long long long long long long long long long long comment
}
#---------#---------#---------#---------#---------|---------#------
#---------#---------#---------#---------#---------|---------#---------#--
When we re-wrap the comment inside the if
statement with a desired width 50,
previous versions of formatR would wrap it as:
if (TRUE) {
# this is a long long long long long long long long
# long long comment
}
That is because the width of the four spaces (indentation) and #
are not
considered. After considering these six characters, the actual width we should
use will be 50 - 6 = 44, which is what formatR does since v1.10:
if (TRUE) {
# this is a long long long long long long long
# long long long comment
}
The right arrow ->
Previously, the right arrow ->
, a lesser known assignment operator in R, would
be reformatted as the left arrow <-
(e.g., 1 -> x
would be reformatted as
x <- 1
). Now the right arrow is preserved. Perhaps I should not have mentioned
this, since the R community has had enough headache about all kinds of
assignment operators.
Final words
This package has been more like a personal hobby project in the past 11 years. I do not have high expectations about it, so I will continue to keep it simple in the future. To me, its main usage is to reformat code in my books (to control the line width). As mentioned before, if you have sophisticated requirements for styling code that are not covered by formatR, you may consider using other packages like styler.