formatR/ 0000755 0001762 0000144 00000000000 13077756142 011700 5 ustar ligges users formatR/inst/ 0000755 0001762 0000144 00000000000 13077671755 012663 5 ustar ligges users formatR/inst/format/ 0000755 0001762 0000144 00000000000 12304667727 014147 5 ustar ligges users formatR/inst/format/messy.R 0000644 0001762 0000144 00000001050 12330116606 015406 0 ustar ligges users # 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
formatR/inst/shiny/ 0000755 0001762 0000144 00000000000 12451070672 013777 5 ustar ligges users formatR/inst/shiny/ui.R 0000644 0001762 0000144 00000003264 12376735132 014551 0 ustar ligges users library(shiny)
shinyUI(fluidPage(
title = 'Tidy R Code with formatR (Yihui Xie)',
helpText(), # just a placeholder for a little bit top margin
sidebarLayout(
sidebarPanel(
tags$head(
tags$script(src = 'shiny-handler.js'),
tags$style(type = 'text/css', '.popover {max-width: 100%;}')
),
helpText('This Shiny app uses the function', code('tidy_source()'),
'in the', a(href = 'http://yihui.name/formatR', strong('formatR')),
sprintf('(>= v%s)', packageVersion('formatR')),
'package to reformat R code in the text box on the right.',
a(list(icon('hand-o-right'), 'demo'), class = 'btn btn-small btn-info',
onclick = '$("textarea#src").val($("#demo").val()).trigger("change");')),
checkboxInput('arg_comment', 'Preserve comments', TRUE),
checkboxInput('arg_blank', 'Preserve blank lines', TRUE),
checkboxInput('arg_assign', 'Replace = with <-', FALSE),
checkboxInput('arg_brace', 'Put { on a new line', FALSE),
numericInput ('arg_indent', 'Number of spaces for indentation', 4, min = 0),
numericInput ('arg_width', 'Minimum line width', 70, min = 20, max = 500),
submitButton ('Tidy My Code', icon('toggle-right'))
),
mainPanel(
tags$textarea(
id = 'src', rows = 20,
style = 'width: 99%; font-family: monospace; word-wrap: normal; white-space: pre;',
placeholder = 'paste your R code here...'
),
tags$textarea(
id = 'demo', style = 'display: none;',
paste(
readLines(system.file('format', 'messy.R', package = 'formatR')),
collapse = '\n'
)
)
)
)
))
formatR/inst/shiny/server.R 0000644 0001762 0000144 00000001002 12376444004 015421 0 ustar ligges users library(shiny)
library(formatR)
shinyServer(function(input, output, session) {
observe({
res = try(tidy_source(
text = input$src, output = FALSE, comment = input$arg_comment,
blank = input$arg_blank, arrow = input$arg_assign,
brace.newline = input$arg_brace, indent = input$arg_indent,
width.cutoff = input$arg_width
))
session$sendCustomMessage(
'replace_textarea',
if (inherits(res, 'try-error')) I(res) else paste(res$text.tidy, collapse = '\n')
)
})
})
formatR/inst/shiny/www/ 0000755 0001762 0000144 00000000000 12330245417 014620 5 ustar ligges users formatR/inst/shiny/www/shiny-handler.js 0000644 0001762 0000144 00000001400 12330114006 017703 0 ustar ligges users Shiny.addCustomMessageHandler("replace_textarea",
function(message) {
if (typeof message === 'object') {
// an error must have occurred
var msg = '
'
+ message[0] + '
';
$('textarea#src').popover('destroy')
.popover({
placement: 'bottom',
html: true,
title: 'Failed to format the code',
content: msg
})
.popover('show')
.addClass('alert-error');
} else {
$('textarea#src').popover('destroy').removeClass('alert-error').val(message);
};
}
);
formatR/inst/shiny/DESCRIPTION 0000644 0001762 0000144 00000000215 12330114727 015477 0 ustar ligges users Title: Tidy R code using formatR
Author: Yihui Xie
AuthorUrl: http://yihui.name
License: MIT
DisplayMode: Showcase
Tags: formatR
Type: Shiny
formatR/inst/shiny/Readme.md 0000644 0001762 0000144 00000000363 12376444004 015520 0 ustar ligges users This app uses a `textarea` input to store R code, which is reformatted by
`formatR::tidy_source()`. The result is written back in the text box. Click the
`demo` button to load a demo, or paste your own R code here to see what this app
can do.
formatR/inst/doc/ 0000755 0001762 0000144 00000000000 13077671755 013430 5 ustar ligges users formatR/inst/doc/formatR.R 0000644 0001762 0000144 00000015214 13077671751 015164 0 ustar ligges users ## ----setup, include=FALSE-------------------------------------------
options(formatR.indent = 4, width = 70)
knitr::opts_chunk$set(tidy = TRUE)
## ----eval=FALSE-----------------------------------------------------
# install.packages('formatR', repos = 'http://cran.rstudio.com')
# #' to install the development version, run
# #' install.packages('formatR', repos = 'https://xran.yihui.name')
## -------------------------------------------------------------------
library(formatR)
sessionInfo()
## ----example, eval=FALSE, tidy=FALSE--------------------------------
# ## comments are retained;
# # a comment block will be reflowed if it contains long comments;
# #' roxygen comments will not be wrapped in any case
# 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 # only 'single quotes' are allowed in comments
#
# lm(y~x1+x2, data=data.frame(y=rnorm(100),x1=rnorm(100),x2=rnorm(100))) ### a linear model
# 1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1 # comment after a long line
# ## here is a long long long long long long long long long long long long long comment which will be wrapped
## ----example, eval=FALSE, tidy.opts=list(width.cutoff=50)-----------
# ## comments are retained;
# # a comment block will be reflowed if it contains long comments;
# #' roxygen comments will not be wrapped in any case
# 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 # only 'single quotes' are allowed in comments
#
# lm(y~x1+x2, data=data.frame(y=rnorm(100),x1=rnorm(100),x2=rnorm(100))) ### a linear model
# 1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1 # comment after a long line
# ## here is a long long long long long long long long long long long long long comment which will be wrapped
## ----collapse=TRUE--------------------------------------------------
library(formatR)
usage(glm, width = 40) # can set arbitrary width here
args(glm)
## ----echo=FALSE, results='asis'-------------------------------------
if (ignore_img <- Sys.getenv('USER', '') != 'yihui') cat('')
## ----comment=NA-----------------------------------------------------
set.seed(123)
tidy_eval(text = c("a<-1+1;a # print the value", "matrix(rnorm(10),5)"))
## ----eval=FALSE-----------------------------------------------------
# library(formatR)
# tidy_eval()
# # without specifying any arguments, it reads code from clipboard
## ----example, eval=FALSE, echo=6, tidy.opts=list(arrow=TRUE)--------
# ## comments are retained;
# # a comment block will be reflowed if it contains long comments;
# #' roxygen comments will not be wrapped in any case
# 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 # only 'single quotes' are allowed in comments
#
# lm(y~x1+x2, data=data.frame(y=rnorm(100),x1=rnorm(100),x2=rnorm(100))) ### a linear model
# 1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1 # comment after a long line
# ## here is a long long long long long long long long long long long long long comment which will be wrapped
## ----example, eval=FALSE, echo=1:6, tidy.opts=list(blank = FALSE)----
# ## comments are retained;
# # a comment block will be reflowed if it contains long comments;
# #' roxygen comments will not be wrapped in any case
# 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 # only 'single quotes' are allowed in comments
#
# lm(y~x1+x2, data=data.frame(y=rnorm(100),x1=rnorm(100),x2=rnorm(100))) ### a linear model
# 1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1 # comment after a long line
# ## here is a long long long long long long long long long long long long long comment which will be wrapped
## ----example, eval=FALSE, echo=6, tidy.opts=list(indent = 2)--------
# ## comments are retained;
# # a comment block will be reflowed if it contains long comments;
# #' roxygen comments will not be wrapped in any case
# 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 # only 'single quotes' are allowed in comments
#
# lm(y~x1+x2, data=data.frame(y=rnorm(100),x1=rnorm(100),x2=rnorm(100))) ### a linear model
# 1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1 # comment after a long line
# ## here is a long long long long long long long long long long long long long comment which will be wrapped
## ----example, eval=FALSE, echo=6, tidy.opts=list(brace.newline = TRUE)----
# ## comments are retained;
# # a comment block will be reflowed if it contains long comments;
# #' roxygen comments will not be wrapped in any case
# 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 # only 'single quotes' are allowed in comments
#
# lm(y~x1+x2, data=data.frame(y=rnorm(100),x1=rnorm(100),x2=rnorm(100))) ### a linear model
# 1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1 # comment after a long line
# ## here is a long long long long long long long long long long long long long comment which will be wrapped
## ----example, eval=FALSE, tidy.opts=list(comment = FALSE, width.cutoff = 50)----
# ## comments are retained;
# # a comment block will be reflowed if it contains long comments;
# #' roxygen comments will not be wrapped in any case
# 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 # only 'single quotes' are allowed in comments
#
# lm(y~x1+x2, data=data.frame(y=rnorm(100),x1=rnorm(100),x2=rnorm(100))) ### a linear model
# 1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1 # comment after a long line
# ## here is a long long long long long long long long long long long long long comment which will be wrapped
## ----comment-brace, tidy=FALSE, eval=FALSE--------------------------
# if (TRUE) {## comments
# }
## ----comment-brace, eval=FALSE--------------------------------------
# if (TRUE) {## comments
# }
## -------------------------------------------------------------------
deparse(parse(text = '1+2-3*4/5 # a comment'))
formatR/inst/doc/formatR.html 0000644 0001762 0000144 00000365620 13077671755 015744 0 ustar ligges users
formatR
You can install formatR from CRAN, or XRAN if you want to test the latest development version:
install.packages("formatR", repos ="http://cran.rstudio.com")
#' to install the development version, run#' install.packages('formatR', repos = 'https://xran.yihui.name')
Or check out the Github repository and install from source if you know what this means. This page is always based on the development version.
library(formatR)
sessionInfo()
## R version 3.4.0 (2017-04-21)
## Platform: x86_64-apple-darwin15.6.0 (64-bit)
## Running under: macOS Sierra 10.12.4
##
## Matrix products: default
## BLAS: /Library/Frameworks/R.framework/Versions/3.4/Resources/lib/libRblas.0.dylib
## LAPACK: /Library/Frameworks/R.framework/Versions/3.4/Resources/lib/libRlapack.dylib
##
## locale:
## [1] C/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
##
## attached base packages:
## [1] stats graphics grDevices utils datasets methods
## [7] base
##
## other attached packages:
## [1] formatR_1.5
##
## loaded via a namespace (and not attached):
## [1] compiler_3.4.0 backports_1.0.5 magrittr_1.5
## [4] rprojroot_1.2 htmltools_0.3.5 tools_3.4.0
## [7] yaml_2.1.14 Rcpp_0.12.10 rmarkdown_1.4.0.9001
## [10] stringi_1.1.5 knitr_1.15.20 digest_0.6.12
## [13] stringr_1.2.0 evaluate_0.10
2. Reformat R code
The formatR package was designed to reformat R code to improve readability; the main workhorse is the function tidy_source(). Features include:
long lines of code and comments are reorganized into appropriately shorter ones
spaces and indent are added where necessary
comments are preserved in most cases
the number of spaces to indent the code (i.e. tab width) can be specified (default is 4)
an else statement in a separate line without the leading } will be moved one line back
= as an assignment operator can be replaced with <-
the left brace { can be moved to a new line
Below is an example of what tidy_source() can do. The source code is:
## comments are retained;
# a comment block will be reflowed if it contains long comments;#' roxygen comments will not be wrapped in any case1+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# only 'single quotes' are allowed in commentslm(y~x1+x2, data=data.frame(y=rnorm(100),x1=rnorm(100),x2=rnorm(100))) ### a linear model
1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1# comment after a long line
## here is a long long long long long long long long long long long long long comment which will be wrapped
We can copy the above code to clipboard, and type tidy_source(width.cutoff = 50) to get:
## comments are retained; a comment block will be
## reflowed if it contains long comments;
#' roxygen comments will not be wrapped in any case1 +1
if (TRUE) {
x =1# inline comments
} else {
x =2print("Oh no... ask the right bracket to go away!")
}
1 *3# one space before this comment will become two!2 +2 +2# only 'single quotes' are allowed in commentslm(y ~x1 +x2, data =data.frame(y =rnorm(100), x1 =rnorm(100),
x2 =rnorm(100))) ### a linear model
1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1# comment after a long line
## here is a long long long long long long long long
## long long long long long comment which will be
## wrapped
Two applications of tidy_source():
tidy_dir() can reformat all R scripts under a directory
usage() can reformat the usage of a function, e.g. compare usage() with the default output of args():
library(formatR)
usage(glm, width =40) # can set arbitrary width here
## glm(formula, family = gaussian, data,
## weights, subset, na.action,
## start = NULL, etastart, mustart,
## offset, control = list(...),
## model = TRUE, method = "glm.fit",
## x = FALSE, y = TRUE,
## contrasts = NULL, ...)
args(glm)
## function (formula, family = gaussian, data, weights, subset,
## na.action, start = NULL, etastart, mustart, offset, control = list(...),
## model = TRUE, method = "glm.fit", x = FALSE, y = TRUE, contrasts = NULL,
## ...)
## NULL
3. The Graphical User Interface
If the shiny packages has been installed, the function tidy_app() can launch a Shiny app to reformat R code like this (live demo):
formatR::tidy_app()
After hitting the Tidy button:
4. Evaluate the code and mask output in comments
It is often a pain when trying to copy R code from other people’s code which has been run in R and the prompt characters (usually >) are attached in the beginning of code, because we have to remove all the prompts > and + manually before we are able to run the code. However, it will be convenient for the reader to understand the code if the output of the code can be attached. This motivates the function tidy_eval(), which uses tidy_source() to reformat the source code, evaluates the code in chunks, and attaches the output of each chunk as comments which will not actually break the original source code. Here is an example:
set.seed(123)
tidy_eval(text =c("a<-1+1;a # print the value", "matrix(rnorm(10),5)"))
a <- 1 + 1
a # print the value
## [1] 2
matrix(rnorm(10), 5)
## [,1] [,2]
## [1,] -0.56047565 1.7150650
## [2,] -0.23017749 0.4609162
## [3,] 1.55870831 -1.2650612
## [4,] 0.07050839 -0.6868529
## [5,] 0.12928774 -0.4456620
The default source of the code is from clipboard like tidy_source(), so we can copy our code to clipboard, and simply run this in R:
library(formatR)
tidy_eval()
# without specifying any arguments, it reads code from clipboard
5. Showcase
We continue the example code in Section 2, using different arguments in tidy_source() such as arrow, blank, indent, brace.newline and comment, etc.
Replace = with <-
if (TRUE) {
x <-1# inline comments
} else {
x <-2print("Oh no... ask the right bracket to go away!")
}
Discard blank lines
Note the 5th line (an empty line) was discarded:
## comments are retained; a comment block will be reflowed if it
## contains long comments;
#' roxygen comments will not be wrapped in any case1 +1
if (TRUE) {
x =1# inline comments
} else {
x =2print("Oh no... ask the right bracket to go away!")
}
1 *3# one space before this comment will become two!
Reindent code (2 spaces instead of 4)
if (TRUE) {
x =1# inline comments
} else {
x =2print("Oh no... ask the right bracket to go away!")
}
Move left braces { to new lines
if (TRUE)
{
x =1# inline comments
} else
{
x =2print("Oh no... ask the right bracket to go away!")
}
Discard comments
1 +1
if (TRUE) {
x =1
} else {
x =2print("Oh no... ask the right bracket to go away!")
}
1 *32 +2 +2lm(y ~x1 +x2, data =data.frame(y =rnorm(100), x1 =rnorm(100),
x2 =rnorm(100)))
1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1
6. Further notes
The tricks used in this packages are very dirty. There might be dangers in using the functions in formatR. Please read the next section carefully to know exactly how comments are preserved. The best strategy to avoid failure is to put comments in complete lines or after complete R expressions. Below are some known issues that tidy_source() may fail.
In-line comments after an incomplete expression or ;
1 +2 +## comments after an incomplete line
3 +4
x <-## this is not a complete expression
5
x <-1; # you should not use ; here!
It is not a good idea to interrupt R code with comments and sometimes it can be confusing – comments should come after a complete R expression naturally; by the way, tidy_source() will move the comments after { to the next line, e.g.
if (TRUE) {## comments
}
will become
if (TRUE) {
## comments
}
Inappropriate blank lines
Blank lines are often used to separate complete chunks of R code, and arbitrary blank lines may cause failures in tidy_source() as well when the argument blank = TRUE, e.g.
if (TRUE)
{'this is a BAD style of R programming!'} else 'failure!'
There should not be a blank line after the if statement. Of course blank = FALSE will not fail in this case.
? with comments
We can use the question mark (?) to view the help page, but formatR package is unable to correctly format the code using ? with comments, e.g.
?sd # help on sd()
In this case, it is recommended to use the function help() instead of the short-hand version ?.
-> with comments
We can also use the right arrow -> for assignment, e.g. 1:10 -> x. I believe this flexibility is worthless, and it is amazing that a language has three assignment operators: <-, = and -> (whereas almost all other languages uses = for assignment). Bad news for formatR is that it is unable to format code using both -> and comments in a line, e.g.
1:10 ->x # assignment with right arrow
I recommend you to use <- or = consistently. What is more important is consistency. I always use = because it causes me no confusion (I do not believe it is ever possible for people to interpret fun(a = 1) as assigning 1 to a variable a instead of passing an argument value) and <- is more dangerous because it works everywhere (you might have unconsciously created a new variable a in fun(a <- 1); see an example here). The only disadvantage is that most R people use <- so it may be difficult to collaborate with other people.
7. How does tidy_source() actually work?
In a nutshell, tidy_source(text = code) is basically deparse(parse(text = code)), but actually it is more complicated only because of one thing: deparse() drops comments, e.g.,
deparse(parse(text ="1+2-3*4/5 # a comment"))
## [1] "expression(1 + 2 - 3 * 4/5)"
The method to preserve comments is to protect them as strings in R expressions. For example, there is a single line of comments in the source code:
# asdf
It will be first masked as
invisible(".IDENTIFIER1 # asdf.IDENTIFIER2")
which is a legal R expression, so base::parse() can deal with it and will no longer remove the disguised comments. In the end the identifiers will be removed to restore the original comments, i.e. the strings invisible(".IDENTIFIER1 and .IDENTIFIER2") are replaced with empty strings.
Inline comments are handled differently: two spaces will be added before the hash symbol #, e.g.
1+1# comments
will become
1+1# comments
Inline comments are first disguised as a weird operation with its preceding R code, which is essentially meaningless but syntactically correct! For example,
1+1 %InLiNe_IdEnTiFiEr% "# comments"
then base::parse() will deal with this expression; again, the disguised comments will not be removed. In the end, inline comments will be freed as well (remove the operator %InLiNe_IdEnTiFiEr% and surrounding double quotes).
All these special treatments to comments are due to the fact that base::parse() and base::deparse() can tidy the R code at the price of dropping all the comments.
8. Global options
There are global options which can override some arguments in tidy_source():
argument
global option
default
comment
options('formatR.comment')
TRUE
blank
options('formatR.blank')
TRUE
arrow
options('formatR.arrow')
FALSE
indent
options('formatR.indent')
4
brace.newline
options('formatR.brace.newline')
FALSE
Also note that single lines of long comments will be wrapped into shorter ones automatically, but roxygen comments will not be wrapped (i.e., comments that begin with #').
formatR/inst/doc/formatR.Rmd 0000644 0001762 0000144 00000024726 13077671653 015516 0 ustar ligges users ---
title: formatR
subtitle: Format R code automatically
author: Yihui Xie
date: "`r Sys.Date()`"
show_toc: true
slug: formatR
githubEditURL: https://github.com/yihui/formatR/edit/master/vignettes/formatR.Rmd
output:
knitr:::html_vignette:
toc: yes
vignette: >
%\VignetteEngine{knitr::rmarkdown}
%\VignetteIndexEntry{An Introduction to formatR}
---
```{r setup, include=FALSE}
options(formatR.indent = 4, width = 70)
knitr::opts_chunk$set(tidy = TRUE)
```
# 1. Installation
You can install **formatR** from [CRAN](https://cran.r-project.org/package=formatR), or [XRAN](https://xran.yihui.name) if you want to test the latest development version:
```{r eval=FALSE}
install.packages('formatR', repos = 'http://cran.rstudio.com')
#' to install the development version, run
#' install.packages('formatR', repos = 'https://xran.yihui.name')
```
Or check out the [Github repository](https://github.com/yihui/formatR) and install from source if you know what this means. This page is always based on the development version.
```{r}
library(formatR)
sessionInfo()
```
# 2. Reformat R code
The **formatR** package was designed to reformat R code to improve readability; the main workhorse is the function `tidy_source()`. Features include:
- long lines of code and comments are reorganized into appropriately shorter ones
- spaces and indent are added where necessary
- comments are preserved in most cases
- the number of spaces to indent the code (i.e. tab width) can be specified (default is 4)
- an `else` statement in a separate line without the leading `}` will be moved one line back
- `=` as an assignment operator can be replaced with `<-`
- the left brace `{` can be moved to a new line
Below is an example of what `tidy_source()` can do. The source code is:
```{r example, eval=FALSE, tidy=FALSE}
## comments are retained;
# a comment block will be reflowed if it contains long comments;
#' roxygen comments will not be wrapped in any case
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 # only 'single quotes' are allowed in comments
lm(y~x1+x2, data=data.frame(y=rnorm(100),x1=rnorm(100),x2=rnorm(100))) ### a linear model
1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1 # comment after a long line
## here is a long long long long long long long long long long long long long comment which will be wrapped
```
We can copy the above code to clipboard, and type `tidy_source(width.cutoff = 50)` to get:
```{r example, eval=FALSE, tidy.opts=list(width.cutoff=50)}
```
Two applications of `tidy_source()`:
- `tidy_dir()` can reformat all R scripts under a directory
- `usage()` can reformat the usage of a function, e.g. compare `usage()` with the default output of `args()`:
```{r collapse=TRUE}
library(formatR)
usage(glm, width = 40) # can set arbitrary width here
args(glm)
```
# 3. The Graphical User Interface
If the **shiny** packages has been installed, the function `tidy_app()` can launch a Shiny app to reformat R code like this ([live demo](https://yihui.shinyapps.io/formatR/)):
```r
formatR::tidy_app()
```
```{r echo=FALSE, results='asis'}
if (ignore_img <- Sys.getenv('USER', '') != 'yihui') cat('')
```
# 4. Evaluate the code and mask output in comments
It is often a pain when trying to copy R code from other people's code which has been run in R and the prompt characters (usually `> `) are attached in the beginning of code, because we have to remove all the prompts `> ` and `+ ` manually before we are able to run the code. However, it will be convenient for the reader to understand the code if the output of the code can be attached. This motivates the function `tidy_eval()`, which uses `tidy_source()` to reformat the source code, evaluates the code in chunks, and attaches the output of each chunk as comments which will not actually break the original source code. Here is an example:
```{r comment=NA}
set.seed(123)
tidy_eval(text = c("a<-1+1;a # print the value", "matrix(rnorm(10),5)"))
```
The default source of the code is from clipboard like `tidy_source()`, so we can copy our code to clipboard, and simply run this in R:
```{r eval=FALSE}
library(formatR)
tidy_eval()
# without specifying any arguments, it reads code from clipboard
```
# 5. Showcase
We continue the example code in Section 2, using different arguments in `tidy_source()` such as `arrow`, `blank`, `indent`, `brace.newline` and `comment`, etc.
## Replace `=` with `<-`
```{r example, eval=FALSE, echo=6, tidy.opts=list(arrow=TRUE)}
```
## Discard blank lines
Note the 5th line (an empty line) was discarded:
```{r example, eval=FALSE, echo=1:6, tidy.opts=list(blank = FALSE)}
```
## Reindent code (2 spaces instead of 4)
```{r example, eval=FALSE, echo=6, tidy.opts=list(indent = 2)}
```
## Move left braces `{` to new lines
```{r example, eval=FALSE, echo=6, tidy.opts=list(brace.newline = TRUE)}
```
## Discard comments
```{r example, eval=FALSE, tidy.opts=list(comment = FALSE, width.cutoff = 50)}
```
# 6. Further notes
The tricks used in this packages are very dirty. There might be dangers in using the functions in **formatR**. Please read the next section carefully to know exactly how comments are preserved. The best strategy to avoid failure is to put comments in complete lines or after _complete_ R expressions. Below are some known issues that `tidy_source()` may fail.
## In-line comments after an incomplete expression or ;
```r
1 + 2 + ## comments after an incomplete line
3 + 4
x <- ## this is not a complete expression
5
x <- 1; # you should not use ; here!
```
It is not a good idea to interrupt R code with comments and sometimes it can be confusing -- comments should come after a complete R expression naturally; by the way, `tidy_source()` will move the comments after `{` to the next line, e.g.
```{r comment-brace, tidy=FALSE, eval=FALSE}
if (TRUE) {## comments
}
```
will become
```{r comment-brace, eval=FALSE}
```
## Inappropriate blank lines
Blank lines are often used to separate complete chunks of R code, and arbitrary blank lines may cause failures in `tidy_source()` as well when the argument `blank = TRUE`, e.g.
```r
if (TRUE)
{'this is a BAD style of R programming!'} else 'failure!'
```
There should not be a blank line after the `if` statement. Of course `blank = FALSE` will not fail in this case.
## `?` with comments
We can use the question mark (`?`) to view the help page, but **formatR** package is unable to correctly format the code using `?` with comments, e.g.
```r
?sd # help on sd()
```
In this case, it is recommended to use the function `help()` instead of the short-hand version `?`.
## `->` with comments
We can also use the right arrow `->` for assignment, e.g. `1:10 -> x`. I believe this flexibility is worthless, and it is amazing that a language has three assignment operators: `<-`, `=` and `->` (whereas almost all other languages uses `=` for assignment). Bad news for **formatR** is that it is unable to format code using both `->` and comments in a line, e.g.
```r
1:10 -> x # assignment with right arrow
```
I recommend you to use `<-` or `=` consistently. What is more important is consistency. I always use `=` because it causes me no confusion (I do not believe it is ever possible for people to interpret `fun(a = 1)` as assigning `1` to a variable `a` instead of passing an argument value) and `<-` is more dangerous because it works everywhere (you might have unconsciously created a new variable `a` in `fun(a <- 1)`; see [an example here](https://stat.ethz.ch/pipermail/r-devel/2011-December/062786.html)). The only disadvantage is that most R people use `<-` so it may be difficult to collaborate with other people.
# 7. How does `tidy_source()` actually work?
In a nutshell, `tidy_source(text = code)` is basically `deparse(parse(text = code))`, but actually it is more complicated only because of one thing: `deparse()` drops comments, e.g.,
```{r}
deparse(parse(text = '1+2-3*4/5 # a comment'))
```
The method to preserve comments is to protect them as strings in R expressions. For example, there is a single line of comments in the source code:
```r
# asdf
```
It will be first masked as
```r
invisible(".IDENTIFIER1 # asdf.IDENTIFIER2")
```
which is a legal R expression, so `base::parse()` can deal with it and will no longer remove the disguised comments. In the end the identifiers will be removed to restore the original comments, i.e. the strings `invisible(".IDENTIFIER1` and `.IDENTIFIER2")` are replaced with empty strings.
Inline comments are handled differently: two spaces will be added before the hash symbol `#`, e.g.
```r
1+1# comments
```
will become
```r
1+1 # comments
```
Inline comments are first disguised as a weird operation with its preceding R code, which is essentially meaningless but syntactically correct! For example,
```r
1+1 %InLiNe_IdEnTiFiEr% "# comments"
```
then `base::parse()` will deal with this expression; again, the disguised comments will not be removed. In the end, inline comments will be freed as well (remove the operator `%InLiNe_IdEnTiFiEr%` and surrounding double quotes).
All these special treatments to comments are due to the fact that `base::parse()` and `base::deparse()` can tidy the R code at the price of dropping all the comments.
# 8. Global options
There are global options which can override some arguments in `tidy_source()`:
| argument | global option | default |
|-----------------|------------------------------------|---------|
| `comment` | `options('formatR.comment')` | `TRUE` |
| `blank` | `options('formatR.blank')` | `TRUE` |
| `arrow` | `options('formatR.arrow')` | `FALSE` |
| `indent` | `options('formatR.indent')` | `4` |
| `brace.newline` | `options('formatR.brace.newline')` | `FALSE` |
Also note that single lines of long comments will be wrapped into shorter ones automatically, but roxygen comments will not be wrapped (i.e., comments that begin with `#'`).
formatR/tests/ 0000755 0001762 0000144 00000000000 12304662033 013025 5 ustar ligges users formatR/tests/testit/ 0000755 0001762 0000144 00000000000 13077173260 014350 5 ustar ligges users formatR/tests/testit/test-utils.R 0000644 0001762 0000144 00000001545 12235532745 016617 0 ustar ligges users library(testit)
assert(
'move_leftbrace() works',
# no indent before abc, so no indent before {
identical(move_leftbrace('abc() {\n }'), 'abc()\n{\n }'),
# 3 spaces before abc, 3 before {
identical(move_leftbrace(' a() {\n}'), c(' a()\n {\n}')),
# blank lines are not removed
identical(move_leftbrace(c('a', '', 'b')), c('a', '', 'b')),
identical(move_leftbrace('if (TRUE) {\n if (FALSE) {\n 1\n }\n}'),
'if (TRUE)\n{\n if (FALSE)\n {\n 1\n }\n}'),
identical(move_leftbrace('if (TRUE) {\n 1\n} else {\n 2}'),
'if (TRUE)\n{\n 1\n} else\n{\n 2}')
)
assert(
'reindent_lines() works',
identical(reindent_lines(''), ''),
identical(reindent_lines(c('', '')), c('', '')),
identical(reindent_lines(' ', 2), ' '),
identical(reindent_lines('if (TRUE) {\n 1\n}', 2), 'if (TRUE) {\n 1\n}')
)
formatR/tests/testit/test-tidy.R 0000644 0001762 0000144 00000007113 12700142121 016403 0 ustar ligges users library(testit)
tidy.res = function(x, ...) {
tidy_source(text = x, ..., output = FALSE)$text.tidy
}
assert(
'tidy_source() tries to keep R comments',
identical(tidy.res('1+1#asdf'), '1 + 1 #asdf'),
identical(tidy.res('paste(1 #asdf\n,2)'), 'paste(1 #asdf\n, 2)'),
identical(tidy.res(c('# asdf', '1+1')), c('# asdf', '1 + 1'))
)
assert(
'tidy_source() preserves backslashes in comments',
identical(tidy.res('# \\a \\b \\c'), '# \\a \\b \\c')
)
assert(
'tidy_source() can preserve blank lines among non-empty code lines',
identical(tidy.res(c('if(TRUE){1+1', '', '}', '', '# a comment')),
c('if (TRUE) {\n 1 + 1\n \n}', '', '# a comment'))
)
x1 = paste(c('#', letters), collapse = ' ')
x2 = c('# a b c d e f g h i j', '# k l m n o p q r s t', '# u v w x y z')
assert(
'long comments are wrapped in tidy_source()',
identical(tidy.res(x1, width.cutoff = 20), x2),
identical(
tidy.res(rep(x1, 2), width.cutoff = 20),
c('# a b c d e f g h i j', '# k l m n o p q r s t', '# u v w x y z a b c d',
'# e f g h i j k l m n', '# o p q r s t u v w x', '# y z')
),
identical(tidy.res(c(x1, '1+1', x1), width.cutoff = 20), c(x2, '1 + 1', x2))
)
assert(
'roxygen comments are not wrapped',
identical(tidy.res(c(paste("#'", x1), '1*1')), c(paste("#'", x1), '1 * 1'))
)
x1 = '
# only a comment
'
x2 = c('', '# only a comment', '', '')
assert(
'tidy_source() can deal with code that only contains a comment',
identical(tidy.res(x1), c('', '# only a comment', '')),
identical(tidy.res(x2), x2)
)
x1 = '{if (TRUE) {
1
}
else 2}'
assert(
'tidy_source() moves else back if it is in a standalone line',
identical(tidy.res(x1), '{\n if (TRUE) {\n 1\n } else 2\n}')
)
x1 = '{x=1
else.x=2
}'
assert(
'should not move any lines starting with `else` back to the previous line',
tidy.res(x1) %==% '{\n x = 1\n else.x = 2\n}'
)
x1 = 'if (TRUE) {# comment
1
}'
assert(
'comments after { are moved down one line',
identical(tidy.res(x1), 'if (TRUE) {\n # comment\n 1\n}')
)
assert(
'empty code returns empty string',
identical(tidy.res(''), ''),
identical(tidy.res(c('', ' ')), c('', ' '))
)
assert(
'keep.comment=FALSE removes comments',
identical(tidy.res(c('# a comment', '1+1'), comment = FALSE), '1 + 1')
)
if (packageVersion('formatR') > '0.8') assert(
'when comment=FALSE and everything is comment, tidy_source() returns character(0)',
identical(tidy.res('# a comment', comment = FALSE), character(0))
)
x1 = '1+1
if(F){
}
'
assert(
'keep.blank.line=FALSE removes blank lines',
identical(tidy.res(x1), c('1 + 1', '', 'if (F) {\n \n}', '')),
identical(tidy.res(x1, blank = FALSE), c('1 + 1', 'if (F) {\n}'))
)
assert(
'= can be replaced with <- when replace.assign=TRUE',
identical(tidy.res('x=1;c(x=1)', arrow = TRUE), c('x <- 1', 'c(x = 1)'))
)
assert(
'since R 3.0.0 comments can be written with double quotes in them',
identical(tidy.res('1+1# hello "world"'), "1 + 1 # hello 'world'")
)
x1 = 'x="
# this is not a comment
"'
assert(
'since R 3.0.0, # in the beginning of a line does not necessarily mean comments',
identical(tidy.res(x1), 'x = "\n# this is not a comment\n"')
)
assert(
'the shebang is preserved',
identical(tidy.res(c('#!/usr/bin/Rscript', '1+1')), c('#!/usr/bin/Rscript', '1 + 1'))
)
x1 = paste0('x="', r <- rand_string(2000), '"')
assert(
'Long strings (> 1000 chars) can be preserved',
tidy.res(x1) %==% paste0('x = "', r, '"')
)
x1 = 'x = "
this is a
character string
"'
assert(
'line breaks in strings are preserved instead of being replaced by \\n',
tidy.res(x1) %==% x1
)
formatR/tests/testit/test-usage.R 0000644 0001762 0000144 00000015741 13077173260 016564 0 ustar ligges users library(testit)
capture_usage = function(...) capture.output(usage(..., output = TRUE))
n_spaces = function(n) paste(character(n + 1L), collapse = ' ')
# Test usage() ------------------------------------------------------------
make_fn = function(arg) {
eval(call('function', as.pairlist(arg), quote(expr = )))
}
subsets_lgl = function(n) {
lapply(seq_len(2L ^ n) - 1L, function(.) as.logical(intToBits(.))[1L:n])
}
subsets = function(x) {
lapply(subsets_lgl(length(x)), function(.) x[.])
}
args_pre = alist(x1 = , x2 = , x3 = , x4 = , y1 = 1, y2 = 2, y3 = 3, y4 = 4)
args_post = alist(a = '', b = 'b')
args_wo_dots = subsets(args_pre)
args_w_dots = do.call('c', lapply(subsets(args_post), function(.) {
lapply(args_wo_dots, function(..) c(.., alist(... = ), .))
}))
args = c(args_wo_dots, args_w_dots)
fns = lapply(args, make_fn)
# test usage() for 7.5k+ functions
assert(
'usage() output respects indent and line width, whenver this is feasible',
{
ops = options(formatR.indent = 4L)
re = sprintf('^%s\\S', n_spaces(getOption('formatR.indent')))
w0 = nchar('a_function()')
# call with maximal set of arguments
usg = paste(trimws(
deparse(make_fn(c(args_pre, alist(... = ), args_post))),
which = 'left'), collapse = '')
w1 = nchar(sub('^function ', 'a_function', usg))
assertions = lapply(fns, function(f) {
a_function = f
r = (w1 - w0) %/% 4L
lapply(seq(w0, w1 + r, by = r), function(w) {
out = capture_usage(a_function, w)
c(
'output was created' = length(out) > 0L,
'lines within width' = nchar(out) <= w,
'indentation by indent amt' = grepl(re, out[-1L])
)
})
})
options(ops)
unlist(assertions)
}
)
assert(
'for an S3 method, usage() uses the generic function name in call signature',
{
out = capture_usage(barplot.default, 60L)
TRUE
},
identical(out[1L], '## Default S3 method:'),
identical(substr(out[2L], 1L, 8L), 'barplot(')
)
assert(
'if width constraint is unfulfillable, usage() warns when fail is "warn"',
{
# Verify that width constraint is unfulfillable
out = suppressWarnings(capture_usage(barplot.default, 30L, fail = 'warn'))
any(nchar(out) > 30L)
},
has_warning(capture_usage(barplot.default, 30L, fail = 'warn'))
)
assert(
'if width constraint is unfulfillable, usage() stops when fail is "stop"',
{
out = tryCatch(capture_usage(barplot.default, 30L, fail = 'stop'),
error = function(e) 'Error signaled')
identical(out, 'Error signaled')
}
)
assert(
'if width constraint is unfulfillable, usage() is silent when fail is "none"',
{
out = tryCatch(capture_usage(barplot.default, 30L, fail = 'none'),
warning = function(w) 'Warning signaled',
error = function(e) 'Error signaled')
!out %in% c('Warning signaled', 'Error signaled')
}
)
assert(
'if width constraint is unfulfillable and fail is "warn" or "stop", then
the lengths of all overflowing lines are shown',
{
out = capture.output(
suppressWarnings(usage(barplot.default, 30L, fail = 'warn'))
)
warn = capture.output(cat(
tryCatch(usage(barplot.default, 30L, fail = 'warn'),
warning = conditionMessage)
))[-1L]
bad_lines = out[nchar(out) > 30L]
overflow_out = nchar(bad_lines)
overflow_warn = as.integer(sub('^\\(([[:digit:]]*)\\).*', '\\1', warn))
identical(overflow_out, overflow_warn)
}
)
assert(
'usage() fits entire call on one line if it falls within width',
{
foo = function(bar, ..., baz = "baz") {}
width = nchar('foo(bar, ..., baz = "baz")')
vapply(seq(width, width + 60L, by = 5L), function(w) {
out = usage(foo, width = w, output = FALSE)
identical(nchar(out), width)
}, logical(1))
}
)
assert(
'usage() breaks lines maximally and uniformly when all lines of same length',
{
foo = function(bar, baz = 0,
buzz, x, ...,
y = 2, z = 3) {}
w = nchar('foo(bar, baz = 0,')
out = capture_usage(foo, width = w, indent.by.FUN = TRUE)
TRUE
},
identical(length(out), 3L),
all(nchar(out) == w)
)
assert(
'usage() indents by getOption("formatR.indent", 4L),
when indent.by.FUN is FALSE',
{
foo = function(bar, ..., baz = "baz") {}
TRUE
},
{
ops = options(formatR.indent = NULL)
out = capture_usage(foo, width = 20L, indent.by.FUN = FALSE)
options(ops)
identical(out[2L], ' baz = "baz")')
},
{
ops = options(formatR.indent = 2L)
out = capture_usage(foo, width = 20L)
options(ops)
identical(out[2L], ' baz = "baz")')
}
)
assert(
'usage() indents by function name width, when indent.by.FUN is TRUE',
{
re = function(n) sprintf('^%s\\S', n_spaces(n))
out1 = capture_usage(barplot.default, width = 60L, indent.by.FUN = TRUE)
out2 = capture_usage(stats::lm, width = 60, indent.by.FUN = TRUE)
TRUE
},
grepl(re(nchar('barplot(')), out1[-(1L:2L)]),
grepl(re(nchar('lm(')), out2[-1L])
)
assert(
'usage() breaks line on function name, if function name exceeds width',
{
reallylongfunctionname = function() {}
w = nchar('reallylongfunctionname')
out = capture_usage(reallylongfunctionname, w, fail = 'none')
identical(out, 'reallylongfunctionname()')
},
{
warn = tryCatch(usage(reallylongfunctionname, w, fail = 'warn'),
warning = function(w) {
capture.output(cat(conditionMessage(w)))
})
l = nchar('reallylongfunctionname()')
identical(warn[2L], sprintf('(%s) \"reallylongfunctionname()\"', l))
},
{
reallylongfunctionname = function(bar, baz, ..., a, b, c, d, e) {}
res = lapply(c(5L, 10L, 20L), function(w) {
list(
out = capture_usage(reallylongfunctionname, w, fail = 'none'),
warn = tryCatch(usage(reallylongfunctionname, w, fail = 'warn'),
warning = function(w) {
capture.output(cat(conditionMessage(w)))
})
)
})
l = nchar('reallylongfunctionname(')
vapply(res, function(.) {
all(
nchar(.$out[-1L]) <= w,
identical(.$warn[2L], sprintf('(%s) \"reallylongfunctionname(\"', l))
)
}, logical(1))
}
)
# Test internal functions (optional) --------------------------------------
exprs = list(
quote(foo()),
quote(foo(...)),
quote(foo(bar)),
quote(foo(bar, baz)),
quote(foo(bar = 1, baz)),
quote(foo(bar = 1, baz = "")),
quote(foo(bar, baz = 2)),
quote(foo(bar, ..., baz = 2)),
quote(foo(bar, ..., baz))
)
counts = list(
c(5L),
c(4L, 4L),
c(4L, 4L),
c(4L, 4L, 5L),
c(4L, 8L, 5L),
c(4L, 8L, 10L),
c(4L, 4L, 9L),
c(4L, 4L, 5L, 9L),
c(4L, 4L, 5L, 5L)
)
totals_manual = lapply(counts, cumsum)
totals_count_tokens = lapply(exprs, count_tokens)
assert(
'count_tokens() matches manual count of tokens',
unlist(
Map(function(x, y) isTRUE(all.equal(x, y, check.names = FALSE)),
totals_count_tokens, totals_manual)
)
)
formatR/tests/test-all.R 0000644 0001762 0000144 00000000044 12235532745 014704 0 ustar ligges users library(testit)
test_pkg('formatR')
formatR/NAMESPACE 0000644 0001762 0000144 00000000244 13045521125 013101 0 ustar ligges users # Generated by roxygen2: do not edit by hand
export(tidy_app)
export(tidy_dir)
export(tidy_eval)
export(tidy_file)
export(tidy_source)
export(usage)
import(utils)
formatR/NEWS 0000644 0001762 0000144 00000032532 13077671664 012411 0 ustar ligges users CHANGES IN formatR VERSION 1.5
NEW FEATURES
o added a new function `tidy_file()` to format specified R scripts (thanks,
@edlee123, #61)
o usage() was re-implemented by @egnha in #66; the major difference with the
previous version is that `width` means the maximum width (if possible) instead
of the minimum; it also gained two new arguments, `indent.by.FUN` and `fail`;
see ?formatR::usage for details
CHANGES IN formatR VERSION 1.4
NEW FEATURES
o `tidy_source()` can preserve line breaks in character strings in source code
MAJOR CHANGES
o the deprecated functions tidy.source(), tidy.dir(), and tidy.eval() have
been removed; use tidy_source(), tidy_dir() and tidy_eval() instead
o comments that begin with `#+` or `#-` are no longer wrapped; such comments
are treated as knitr chunk options in `knitr::spin()` (#52)
BUG FIXES
o `tidy_source()` should not write an extra space to the last line of code
(thanks, @mr-karan, #49)
o long strings (> 1000 characters) in source code can be preserved now
(thanks, @jholtman, #50)
o `tidy_source()` might move any lines of code starting with `else` back to
the previous lines (thanks, @Auburngrads, #51)
CHANGES IN formatR VERSION 1.3
NEW FEATURES
o `tidy_source()` can deal with multibyte characters that cannot represented
in the system native encoding now (on Windows)
o `usage()` works for functions obtained from `::` or `:::` now, e.g.
`usage(formatR::tidy_source)`
CHANGES IN formatR VERSION 1.2
MAJOR CHANGES
o the minimal required R version is 3.0.2 now
CHANGES IN formatR VERSION 1.1
NEW FEATURES
o added a new argument `output` to usage()
BUG FIXES
o fixed yihui/knitr#918: when code is NULL, parse() will hang (with a question
mark waiting for input)
CHANGES IN formatR VERSION 1.0
NEW FEATURES
o added a function tidy_app() to replace tidy.gui() in previous versions:
tidy_app() launches a Shiny app in the browser to reformat R code. The
gWidgets interface (e.g. GTK+) is no longer supported. See
https://yihui.shinyapps.io/formatR/ for a live demo.
BUG FIXES
o the shebang #! is no longer treated as an R comment (thanks, Mirko
Ebert, #36)
MAJOR CHANGES
o three functions were renamed (from the `foo.bar` style to `foo_bar`):
`tidy.source()` (`tidy_source()`), `tidy.dir()` (`tidy_dir()`), and
`tidy.eval()` (`tidy_eval()`)
o the arguments of tidy_source() were renamed: `keep.comment` was renamed to
`comment`, `keep.blank.line` -> `blank`, `replace.assign` -> `arrow`,
`left.brace.newline` -> `brace.newline`, and `reindent.spaces` -> `indent`;
similarly, the corresponding global options were also renamed: now you should
use `options(formatR.comment)` instead of `options(keep.comment)`,
`keep.blank.line` -> `formatR.blank`, and so on; see `?tidy_source` and
http://yihui.name/formatR for details
MINOR CHANGES
o the usage() function returns the source code of the usage of a function
now; in previous versions, it only returns NULL
o added a new argument 'tidy' in usage() to make it possible not to
reformat the usage code
o tidy_source() may not work for R 3.0.0 or 3.0.1 if the code only
contains comments due to a bug in base R, which has been fixed; if you use
R 3.0, please upgrade to at least R 3.0.2 (R 2.15.x is not affected)
CHANGES IN formatR VERSION 0.10
MINOR CHANGES
o the argument 'replace.assign' in tidy.source() will be treated as FALSE
if there is no = in the code, because there is no need to replace = in
such cases (this slighly improves the performance)
o the PDF vignette was removed, and only the Markdown vignette was kept in
this package, which uses the vignette engine knitr::docco_linear; see
vignette('formatR', package = 'formatR')
CHANGES IN formatR VERSION 0.9
MAJOR CHANGES
o tidy.source() uses utils::getParseData() to identify comments in R code
under R 3.0.x, which is much more accurate than the regular expressions in
previous versions; users are strongly recommended to try R 3.0.x (#25, #26)
o changed the meaning of the argument 'width' in usage(); see documentation
CHANGES IN formatR VERSION 0.8
MAJOR CHANGES
o tidy.source(text = character(0)) returns character(0) instead of ''
o removed the (dark voodoo) functions parse.tidy() and deparse.tidy() as
well as the operator "%InLiNe_IdEnTiFiEr%"; they were designed for the
pgfSweave package, which has been archived on CRAN for a long time
o the function unmask.source() is no longer exported
CHANGES IN formatR VERSION 0.7
BUG FIXES
o backslashes in whole lines of comments can be correctly retained
now (e.g. #' \code{1+1}) (thanks, KAPLAN Bernard)
o the font button in tidy.gui() works again (#23) (thanks, Dason
Kurkiewicz)
o the option left.brace.newline was buggy; it did not work for empty
lines
MAJOR CHANGES
o the option keep.space in tidy.source() was removed; the spaces
before comments will not be faithfully kept
NEW FEATURES
o the number of spaces for indentation can be specified in
tidy.gui()
CHANGES IN formatR VERSION 0.6
NEW FEATURES
o the replace.assign argument is much more reliable now; it is based
on the codetools package (code analysis) instead of regular
expressions (big thanks to Kohske Takahashi)
o replace.assign also works when keep.comment=FALSE; in previous
versions, replace.assign=TRUE only applies to keep.comment=TRUE
o tidy.source() gained a new argument 'left.brace.newline'; when set
to TRUE, the left curly brace { will be moved to a new line (#18)
(thanks, Jared Lander)
MAJOR CHANGES
o the 'text.tidy' component in the results of tidy.source() is a
character vector of code blocks instead of code lines now, e.g. in
previous versions, the result may be c('if (TRUE) {', '1', '}')
(vector of length 3), but now it becomes 'if (TRUE) {\n1\n}'; each
element of 'text.tidy' contains a minimal complete code block
o potential dependency on the parser package has been removed
(replaced by the codetools package); this also makes it more robust
to use Unicode characters in R code now, see issue #13 for example
o roxygen comments (#') will not be reflowed; this gives us control
over which comments to be reflowed (sometimes we do not want
comments to be wrapped and we can write them in the special roxygen
comments)
MINOR CHANGES
o the results of tidy.source() (a list) only contain text.tidy and
text.mask now; begin.comment and end.comment were removed since they
were not used anywhere
CHANGES IN formatR VERSION 0.5
MAJOR CHANGES
o the dependency on the parser package was removed because it was
orphaned on CRAN; this affects two features: replace = with <- (the
'replace.assign' option in tidy.source()) and the identification of
inline comments; tidy.source() will still work in most cases, but
please keep in mind that (1) 'replace.assign=TRUE' will not be
entirely reliable without parser (so use with extreme caution if you
do not have parser installed) (2) if you want to write # in a
character string, you must use double quotes, e.g. "here is a #"
will be fine whereas 'here is a #' is not; if you want to use quotes
in comments, please always use single quotes, e.g. # 'single quotes'
(inline comments that contain double quotes will be dropped); if the
parser package is available in your system (e.g. you installed it
from the archived source on CRAN), everything will be the same as
before
o the default value for 'envir' in tidy.eval() was changed from
globalenv() to parent.frame()
MINOR CHANGES
o \\t will no longer be replaced with \t when keep.space=TRUE
because it is dangerous to do so; see #17 for an example
CHANGES IN formatR VERSION 0.4
NEW FEATURES
o a new argument 'reindent.spaces' for tidy.source() to reindent the
code with a specified number of spaces (e.g. 2)
o comments will be reflowed as a whole block when possible (instead
of being wrapped line by line); thanks, Paul Johnson
MAJOR CHANGES
o when a comment block is reflowed, the second and following lines
will not be indented
o the default value of the 'width.cutoff' argument in tidy.source()
is getOption('width') now; in the past it was 75% of that width
which was less intuitive
o part of the documentation of tidy.source() has been moved to
https://github.com/yihui/formatR/wiki/
o internally the comments are preserved by putting them in an
expression invisible("# comments"); in past versions comments were
retained in assignments; this change should not affect end users
BUG FIXES
o fixed #16: \\ in comments are preserved correctly now
CHANGES IN formatR VERSION 0.3-4
MINOR CHANGES
o slight tweaks to the vignette (stopped Sweave from adding
\usepackage{Sweave} which introduces ae by default)
o fixed the error message in tidy.source(), pointing users to the
wiki page on GitHub (thanks, Gabor Grothendieck)
CHANGES IN formatR VERSION 0.3-3
MINOR CHANGES
o functions unmask.source(), parse.tidy(), deparse.tidy() and the
operator %InLiNe_IdEnTiFiEr% were marked as `internal' in
documentation
o the vignette is processed by the knitr package
o fixed a buglet in usage() so it can process functions with dots
correctly
CHANGES IN formatR VERSION 0.3-2
MINOR CHANGES
o the parser package is imported (in previous versions formatR
depends on parser); thanks, Romain Francois
CHANGES IN formatR VERSION 0.3-1
SIGNIFICANT CHANGES
o the function formatR() was renamed to tidy.gui() which is a more
meaningful name since it is used to create a GUI
NEW FEATURES
o usage() will tell if the function is S3
o a wiki is set up as the manual for formatR:
https://github.com/yihui/formatR/wiki
o tidy.eval() can evaluate the code in a specified environment now;
see the 'envir' argument
MINOR CHANGES
o keep.blank.line is TRUE by default now (was FALSE in previous
versions), i.e. blank lines are preserved by default
CHANGES IN formatR VERSION 0.2-4
NEW FEATURES
o a new function tidy.eval(): evaluate R code and insert the output
masked in comments (following ##)
o the empty lines before 'else' will be removed even if
keep.blank.line = TRUE; it is ill-advised to use blank lines among
incomplete code chunks, e.g.
if (TRUE)
{'this is a BAD style of R programming'}
o tidy.source() reports the line number when errors occur, which can
help users detect the problem in the R code more quickly (thanks,
Hadley Wickham)
CHANGES IN formatR VERSION 0.2-3
NEW FEATURES
o 'else ...' will be moved back to the last line so that we will no
longer see an 'else' statement in a new line
CHANGES IN formatR VERSION 0.2-2
NEW FEATURES
o formatR now uses the parser package to parse inline comments,
which can guarantee that these comments will be correctly parsed (no
longer uses the 'unsafe' regular expressions to parse comments, so
forget about the previous rules of writing comments -- just write
comments with an arbitrary number of spaces before # as you wish)
o the use of parser also enabled a new feature: '=' can be
replaced with '<-' wherever appropriate (for example, '=' in
function arguments will not be replaced; only thoese equal signs
which are used to assigning purposes can be replaced)
o long roxygen comments will not be wrapped (i.e. comments begin
with #' or ##')
MINOR CHANGES
o fixed a minor problem in the function usage() (out --> output)
o comments after { will be moved to the next line (in previous
versions, these comments will cause errors)
CHANGES IN formatR VERSION 0.2-1
MINOR CHANGES
o the escape character '\' in comments of complete lines will be
successfully preserved, which is especially useful for tidy.source()
to format the roxygen comments since we usually write comments like
"##' @author Someone \email{}" but "\e" is not a legal character in
R (this will lead to errors in earlier versions of this package)
CHANGES IN formatR VERSION 0.2-0
NEW FEATURES
o a new function usage() to print the formatted usage of a function
CHANGES IN formatR VERSION 0.1-9
NEW FEATURES
o tidy.source() can wrap long comments into shorter ones now (this
only applies to the whole lines of comments; the inline comments
will not be wrapped since it is tricky to do so)
MINOR CHANGES
o '\t' will be parsed to ' ' when 'keep.space' is TRUE in
tidy.source() (this might be undesirable, though)
CHANGES IN formatR VERSION 0.1-8
NEW FEATURES
o new functions parse.tidy() and deparse.tidy() for the package
pgfSweave to help tidy the source code in Sweave
o a new function tidy.dir() to format all the R scripts under a
directory
o added a package vignette
CHANGES IN formatR VERSION 0.1-7
NEW FEATURES
o full support to multi-byte characters in the formatR() GUI
o a new function unmask.source() to obtain the real source code
from the masked source
o a new operator '%InLiNe_IdEnTiFiEr%' designed mainly for
pgfSweave (mask the inline comments)
CHANGES IN formatR VERSION 0.1-6
NEW FEATURES
o the inline comments will also be preserved in most cases (in
earlier versions, only single lines of comments are preserved)
o tidy.source() gained a new argument 'text' to accept a character
vector as the source code
o multi-byte characters are partially supported in the formatR() GUI
now (full support will come in 0.1-7)
formatR/R/ 0000755 0001762 0000144 00000000000 13077671653 012104 5 ustar ligges users formatR/R/utils.R 0000644 0001762 0000144 00000015703 13077201353 013357 0 ustar ligges users # replace `=` by `<-` in expressions
replace_assignment = function(exp) {
wc = codetools::makeCodeWalker(
call = function(e, w) {
cl = codetools::walkCode(e[[1]], w)
arg = lapply(as.list(e[-1]), function(a) if (missing(a)) NA else {
codetools::walkCode(a, w)
})
as.call(c(list(cl), arg))
},
leaf = function(e, w) {
if (length(e) == 0 || inherits(e, "srcref")) return(NULL)
# x = 1 is actually `=`(x, 1), i.e. `=` is a function
if (identical(e, as.name("="))) e <- as.name("<-")
e
})
lapply(as.list(exp), codetools::walkCode, w = wc)
}
## mask comments to cheat R
mask_comments = function(x, width, keep.blank.line) {
d = utils::getParseData(parse_source(x))
if (nrow(d) == 0 || (n <- sum(d$terminal)) == 0) return(x)
d = d[d$terminal, ]
d = fix_parse_data(d, x)
d.line = d$line1; d.line2 = d$line2; d.token = d$token; d.text = d$text
# move else back
for (i in which(d.token == 'ELSE')) {
delta = d.line[i] - d.line[i - 1]
d.line[i:n] = d.line[i:n] - delta
d.line2[i:n] = d.line2[i:n] - delta
}
# how many blank lines after each token?
blank = c(pmax(d.line[-1] - d.line2[-n] - 1, 0), 0)
i = d.token == 'COMMENT'
# double backslashes and replace " with ' in comments
d.text[i] = gsub('"', "'", gsub('\\\\', '\\\\\\\\', d.text[i]))
c0 = d.line[-1] != d.line[-n] # is there a line change?
c1 = i & c(TRUE, c0 | (d.token[-n] == "'{'")) # must be comment blocks
c2 = i & !c1 # inline comments
c3 = c1 & grepl("^#+[-'+]", d.text) # roxygen or knitr spin() comments
if (grepl('^#!', d.text[1])) c3[1] = TRUE # shebang comment
# reflow blocks of comments: first collapse them, then wrap them
i1 = which(c1 & !c3) # do not wrap roxygen comments
j1 = i1[1]
if (length(i1) > 1) for (i in 2:length(i1)) {
# two neighbor lines of comments
if (d.line[i1[i]] - d.line[i1[i - 1]] == 1) {
j2 = i1[i]
d.text[j1] = paste(d.text[j1], sub('^#+', '', d.text[j2]))
d.text[j2] = ''
c1[j2] = FALSE # the second line is no longer a comment
} else j1 = i1[i]
}
# mask block and inline comments
d.text[c1 & !c3] = reflow_comments(d.text[c1 & !c3], width)
d.text[c3] = sprintf('invisible("%s%s%s")', begin.comment, d.text[c3], end.comment)
d.text[c2] = sprintf('%%InLiNe_IdEnTiFiEr%% "%s"', d.text[c2])
# add blank lines
if (keep.blank.line) for (i in seq_along(d.text)) {
if (blank[i] > 0)
d.text[i] = paste(c(d.text[i], rep(blank.comment, blank[i])), collapse = '\n')
}
unlist(lapply(split(d.text, d.line), paste, collapse = ' '), use.names = FALSE)
}
# no blank lines before an 'else' statement!
move_else = function(x) {
blank = grepl('^\\s*$', x)
if (!any(blank)) return(x)
else.line = grep('^\\s*else(\\s+|$)', x)
for (i in else.line) {
j = i - 1
while (blank[j]) {
blank[j] = FALSE; j = j - 1 # search backwards & rm blank lines
warning('removed blank line ', j, " (should not put an 'else' in a separate line!)")
}
}
x[blank] = blank.comment
x
}
# a literal # must be writen in double quotes, e.g. "# is not comment"
mask_inline = function(x) {
# move comments after { to the next line
if (length(idx <- grep('\\{\\s*#.*$', x))) {
p = paste('{\ninvisible("', begin.comment, '\\1', end.comment, '")', sep = '')
x[idx] = gsub('\\{\\s*(#.*)$', p, x[idx])
}
gsub('(#[^"]*)$', ' %InLiNe_IdEnTiFiEr% "\\1"', x)
}
# reflow comments (excluding roxygen comments)
reflow_comments = function(x, width) {
if (length(x) == 0) return(x)
# returns a character vector of the same length as x
b = sub('^(#+).*', '\\1', x)
mapply(function(res, prefix) {
paste(sprintf(
'invisible("%s%s%s")', begin.comment, paste(prefix, res), end.comment
), collapse = '\n')
}, strwrap(sub('^#+', '', x), width = width, simplify = FALSE), b)
}
# reindent lines with a different number of spaces
reindent_lines = function(text, n = 2) {
if (length(text) == 0) return(text)
if (n == 4) return(text) # no need to do anything
s = paste(rep(' ', n), collapse = '')
unlist(lapply(strsplit(text, '\n'), function(x) {
t1 = gsub('^( *)(.*)', '\\1', x)
t2 = gsub('^( *)(.*)', '\\2', x)
paste(gsub(' {4}', s, t1), t2, sep = '', collapse = '\n')
}), use.names = FALSE)
}
# move { to the next line
move_leftbrace = function(text) {
if (!length(text)) return(text)
# the reason to use lapply() here is that text is a vector of source code with
# each element being a complete R expression; we do not want to break the
# expression structure; same reason for reindent_lines() above
unlist(lapply(strsplit(text, '\n'), function(x) {
if (length(x) > 1L && length(idx <- grep('(\\)|else) \\{$', x))) {
# indent the same amount of spaces as the { lines
pre = gsub('^( *)(.*)', '\\1', x[idx])
x[idx] = mapply(gsub, '(\\)|else) \\{$', sprintf('\\1\n%s{', pre), x[idx],
USE.NAMES = FALSE)
}
paste(x, collapse = '\n')
}), use.names = FALSE)
}
# parse but do not keep source (moved from knitr)
parse_only = function(code) {
if (length(code) == 0) return(expression())
base::parse(text = code, keep.source = FALSE)
}
# copied from highr
# TODO: eventually remove the hack for R <= 3.2.2
parse_source = if (getRversion() > '3.2.2') function(lines) {
parse(text = lines, keep.source = TRUE)
} else function(lines) {
# adapted from evaluate
src = srcfilecopy('', lines = '')
if (length(grep('\n', lines))) lines = unlist(strsplit(
sub('$', '\n', as.character(lines)), '\n'
))
src$lines = lines
parse(text = lines, srcfile = src)
}
# restore backslashes
restore_bs = function(x) gsub('\\\\\\\\', '\\\\', x)
# a workaround for the R bug (long strings are truncated in getParseData()):
# https://bugs.r-project.org/bugzilla3/show_bug.cgi?id=16354
fix_parse_data = function(d, x) {
if (length(s <- which(d$token == 'STR_CONST')) == 0) return(d)
ws = s[grep('^\\[\\d+ (wide )?chars quoted with \'"\'\\]$', d$text[s])]
for (i in ws) {
di = d[i, , drop = FALSE]
d[i, 'text'] = get_src_string(x, di$line1, di$line2, di$col1, di$col2)
}
d[s, 'text'] = mask_line_break(d[s, 'text'])
d
}
get_src_string = function(x, l1, l2, c1, c2) {
if (l1 == l2) return(substr(x[l1], c1, c2))
x[l1] = substr(x[l1], c1, nchar(x[l1]))
x[l2] = substr(x[l2], 1, c2)
paste(x[l1:l2], collapse = '\n')
}
# generate a random string
CHARS = c(letters, LETTERS, 0:9)
rand_string = function(len = 32) {
paste(sample(CHARS, len, replace = TRUE), collapse = '')
}
.env = new.env()
.env$line_break = NULL
mask_line_break = function(x) {
if (length(grep('\n', x)) == 0) return(x)
m = (function() {
for (i in 2:10) {
for (j in 1:100) if (length(grep(s <- rand_string(i), x)) == 0) return(s)
}
})()
if (is.null(m)) return(x)
.env$line_break = m
gsub('\n', m, x)
}
trimws = function(x, which = c('both', 'left', 'right')) {
switch(match.arg(which),
both = gsub('^\\s+|\\s+$', '', x),
left = gsub('^\\s+', '', x), right = gsub('\\s+$', '', x)
)
}
formatR/R/eval.R 0000644 0001762 0000144 00000003207 13077671653 013160 0 ustar ligges users #' Evaluate R code and mask the output by a prefix
#'
#' This function is designed to insert the output of each chunk of R code into
#' the source code without really breaking the source code, since the output is
#' masked in comments.
#' @param source the input filename (by default the clipboard; see
#' \code{\link{tidy_source}})
#' @param ... other arguments passed to \code{\link{tidy_source}}
#' @param file the file to write by \code{\link{cat}}; by default the output is
#' printed on screen
#' @param prefix the prefix to mask the output
#' @param envir the environment in which to evaluate the code (by default the
#' parent environment; if we do not want to mess up with the parent
#' environment, we can set \code{envir = NULL} or \code{envir = new.env()})
#' @return Evaluated R code with corresponding output (printed on screen or
#' written in a file).
#' @export
#' @references \url{https://yihui.name/formatR}
#' @examples library(formatR)
#' ## evaluate simple code as a character vector
#' tidy_eval(text = c('a<-1+1;a','matrix(rnorm(10),5)'))
#'
#' ## evaluate a file
#' tidy_eval(system.file('format', 'messy.R', package = 'formatR'))
tidy_eval = function(source = 'clipboard', ..., file = '', prefix = '## ', envir = parent.frame()) {
txt = tidy_source(source, ..., output = FALSE)$text.tidy
for(i in 1:length(txt)) {
cat(txt[i], '\n', sep = '', file = file, append = TRUE)
out = capture.output(eval(res <- parse_only(txt[i]), envir = envir))
if (length(res) > 0L && length(out) > 0L) {
cat(paste(prefix, out, sep = ''), sep = '\n', file = file, append = TRUE)
cat('\n', file = file, append = TRUE)
}
}
}
formatR/R/usage.R 0000644 0001762 0000144 00000014407 13077671653 013341 0 ustar ligges users deparse_collapse = function(x) {
d = deparse(x)
if (length(d) > 1L) {
paste(trimws(d, which = 'both'), collapse = ' ')
} else {
d
}
}
count_tokens = function(.call) {
if (length(.call) == 1L) {
# +2 for '()'
return(nchar(.call) + 2L)
}
# +1 for value-delimiting '(', ',', or ')'
cnt_val = nchar(vapply(.call, deparse_collapse, character(1L))) + 1L
nms = names(.call[-1L])
if (is.null(nms)) nms = character(length(.call[-1L]))
# nchar() of argument names
cnt_nm = nchar(nms)
# +3 for ' = ', for argument-value pairs
cnt_nm[cnt_nm != 0L] = cnt_nm[cnt_nm != 0L] + 3L
# +1 for space before name, beyond the first argument
cnt_nm[-1L] = cnt_nm[-1L] + 1L
# function itself is not a named component
cnt_nm = c(0L, cnt_nm)
cumsum(cnt_val + cnt_nm)
}
# counts is a strictly increasing, positive integer vector
find_breaks = function(counts, width, indent, track, counted = 0L) {
if (!length(counts)) {
return(list(breaks = NULL, overflow = NULL))
}
overflow = NULL
shift = if (counted == 0L) 0L else indent
fits = counts - counted + shift <= width
i = which.min(fits) - 1L
if (i == 0L) {
if (fits[[1L]]) {
# all components of fits_on_line are TRUE
i = length(counts)
} else {
# all components of fits_on_line are FALSE
overflow = track(counted, counts[1L], shift)
i = 1L
}
}
post_space = if (i == 1L && counted == 0L) 0L else 1L
rest = Recall(counts[-(1L:i)], width, indent, track, counts[i] + post_space)
list(
breaks = c(counts[i], rest$breaks),
overflow = c(overflow, rest$overflow)
)
}
overflow_message = function(overflow, width, indent, text) {
header = sprintf('Could not fit all lines to width %s (with indent %s):',
width, indent)
idxs = seq_along(overflow)
args = vapply(idxs[idxs %% 3L == 1L], function(i) {
l = paste(c(rep(' ', overflow[i + 2L]),
trimws(substr(text, overflow[i] + 1L, overflow[i + 1L]),
which = 'left')),
collapse = '')
sprintf('(%s) \"%s\"', nchar(l), l)
}, character(1L))
paste(c(header, args), collapse = '\n')
}
tidy_usage = function(nm, usg, width, indent, fail) {
text = paste(trimws(usg, which = 'both'), collapse = ' ')
text = sub(sprintf('^%s\\s*', nm), nm, text)
expr = parse(text = text)[[1L]]
track_overflow = if (fail == 'none') function(...) NULL else base::c
breaks = find_breaks(count_tokens(expr), width, indent, track_overflow)
if (length(breaks$overflow)) {
signal = switch(fail, stop = 'stop', warn = 'warning')
msg = overflow_message(breaks$overflow, width, indent, text)
getFromNamespace(signal, 'base')(msg, call. = FALSE)
}
breaks = c(0L, breaks$breaks)
newline = paste(c('\n', character(indent)), collapse = ' ')
paste(
vapply(1L:(length(breaks) - 1L), function(i) {
trimws(substr(text, breaks[i] + 1L, breaks[i + 1L]), which = 'left')
}, character(1L)),
collapse = newline
)
}
#' Show the usage of a function
#'
#' Print the reformatted usage of a function. The arguments of the function are
#' searched by \code{\link{argsAnywhere}}, so the function can be either
#' exported or non-exported in a package. S3 methods will be marked.
#'
#' @param FUN the function name
#' @param width the width of output
#' @param tidy whether to reformat the usage code
#' @param output whether to write the output to the console (via
#' \code{\link{cat}})
#' @param indent.by.FUN whether to indent subsequent lines by the width of the
#' function name (see \dQuote{Details})
#' @param fail a character string that determines whether to generate a warning,
#' stop, or do neither, if the width constraint is unfulfillable (default is
#' \code{"warn"})
#' @return The R code for the usage is returned as a character string
#' (invisibly).
#' @details Line breaks in the output occur between arguments. In particular,
#' default values of arguments will not be split across lines.
#'
#' When \code{indent.by.FUN} is \code{FALSE}, indentation is set by the option
#' \code{\link{getOption}("formatR.indent", 4L)}, the default value of the
#' \code{indent} argument of \code{\link{tidy_source}}.
#' @seealso \code{\link{tidy_source}}
#' @export
#' @examples library(formatR)
#' usage(var)
#'
#' usage(plot)
#'
#' usage(plot.default) # default method
#' usage('plot.lm') # on the 'lm' class
#'
#' usage(usage)
#'
#' usage(barplot.default, width = 60) # output lines have 60 characters or less
#'
#' # indent by width of 'barplot('
#' usage(barplot.default, width = 60, indent.by.FUN = TRUE)
#'
#' \dontrun{
#' # a warning is raised because the width constraint is unfulfillable
#' usage(barplot.default, width = 30)
#' }
usage = function(FUN, width = getOption('width'), tidy = TRUE, output = TRUE,
indent.by.FUN = FALSE, fail = c('warn', 'stop', 'none')) {
fail = match.arg(fail)
fn = as.character(substitute(FUN))
res = capture.output(if (is.function(FUN)) args(FUN) else {
do.call(argsAnywhere, list(fn))
})
if (identical(res, 'NULL')) return()
res[1] = substring(res[1], 9) # rm 'function ' in the beginning
isS3 = FALSE
if (length(fn) == 3 && (fn[1] %in% c('::', ':::'))) fn = fn[3]
if (grepl('.', fn, fixed = TRUE)) {
n = length(parts <- strsplit(fn, '.', fixed = TRUE)[[1]])
for (i in 2:n) {
gen = paste(parts[1L:(i - 1)], collapse = ".")
cl = paste(parts[i:n], collapse = ".")
if (gen == "" || cl == "") next
if (!is.null(f <- getS3method(gen, cl, TRUE)) && !is.null(environment(f))) {
res[1] = paste(gen, res[1])
header = if (cl == 'default')
'## Default S3 method:' else sprintf("## S3 method for class '%s'", cl)
res = c(header, res)
isS3 = TRUE
break
}
}
}
if (!isS3) res[1] = paste(fn, res[1])
if ((n <- length(res)) > 1 && res[n] == 'NULL') res = res[-n] # rm last element 'NULL'
if (!tidy) {
if (output) cat(res, sep = '\n')
return(invisible(res))
}
nm = if (isS3) gen else fn
usg = if (isS3) res[-1L] else res
indent = if (indent.by.FUN) {
# +1 for '('
nchar(nm) + 1L
} else {
# Default indent for tidy_source()
getOption('formatR.indent', 4L)
}
out = tidy_usage(nm, usg, width, indent, fail)
if (isS3) out = c(res[1L], out)
if (output) cat(out, sep = '\n')
invisible(out)
}
formatR/R/tidy.R 0000644 0001762 0000144 00000016361 13077671653 013207 0 ustar ligges users #' Reformat R code while preserving blank lines and comments
#'
#' This function returns reformatted source code; it tries to preserve blank
#' lines and comments, which is different with \code{\link{parse}} and
#' \code{\link{deparse}}. It can also replace \code{=} with \code{<-} where
#' \code{=} means assignments, and reindent code by a specified number of spaces
#' (default is 4).
#' @param source a character string: location of the source code (default to be
#' the clipboard; this means we can copy the code to clipboard and use
#' \code{tidy_source()} without specifying the argument \code{source})
#' @param comment whether to keep comments (\code{TRUE} by default)
#' @param blank whether to keep blank lines (\code{TRUE} by default)
#' @param arrow whether to replace the assign operator \code{=} with \code{<-}
#' @param brace.newline whether to put the left brace \code{\{} to a new line
#' (default \code{FALSE})
#' @param indent number of spaces to indent the code (default 4)
#' @param output output to the console or a file using \code{\link{cat}}?
#' @param text an alternative way to specify the input: if it is \code{NULL},
#' the function will read the source code from the \code{source} argument;
#' alternatively, if \code{text} is a character vector containing the source
#' code, it will be used as the input and the \code{source} argument will be
#' ignored
#' @param width.cutoff passed to \code{\link{deparse}}: integer in [20, 500]
#' determining the cutoff at which line-breaking is tried (default to be
#' \code{getOption("width")})
#' @param ... other arguments passed to \code{\link{cat}}, e.g. \code{file}
#' (this can be useful for batch-processing R scripts, e.g.
#' \code{tidy_source(source = 'input.R', file = 'output.R')})
#' @return A list with components \item{text.tidy}{the reformatted code as a
#' character vector} \item{text.mask}{the code containing comments, which are
#' masked in assignments or with the weird operator}
#' @note Be sure to read the reference to know other limitations.
#' @author Yihui Xie <\url{https://yihui.name}> with substantial contribution
#' from Yixuan Qiu <\url{http://yixuan.cos.name}>
#' @seealso \code{\link{parse}}, \code{\link{deparse}}
#' @references \url{https://yihui.name/formatR} (an introduction to this package,
#' with examples and further notes)
#' @import utils
#' @export
#' @example inst/examples/tidy.source.R
tidy_source = function(
source = 'clipboard', comment = getOption('formatR.comment', TRUE),
blank = getOption('formatR.blank', TRUE),
arrow = getOption('formatR.arrow', FALSE),
brace.newline = getOption('formatR.brace.newline', FALSE),
indent = getOption('formatR.indent', 4),
output = TRUE, text = NULL,
width.cutoff = getOption('width'), ...
) {
if (is.null(text)) {
if (source == 'clipboard' && Sys.info()['sysname'] == 'Darwin') {
source = pipe('pbpaste')
}
} else {
source = textConnection(text); on.exit(close(source), add = TRUE)
}
text = readLines(source, warn = FALSE)
if (length(text) == 0L || all(grepl('^\\s*$', text))) {
if (output) cat('\n', ...)
return(list(text.tidy = text, text.mask = text))
}
if (blank) {
one = paste(text, collapse = '\n') # record how many line breaks before/after
n1 = attr(regexpr('^\n*', one), 'match.length')
n2 = attr(regexpr('\n*$', one), 'match.length')
}
on.exit(.env$line_break <- NULL, add = TRUE)
if (comment) text = mask_comments(text, width.cutoff, blank)
text.mask = tidy_block(text, width.cutoff, arrow && length(grep('=', text)))
text.tidy = if (comment) unmask_source(text.mask) else text.mask
text.tidy = reindent_lines(text.tidy, indent)
if (brace.newline) text.tidy = move_leftbrace(text.tidy)
# restore new lines in the beginning and end
if (blank) text.tidy = c(rep('', n1), text.tidy, rep('', n2))
if (output) cat(text.tidy, sep = '\n', ...)
invisible(list(text.tidy = text.tidy, text.mask = text.mask))
}
## if you have variable names like this in your code, then you really beat me...
begin.comment = '.BeGiN_TiDy_IdEnTiFiEr_HaHaHa'
end.comment = '.HaHaHa_EnD_TiDy_IdEnTiFiEr'
pat.comment = sprintf('invisible\\("\\%s|\\%s"\\)', begin.comment, end.comment)
mat.comment = sprintf('invisible\\("\\%s([^"]*)\\%s"\\)', begin.comment, end.comment)
inline.comment = ' %InLiNe_IdEnTiFiEr%[ ]*"([ ]*#[^"]*)"'
blank.comment = sprintf('invisible("%s%s")', begin.comment, end.comment)
# wrapper around parse() and deparse()
tidy_block = function(text, width = getOption('width'), arrow = FALSE) {
exprs = parse_only(text)
if (length(exprs) == 0) return(character(0))
exprs = if (arrow) replace_assignment(exprs) else as.list(exprs)
sapply(exprs, function(e) paste(base::deparse(e, width), collapse = '\n'))
}
# Restore the real source code from the masked text
unmask_source = function(text.mask) {
if (length(text.mask) == 0) return(text.mask)
m = .env$line_break
if (!is.null(m)) text.mask = gsub(m, '\n', text.mask)
## if the comments were separated into the next line, then remove '\n' after
## the identifier first to move the comments back to the same line
text.mask = gsub('%InLiNe_IdEnTiFiEr%[ ]*\n', '%InLiNe_IdEnTiFiEr%', text.mask)
## move 'else ...' back to the last line
text.mask = gsub('\n\\s*else(\\s+|$)', ' else\\1', text.mask)
if (any(grepl('\\\\\\\\', text.mask)) &&
(any(grepl(mat.comment, text.mask)) || any(grepl(inline.comment, text.mask)))) {
m = gregexpr(mat.comment, text.mask)
regmatches(text.mask, m) = lapply(regmatches(text.mask, m), restore_bs)
m = gregexpr(inline.comment, text.mask)
regmatches(text.mask, m) = lapply(regmatches(text.mask, m), restore_bs)
}
text.tidy = gsub(pat.comment, '', text.mask)
# inline comments should be terminated by $ or \n
text.tidy = gsub(paste(inline.comment, '(\n|$)', sep = ''), ' \\1\\2', text.tidy)
# the rest of inline comments should be appended by \n
gsub(inline.comment, ' \\1\n', text.tidy)
}
#' Format all R scripts under a directory, or specified R scripts
#'
#' \code{tidy_dir()} first looks for all the R scripts under a directory (using
#' the pattern \code{"[.][RrSsQq]$"}), then uses \code{\link{tidy_source}} to
#' tidy these scripts. The original scripts will be overwritten with reformatted
#' code if reformatting was successful. You may need to back up the original
#' directory first if you do not fully understand the tricks used by
#' \code{\link{tidy_source}}. \code{tidy_file()} formats specified R scripts.
#' @param path the directory
#' @param recursive whether to recursively look for R scripts under \code{path}
#' @param ... other arguments to be passed to \code{\link{tidy_source}}
#' @param file a vector of filenames
#' @return Invisible \code{NULL}.
#' @author Yihui Xie (\code{tidy_dir}) and Ed Lee (\code{tidy_file})
#' @seealso \code{\link{tidy_source}}
#' @export
#' @examples
#' library(formatR)
#'
#' path = tempdir()
#' file.copy(system.file('demo', package = 'base'), path, recursive=TRUE)
#' tidy_dir(path, recursive=TRUE)
tidy_dir = function(path = '.', recursive = FALSE, ...) {
tidy_file(list.files(
path, pattern = '[.][RrSsQq]$', full.names = TRUE, recursive = recursive
), ...)
}
#' @export
#' @rdname tidy_dir
tidy_file = function(file, ...) {
for (f in file) {
message("tidying ", f)
try(tidy_source(f, file = f, ...))
}
}
formatR/R/shiny.R 0000644 0001762 0000144 00000000565 12376444004 013354 0 ustar ligges users #' A Shiny app to format R code
#'
#' This function calls \code{\link{tidy_source}()} to format R code in a Shiny
#' app. The arguments of \code{tidy_source()} are presented in the app as input
#' widgets such as checkboxes.
#' @export
#' @examples if (interactive()) formatR::tidy_app()
tidy_app = function() {
shiny::runApp(system.file('shiny', package = 'formatR'))
}
formatR/vignettes/ 0000755 0001762 0000144 00000000000 13077671755 013716 5 ustar ligges users formatR/vignettes/formatR.Rmd 0000644 0001762 0000144 00000024726 13077671653 016004 0 ustar ligges users ---
title: formatR
subtitle: Format R code automatically
author: Yihui Xie
date: "`r Sys.Date()`"
show_toc: true
slug: formatR
githubEditURL: https://github.com/yihui/formatR/edit/master/vignettes/formatR.Rmd
output:
knitr:::html_vignette:
toc: yes
vignette: >
%\VignetteEngine{knitr::rmarkdown}
%\VignetteIndexEntry{An Introduction to formatR}
---
```{r setup, include=FALSE}
options(formatR.indent = 4, width = 70)
knitr::opts_chunk$set(tidy = TRUE)
```
# 1. Installation
You can install **formatR** from [CRAN](https://cran.r-project.org/package=formatR), or [XRAN](https://xran.yihui.name) if you want to test the latest development version:
```{r eval=FALSE}
install.packages('formatR', repos = 'http://cran.rstudio.com')
#' to install the development version, run
#' install.packages('formatR', repos = 'https://xran.yihui.name')
```
Or check out the [Github repository](https://github.com/yihui/formatR) and install from source if you know what this means. This page is always based on the development version.
```{r}
library(formatR)
sessionInfo()
```
# 2. Reformat R code
The **formatR** package was designed to reformat R code to improve readability; the main workhorse is the function `tidy_source()`. Features include:
- long lines of code and comments are reorganized into appropriately shorter ones
- spaces and indent are added where necessary
- comments are preserved in most cases
- the number of spaces to indent the code (i.e. tab width) can be specified (default is 4)
- an `else` statement in a separate line without the leading `}` will be moved one line back
- `=` as an assignment operator can be replaced with `<-`
- the left brace `{` can be moved to a new line
Below is an example of what `tidy_source()` can do. The source code is:
```{r example, eval=FALSE, tidy=FALSE}
## comments are retained;
# a comment block will be reflowed if it contains long comments;
#' roxygen comments will not be wrapped in any case
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 # only 'single quotes' are allowed in comments
lm(y~x1+x2, data=data.frame(y=rnorm(100),x1=rnorm(100),x2=rnorm(100))) ### a linear model
1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1 # comment after a long line
## here is a long long long long long long long long long long long long long comment which will be wrapped
```
We can copy the above code to clipboard, and type `tidy_source(width.cutoff = 50)` to get:
```{r example, eval=FALSE, tidy.opts=list(width.cutoff=50)}
```
Two applications of `tidy_source()`:
- `tidy_dir()` can reformat all R scripts under a directory
- `usage()` can reformat the usage of a function, e.g. compare `usage()` with the default output of `args()`:
```{r collapse=TRUE}
library(formatR)
usage(glm, width = 40) # can set arbitrary width here
args(glm)
```
# 3. The Graphical User Interface
If the **shiny** packages has been installed, the function `tidy_app()` can launch a Shiny app to reformat R code like this ([live demo](https://yihui.shinyapps.io/formatR/)):
```r
formatR::tidy_app()
```
```{r echo=FALSE, results='asis'}
if (ignore_img <- Sys.getenv('USER', '') != 'yihui') cat('')
```
# 4. Evaluate the code and mask output in comments
It is often a pain when trying to copy R code from other people's code which has been run in R and the prompt characters (usually `> `) are attached in the beginning of code, because we have to remove all the prompts `> ` and `+ ` manually before we are able to run the code. However, it will be convenient for the reader to understand the code if the output of the code can be attached. This motivates the function `tidy_eval()`, which uses `tidy_source()` to reformat the source code, evaluates the code in chunks, and attaches the output of each chunk as comments which will not actually break the original source code. Here is an example:
```{r comment=NA}
set.seed(123)
tidy_eval(text = c("a<-1+1;a # print the value", "matrix(rnorm(10),5)"))
```
The default source of the code is from clipboard like `tidy_source()`, so we can copy our code to clipboard, and simply run this in R:
```{r eval=FALSE}
library(formatR)
tidy_eval()
# without specifying any arguments, it reads code from clipboard
```
# 5. Showcase
We continue the example code in Section 2, using different arguments in `tidy_source()` such as `arrow`, `blank`, `indent`, `brace.newline` and `comment`, etc.
## Replace `=` with `<-`
```{r example, eval=FALSE, echo=6, tidy.opts=list(arrow=TRUE)}
```
## Discard blank lines
Note the 5th line (an empty line) was discarded:
```{r example, eval=FALSE, echo=1:6, tidy.opts=list(blank = FALSE)}
```
## Reindent code (2 spaces instead of 4)
```{r example, eval=FALSE, echo=6, tidy.opts=list(indent = 2)}
```
## Move left braces `{` to new lines
```{r example, eval=FALSE, echo=6, tidy.opts=list(brace.newline = TRUE)}
```
## Discard comments
```{r example, eval=FALSE, tidy.opts=list(comment = FALSE, width.cutoff = 50)}
```
# 6. Further notes
The tricks used in this packages are very dirty. There might be dangers in using the functions in **formatR**. Please read the next section carefully to know exactly how comments are preserved. The best strategy to avoid failure is to put comments in complete lines or after _complete_ R expressions. Below are some known issues that `tidy_source()` may fail.
## In-line comments after an incomplete expression or ;
```r
1 + 2 + ## comments after an incomplete line
3 + 4
x <- ## this is not a complete expression
5
x <- 1; # you should not use ; here!
```
It is not a good idea to interrupt R code with comments and sometimes it can be confusing -- comments should come after a complete R expression naturally; by the way, `tidy_source()` will move the comments after `{` to the next line, e.g.
```{r comment-brace, tidy=FALSE, eval=FALSE}
if (TRUE) {## comments
}
```
will become
```{r comment-brace, eval=FALSE}
```
## Inappropriate blank lines
Blank lines are often used to separate complete chunks of R code, and arbitrary blank lines may cause failures in `tidy_source()` as well when the argument `blank = TRUE`, e.g.
```r
if (TRUE)
{'this is a BAD style of R programming!'} else 'failure!'
```
There should not be a blank line after the `if` statement. Of course `blank = FALSE` will not fail in this case.
## `?` with comments
We can use the question mark (`?`) to view the help page, but **formatR** package is unable to correctly format the code using `?` with comments, e.g.
```r
?sd # help on sd()
```
In this case, it is recommended to use the function `help()` instead of the short-hand version `?`.
## `->` with comments
We can also use the right arrow `->` for assignment, e.g. `1:10 -> x`. I believe this flexibility is worthless, and it is amazing that a language has three assignment operators: `<-`, `=` and `->` (whereas almost all other languages uses `=` for assignment). Bad news for **formatR** is that it is unable to format code using both `->` and comments in a line, e.g.
```r
1:10 -> x # assignment with right arrow
```
I recommend you to use `<-` or `=` consistently. What is more important is consistency. I always use `=` because it causes me no confusion (I do not believe it is ever possible for people to interpret `fun(a = 1)` as assigning `1` to a variable `a` instead of passing an argument value) and `<-` is more dangerous because it works everywhere (you might have unconsciously created a new variable `a` in `fun(a <- 1)`; see [an example here](https://stat.ethz.ch/pipermail/r-devel/2011-December/062786.html)). The only disadvantage is that most R people use `<-` so it may be difficult to collaborate with other people.
# 7. How does `tidy_source()` actually work?
In a nutshell, `tidy_source(text = code)` is basically `deparse(parse(text = code))`, but actually it is more complicated only because of one thing: `deparse()` drops comments, e.g.,
```{r}
deparse(parse(text = '1+2-3*4/5 # a comment'))
```
The method to preserve comments is to protect them as strings in R expressions. For example, there is a single line of comments in the source code:
```r
# asdf
```
It will be first masked as
```r
invisible(".IDENTIFIER1 # asdf.IDENTIFIER2")
```
which is a legal R expression, so `base::parse()` can deal with it and will no longer remove the disguised comments. In the end the identifiers will be removed to restore the original comments, i.e. the strings `invisible(".IDENTIFIER1` and `.IDENTIFIER2")` are replaced with empty strings.
Inline comments are handled differently: two spaces will be added before the hash symbol `#`, e.g.
```r
1+1# comments
```
will become
```r
1+1 # comments
```
Inline comments are first disguised as a weird operation with its preceding R code, which is essentially meaningless but syntactically correct! For example,
```r
1+1 %InLiNe_IdEnTiFiEr% "# comments"
```
then `base::parse()` will deal with this expression; again, the disguised comments will not be removed. In the end, inline comments will be freed as well (remove the operator `%InLiNe_IdEnTiFiEr%` and surrounding double quotes).
All these special treatments to comments are due to the fact that `base::parse()` and `base::deparse()` can tidy the R code at the price of dropping all the comments.
# 8. Global options
There are global options which can override some arguments in `tidy_source()`:
| argument | global option | default |
|-----------------|------------------------------------|---------|
| `comment` | `options('formatR.comment')` | `TRUE` |
| `blank` | `options('formatR.blank')` | `TRUE` |
| `arrow` | `options('formatR.arrow')` | `FALSE` |
| `indent` | `options('formatR.indent')` | `4` |
| `brace.newline` | `options('formatR.brace.newline')` | `FALSE` |
Also note that single lines of long comments will be wrapped into shorter ones automatically, but roxygen comments will not be wrapped (i.e., comments that begin with `#'`).
formatR/README.md 0000644 0001762 0000144 00000000325 12424254510 013142 0 ustar ligges users # formatR
[](https://travis-ci.org/yihui/formatR)
Format R code automatically.
See the package homepage for more information.
formatR/MD5 0000644 0001762 0000144 00000002711 13077756142 012211 0 ustar ligges users 024f2cbeb1ddc02eafadfbbd8d055c6d *DESCRIPTION
9c9a697c05b9c79da518539296869287 *NAMESPACE
a75140bfe99b0e24ca5368b52eb92db4 *NEWS
92a99a92f27a24371272c2492af6e7af *R/eval.R
bd8040ed7c8739ab1f07e752b6055f93 *R/shiny.R
a9ad6d3b4f19cbea32086df5dee060ea *R/tidy.R
af27b5095fef6708dcd72fb826c4e39e *R/usage.R
f2c30b1c1150b7d9179152f78779069c *R/utils.R
48a0ef6c06871db21331c6029a0feb85 *README.md
5a5c537829c65f2bcb109bb03ac0e36c *build/vignette.rds
a768138833bdf7e02d40f5fccb0595eb *inst/doc/formatR.R
1c87c934eb4b8044893574eb79f245bf *inst/doc/formatR.Rmd
e584a5ce8a6417a04942852eb9617447 *inst/doc/formatR.html
4cc1fd6c70617c2386287e60a8839fd9 *inst/format/messy.R
50c29d050db9b30d8f60aecdbe782aad *inst/shiny/DESCRIPTION
70a6ecc0f945539a7af67027fd0e28a5 *inst/shiny/Readme.md
6eb96b6b5f03c68ea942e1b867cb0990 *inst/shiny/server.R
679928625666b6289769e7bef185407a *inst/shiny/ui.R
52694920c5ee5dae5a0987c7dd441b8c *inst/shiny/www/shiny-handler.js
708a612bea122d78b8e2d92d2a64be10 *man/tidy_app.Rd
2d1134bd87e1fdc0a0c60d5e5f392d92 *man/tidy_dir.Rd
d7098e8daed940a530561a7754e89c4a *man/tidy_eval.Rd
d70898e35c08433a69c0bb7633d4c276 *man/tidy_source.Rd
5fcb479c1c35ebe22825d046d906090d *man/usage.Rd
7bbb218a408b00f834be178333ec0386 *tests/test-all.R
43df87d4e2cd1c3aa1b100df0e688a71 *tests/testit/test-tidy.R
cb1c8fbba3af13ce1d253bacb99824eb *tests/testit/test-usage.R
5ea5bde8eec971aa7a270ed54c2fb751 *tests/testit/test-utils.R
1c87c934eb4b8044893574eb79f245bf *vignettes/formatR.Rmd
formatR/build/ 0000755 0001762 0000144 00000000000 13077671755 013005 5 ustar ligges users formatR/build/vignette.rds 0000644 0001762 0000144 00000000326 13077671755 015345 0 ustar ligges users b```b`f@&0`b fd`aiE%AzA)hRRy
y%E)%y
%
Ph*y`dq- :C,Q ,LH
YsSt楀aMwjey~L6̜T!%psQY_/( @hrNb1GRKҊA 9L formatR/DESCRIPTION 0000644 0001762 0000144 00000002144 13077756142 013407 0 ustar ligges users Package: formatR
Type: Package
Title: Format R Code Automatically
Version: 1.5
Date: 2017-04-26
Authors@R: c(
person("Yihui", "Xie", email = "xie@yihui.name", role = c("aut", "cre")),
person("Eugene", "Ha", role = "ctb"),
person("Kohske", "Takahashi", role = "ctb"),
person("Ed", "Lee", role = "ctb")
)
Maintainer: Yihui Xie
Description: Provides a function tidy_source() to format R source code. Spaces
and indent will be added to the code automatically, and comments will be
preserved under certain conditions, so that R code will be more
human-readable and tidy. There is also a Shiny app as a user interface in
this package (see tidy_app()).
Depends: R (>= 3.0.2)
Suggests: codetools, shiny, testit, rmarkdown, knitr
License: GPL
URL: https://yihui.name/formatR
BugReports: https://github.com/yihui/formatR/issues
VignetteBuilder: knitr
RoxygenNote: 6.0.1
NeedsCompilation: no
Packaged: 2017-04-25 16:06:05 UTC; yihui
Author: Yihui Xie [aut, cre],
Eugene Ha [ctb],
Kohske Takahashi [ctb],
Ed Lee [ctb]
Repository: CRAN
Date/Publication: 2017-04-25 23:31:46 UTC
formatR/man/ 0000755 0001762 0000144 00000000000 13077671653 012456 5 ustar ligges users formatR/man/tidy_app.Rd 0000644 0001762 0000144 00000000652 13077671704 014556 0 ustar ligges users % Generated by roxygen2: do not edit by hand
% Please edit documentation in R/shiny.R
\name{tidy_app}
\alias{tidy_app}
\title{A Shiny app to format R code}
\usage{
tidy_app()
}
\description{
This function calls \code{\link{tidy_source}()} to format R code in a Shiny
app. The arguments of \code{tidy_source()} are presented in the app as input
widgets such as checkboxes.
}
\examples{
if (interactive()) formatR::tidy_app()
}
formatR/man/tidy_source.Rd 0000644 0001762 0000144 00000007632 13077671705 015304 0 ustar ligges users % Generated by roxygen2: do not edit by hand
% Please edit documentation in R/tidy.R
\name{tidy_source}
\alias{tidy_source}
\title{Reformat R code while preserving blank lines and comments}
\usage{
tidy_source(source = "clipboard", comment = getOption("formatR.comment",
TRUE), blank = getOption("formatR.blank", TRUE), arrow = getOption("formatR.arrow",
FALSE), brace.newline = getOption("formatR.brace.newline", FALSE),
indent = getOption("formatR.indent", 4), output = TRUE, text = NULL,
width.cutoff = getOption("width"), ...)
}
\arguments{
\item{source}{a character string: location of the source code (default to be
the clipboard; this means we can copy the code to clipboard and use
\code{tidy_source()} without specifying the argument \code{source})}
\item{comment}{whether to keep comments (\code{TRUE} by default)}
\item{blank}{whether to keep blank lines (\code{TRUE} by default)}
\item{arrow}{whether to replace the assign operator \code{=} with \code{<-}}
\item{brace.newline}{whether to put the left brace \code{\{} to a new line
(default \code{FALSE})}
\item{indent}{number of spaces to indent the code (default 4)}
\item{output}{output to the console or a file using \code{\link{cat}}?}
\item{text}{an alternative way to specify the input: if it is \code{NULL},
the function will read the source code from the \code{source} argument;
alternatively, if \code{text} is a character vector containing the source
code, it will be used as the input and the \code{source} argument will be
ignored}
\item{width.cutoff}{passed to \code{\link{deparse}}: integer in [20, 500]
determining the cutoff at which line-breaking is tried (default to be
\code{getOption("width")})}
\item{...}{other arguments passed to \code{\link{cat}}, e.g. \code{file}
(this can be useful for batch-processing R scripts, e.g.
\code{tidy_source(source = 'input.R', file = 'output.R')})}
}
\value{
A list with components \item{text.tidy}{the reformatted code as a
character vector} \item{text.mask}{the code containing comments, which are
masked in assignments or with the weird operator}
}
\description{
This function returns reformatted source code; it tries to preserve blank
lines and comments, which is different with \code{\link{parse}} and
\code{\link{deparse}}. It can also replace \code{=} with \code{<-} where
\code{=} means assignments, and reindent code by a specified number of spaces
(default is 4).
}
\note{
Be sure to read the reference to know other limitations.
}
\examples{
library(formatR)
## a messy R script
messy = system.file("format", "messy.R", package = "formatR")
tidy_source(messy)
## use the 'text' argument
src = readLines(messy)
## source code
cat(src, sep = "\\n")
## the formatted version
tidy_source(text = src)
## preserve blank lines
tidy_source(text = src, blank = TRUE)
## indent with 2 spaces
tidy_source(text = src, indent = 2)
## discard comments!
tidy_source(text = src, comment = FALSE)
## wanna see the gory truth??
tidy_source(text = src, output = FALSE)$text.mask
## tidy up the source code of image demo
x = file.path(system.file(package = "graphics"), "demo", "image.R")
# to console
tidy_source(x)
# 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)
## 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}"))
}
\references{
\url{https://yihui.name/formatR} (an introduction to this package,
with examples and further notes)
}
\seealso{
\code{\link{parse}}, \code{\link{deparse}}
}
\author{
Yihui Xie <\url{https://yihui.name}> with substantial contribution
from Yixuan Qiu <\url{http://yixuan.cos.name}>
}
formatR/man/tidy_dir.Rd 0000644 0001762 0000144 00000002362 13077671704 014554 0 ustar ligges users % Generated by roxygen2: do not edit by hand
% Please edit documentation in R/tidy.R
\name{tidy_dir}
\alias{tidy_dir}
\alias{tidy_file}
\title{Format all R scripts under a directory, or specified R scripts}
\usage{
tidy_dir(path = ".", recursive = FALSE, ...)
tidy_file(file, ...)
}
\arguments{
\item{path}{the directory}
\item{recursive}{whether to recursively look for R scripts under \code{path}}
\item{...}{other arguments to be passed to \code{\link{tidy_source}}}
\item{file}{a vector of filenames}
}
\value{
Invisible \code{NULL}.
}
\description{
\code{tidy_dir()} first looks for all the R scripts under a directory (using
the pattern \code{"[.][RrSsQq]$"}), then uses \code{\link{tidy_source}} to
tidy these scripts. The original scripts will be overwritten with reformatted
code if reformatting was successful. You may need to back up the original
directory first if you do not fully understand the tricks used by
\code{\link{tidy_source}}. \code{tidy_file()} formats specified R scripts.
}
\examples{
library(formatR)
path = tempdir()
file.copy(system.file("demo", package = "base"), path, recursive = TRUE)
tidy_dir(path, recursive = TRUE)
}
\seealso{
\code{\link{tidy_source}}
}
\author{
Yihui Xie (\code{tidy_dir}) and Ed Lee (\code{tidy_file})
}
formatR/man/usage.Rd 0000644 0001762 0000144 00000003571 13077671705 014055 0 ustar ligges users % Generated by roxygen2: do not edit by hand
% Please edit documentation in R/usage.R
\name{usage}
\alias{usage}
\title{Show the usage of a function}
\usage{
usage(FUN, width = getOption("width"), tidy = TRUE, output = TRUE,
indent.by.FUN = FALSE, fail = c("warn", "stop", "none"))
}
\arguments{
\item{FUN}{the function name}
\item{width}{the width of output}
\item{tidy}{whether to reformat the usage code}
\item{output}{whether to write the output to the console (via
\code{\link{cat}})}
\item{indent.by.FUN}{whether to indent subsequent lines by the width of the
function name (see \dQuote{Details})}
\item{fail}{a character string that determines whether to generate a warning,
stop, or do neither, if the width constraint is unfulfillable (default is
\code{"warn"})}
}
\value{
The R code for the usage is returned as a character string
(invisibly).
}
\description{
Print the reformatted usage of a function. The arguments of the function are
searched by \code{\link{argsAnywhere}}, so the function can be either
exported or non-exported in a package. S3 methods will be marked.
}
\details{
Line breaks in the output occur between arguments. In particular,
default values of arguments will not be split across lines.
When \code{indent.by.FUN} is \code{FALSE}, indentation is set by the option
\code{\link{getOption}("formatR.indent", 4L)}, the default value of the
\code{indent} argument of \code{\link{tidy_source}}.
}
\examples{
library(formatR)
usage(var)
usage(plot)
usage(plot.default) # default method
usage("plot.lm") # on the 'lm' class
usage(usage)
usage(barplot.default, width = 60) # output lines have 60 characters or less
# indent by width of 'barplot('
usage(barplot.default, width = 60, indent.by.FUN = TRUE)
\dontrun{
# a warning is raised because the width constraint is unfulfillable
usage(barplot.default, width = 30)
}
}
\seealso{
\code{\link{tidy_source}}
}
formatR/man/tidy_eval.Rd 0000644 0001762 0000144 00000002511 13077671704 014721 0 ustar ligges users % Generated by roxygen2: do not edit by hand
% Please edit documentation in R/eval.R
\name{tidy_eval}
\alias{tidy_eval}
\title{Evaluate R code and mask the output by a prefix}
\usage{
tidy_eval(source = "clipboard", ..., file = "", prefix = "## ", envir = parent.frame())
}
\arguments{
\item{source}{the input filename (by default the clipboard; see
\code{\link{tidy_source}})}
\item{...}{other arguments passed to \code{\link{tidy_source}}}
\item{file}{the file to write by \code{\link{cat}}; by default the output is
printed on screen}
\item{prefix}{the prefix to mask the output}
\item{envir}{the environment in which to evaluate the code (by default the
parent environment; if we do not want to mess up with the parent
environment, we can set \code{envir = NULL} or \code{envir = new.env()})}
}
\value{
Evaluated R code with corresponding output (printed on screen or
written in a file).
}
\description{
This function is designed to insert the output of each chunk of R code into
the source code without really breaking the source code, since the output is
masked in comments.
}
\examples{
library(formatR)
## evaluate simple code as a character vector
tidy_eval(text = c("a<-1+1;a", "matrix(rnorm(10),5)"))
## evaluate a file
tidy_eval(system.file("format", "messy.R", package = "formatR"))
}
\references{
\url{https://yihui.name/formatR}
}