formatR/0000755000176200001440000000000013077756142011700 5ustar liggesusersformatR/inst/0000755000176200001440000000000013077671755012663 5ustar liggesusersformatR/inst/format/0000755000176200001440000000000012304667727014147 5ustar liggesusersformatR/inst/format/messy.R0000644000176200001440000000105012330116606015406 0ustar liggesusers # 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/0000755000176200001440000000000012451070672013777 5ustar liggesusersformatR/inst/shiny/ui.R0000644000176200001440000000326412376735132014551 0ustar liggesuserslibrary(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.R0000644000176200001440000000100212376444004015421 0ustar liggesuserslibrary(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/0000755000176200001440000000000012330245417014620 5ustar liggesusersformatR/inst/shiny/www/shiny-handler.js0000644000176200001440000000140012330114006017703 0ustar liggesusersShiny.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/DESCRIPTION0000644000176200001440000000021512330114727015477 0ustar liggesusersTitle: Tidy R code using formatR Author: Yihui Xie AuthorUrl: http://yihui.name License: MIT DisplayMode: Showcase Tags: formatR Type: Shiny formatR/inst/shiny/Readme.md0000644000176200001440000000036312376444004015520 0ustar liggesusersThis 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/0000755000176200001440000000000013077671755013430 5ustar liggesusersformatR/inst/doc/formatR.R0000644000176200001440000001521413077671751015164 0ustar liggesusers## ----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.html0000644000176200001440000036562013077671755015744 0ustar liggesusers formatR

formatR

Format R code automatically

Yihui Xie

2017-04-25

1. Installation

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:

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 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:

## 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

Two applications of tidy_source():

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()

R source code before tidying

After hitting the Tidy button:

R source code after tidying

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 <- 2
    print("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 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!

Reindent code (2 spaces instead of 4)

if (TRUE) {
  x = 1  # inline comments
} else {
  x = 2
  print("Oh no... ask the right bracket to go away!")
}

Move left braces { to new lines

if (TRUE)
{
    x = 1  # inline comments
} else
{
    x = 2
    print("Oh no... ask the right bracket to go away!")
}

Discard comments

1 + 1
if (TRUE) {
    x = 1
} else {
    x = 2
    print("Oh no... ask the right bracket to go away!")
}
1 * 3
2 + 2 + 2
lm(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.Rmd0000644000176200001440000002472613077671653015516 0ustar liggesusers--- 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/0000755000176200001440000000000012304662033013025 5ustar liggesusersformatR/tests/testit/0000755000176200001440000000000013077173260014350 5ustar liggesusersformatR/tests/testit/test-utils.R0000644000176200001440000000154512235532745016617 0ustar liggesuserslibrary(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.R0000644000176200001440000000711312700142121016403 0ustar liggesuserslibrary(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.R0000644000176200001440000001574113077173260016564 0ustar liggesuserslibrary(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.R0000644000176200001440000000004412235532745014704 0ustar liggesuserslibrary(testit) test_pkg('formatR') formatR/NAMESPACE0000644000176200001440000000024413045521125013101 0ustar liggesusers# 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/NEWS0000644000176200001440000003253213077671664012411 0ustar liggesusers 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/0000755000176200001440000000000013077671653012104 5ustar liggesusersformatR/R/utils.R0000644000176200001440000001570313077201353013357 0ustar liggesusers# 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.R0000644000176200001440000000320713077671653013160 0ustar liggesusers#' 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.R0000644000176200001440000001440713077671653013341 0ustar liggesusersdeparse_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.R0000644000176200001440000001636113077671653013207 0ustar liggesusers#' 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.R0000644000176200001440000000056512376444004013354 0ustar liggesusers#' 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/0000755000176200001440000000000013077671755013716 5ustar liggesusersformatR/vignettes/formatR.Rmd0000644000176200001440000002472613077671653016004 0ustar liggesusers--- 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.md0000644000176200001440000000032512424254510013142 0ustar liggesusers# formatR [![Build Status](https://travis-ci.org/yihui/formatR.svg)](https://travis-ci.org/yihui/formatR) Format R code automatically. See the package homepage for more information. formatR/MD50000644000176200001440000000271113077756142012211 0ustar liggesusers024f2cbeb1ddc02eafadfbbd8d055c6d *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/0000755000176200001440000000000013077671755013005 5ustar liggesusersformatR/build/vignette.rds0000644000176200001440000000032613077671755015345 0ustar liggesusersb```b`f@&0`b fd`aiE%AzA)hRRy y%E)%y % Ph*y`dq-:C,Q,LH YsSt楀aM wjey~L6̜T!%ps QY_/( @hrNb1GRKҊA9LformatR/DESCRIPTION0000644000176200001440000000214413077756142013407 0ustar liggesusersPackage: 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/0000755000176200001440000000000013077671653012456 5ustar liggesusersformatR/man/tidy_app.Rd0000644000176200001440000000065213077671704014556 0ustar liggesusers% 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.Rd0000644000176200001440000000763213077671705015304 0ustar liggesusers% 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.Rd0000644000176200001440000000236213077671704014554 0ustar liggesusers% 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.Rd0000644000176200001440000000357113077671705014055 0ustar liggesusers% 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.Rd0000644000176200001440000000251113077671704014721 0ustar liggesusers% 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} }