pax_global_header00006660000000000000000000000064126365165330014524gustar00rootroot0000000000000052 comment=22491d0793e27786a0fb078f1f50cc0a7bd729c7 r-cran-formatr-1.2.1/000077500000000000000000000000001263651653300143575ustar00rootroot00000000000000r-cran-formatr-1.2.1/DESCRIPTION000066400000000000000000000014301263651653300160630ustar00rootroot00000000000000Package: formatR Type: Package Title: Format R Code Automatically Version: 1.2.1 Date: 2015-09-18 Author: Yihui Xie 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, knitr License: GPL URL: http://yihui.name/formatR BugReports: https://github.com/yihui/formatR/issues VignetteBuilder: knitr NeedsCompilation: no Packaged: 2015-09-18 01:50:40 UTC; yihui Repository: CRAN Date/Publication: 2015-09-18 08:58:36 r-cran-formatr-1.2.1/MD5000066400000000000000000000027621263651653300146760ustar00rootroot00000000000000e9fc1eb0c3463c91e61890419d610318 *DESCRIPTION e5fa9424ad24ca15fe64ddfd280aff11 *NAMESPACE 7648958ebba236632fab7d1db6af2f7c *NEWS 47d74c7f82ba9e62cad0d4d11311f482 *R/deprecated.R 75e9708244794c9a812e77e9f1569c00 *R/eval.R bd8040ed7c8739ab1f07e752b6055f93 *R/shiny.R 5a1358c971a563fc0007cc13de9a8681 *R/tidy.R 6ab0e7774ef7e906b2bae95fdbf101a4 *R/usage.R 23123cf0184f2a6a3a96e080fbc20cd8 *R/utils.R 48a0ef6c06871db21331c6029a0feb85 *README.md b47e5bd56914071d9fec174e52633e2c *build/vignette.rds 4bed83752459943de7a2b6a38412d1ff *inst/doc/formatR.R 544e4588ce454fd41d7b86ab2890beae *inst/doc/formatR.Rmd d9608c9d7d9913b7e4d0c8ee1a97d00f *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 1e88d72277cf9c5e01570b90a38e0fb0 *man/deprecated.Rd 1dbbc14cec901589ba269cdf6b2e9e00 *man/tidy_app.Rd ea7e55f2731c1dab183d090dc7ecf48b *man/tidy_dir.Rd 5a72d1fd91e92f2125c1d125098fdb32 *man/tidy_eval.Rd 0752d046be73a69535d24b2c4c57f0ea *man/tidy_source.Rd 11d2914aa45fc15e2f6bc34a0daa3517 *man/usage.Rd 7bbb218a408b00f834be178333ec0386 *tests/test-all.R 9eae310a5937aaa1f058ed100ef31f66 *tests/testit/test-tidy.R 5ea5bde8eec971aa7a270ed54c2fb751 *tests/testit/test-utils.R 544e4588ce454fd41d7b86ab2890beae *vignettes/formatR.Rmd r-cran-formatr-1.2.1/NAMESPACE000066400000000000000000000002331263651653300155740ustar00rootroot00000000000000export(tidy.dir) export(tidy.eval) export(tidy.source) export(tidy_app) export(tidy_dir) export(tidy_eval) export(tidy_source) export(usage) import(utils) r-cran-formatr-1.2.1/NEWS000066400000000000000000000276071263651653300150720ustar00rootroot00000000000000 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) r-cran-formatr-1.2.1/R/000077500000000000000000000000001263651653300145605ustar00rootroot00000000000000r-cran-formatr-1.2.1/R/deprecated.R000066400000000000000000000015031263651653300170020ustar00rootroot00000000000000#' Deprecated functions #' #' These functions have been renamed and deprecated in \pkg{formatR}: #' \code{tidy.source()} (use \code{\link{tidy_source}()}), \code{tidy.dir()} #' (use \code{\link{tidy_dir}()}), and \code{tidy.eval()} (use #' \code{\link{tidy_eval}()}). #' @rdname deprecated #' @keywords internal #' @aliases formatR-deprecated #' @param ... arguments passed from the old functions of the style #' \code{foo.bar()} to the new functions \code{foo_bar()} #' @export tidy.source = function(...) { .Deprecated('tidy_source', package = 'formatR') tidy_source(...) } #' @rdname deprecated #' @export tidy.dir = function(...) { .Deprecated('tidy_dir', package = 'formatR') tidy_dir(...) } #' @rdname deprecated #' @export tidy.eval = function(...) { .Deprecated('tidy_eval', package = 'formatR') tidy_eval(...) } r-cran-formatr-1.2.1/R/eval.R000066400000000000000000000032131263651653300156310ustar00rootroot00000000000000#' 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{http://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(file.path(system.file(package = 'stats'), 'demo', 'nlm.R')) 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) } } } r-cran-formatr-1.2.1/R/shiny.R000066400000000000000000000005651263651653300160430ustar00rootroot00000000000000#' 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')) } r-cran-formatr-1.2.1/R/tidy.R000066400000000000000000000216601263651653300156610ustar00rootroot00000000000000#' 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{http://yihui.name}> with substantial contribution #' from Yixuan Qiu <\url{http://yixuan.cos.name}> #' @seealso \code{\link{parse}}, \code{\link{deparse}} #' @references \url{http://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'), ... ) { # compatibility with formatR <= v0.10 if (is.logical(getOption('keep.comment'))) { warning("The option 'keep.comment' is deprecated; please use 'formatR.comment'") options(formatR.comment = getOption('keep.comment')) } if (is.logical(getOption('keep.blank.line'))) { warning("The option 'keep.blank.line' is deprecated; please use 'formatR.blank'") options(formatR.blank = getOption('keep.blank.line')) } if (is.logical(getOption('replace.assign'))) { warning("The option 'replace.assign' is deprecated; please use 'formatR.arrow'") options(formatR.arrow = getOption('replace.assign')) } if (is.logical(getOption('left.brace.newline'))) { warning("The option 'left.brace.newline' is deprecated; please use 'formatR.brace.newline'") options(formatR.brace.newline = getOption('left.brace.newline')) } if (is.numeric(getOption('reindent.spaces'))) { warning("The option 'reindent.spaces' is deprecated; please use 'formatR.indent'") options(formatR.indent = getOption('reindent.spaces')) } extra = list(...) if (is.logical(extra$keep.comment)) { warning("The argument 'keep.comment' is deprecated; please use 'comment'") comment = extra$keep.comment extra$keep.comment = NULL } if (is.logical(extra$keep.blank.line)) { warning("The argument 'keep.blank.line' is deprecated; please use 'blank'") blank = extra$keep.blank.line extra$keep.blank.line = NULL } if (is.logical(extra$replace.assign)) { warning("The argument 'replace.assign' is deprecated; please use 'arrow'") arrow = extra$replace.assign extra$replace.assign = NULL } if (is.logical(extra$left.brace.newline)) { warning("The argument 'left.brace.newline' is deprecated; please use 'brace.newline'") brace.newline = extra$left.brace.newline extra$left.brace.newline = NULL } if (is.numeric(extra$reindent.spaces)) { warning("The argument 'reindent.spaces' is deprecated; please use 'indent'") indent = extra$reindent.spaces extra$reindent.spaces = NULL } if (is.null(text)) { if (source == 'clipboard' && Sys.info()['sysname'] == 'Darwin') { source = pipe('pbpaste') } } else { source = textConnection(text); on.exit(close(source)) } 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') } 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) do.call(cat, c(list(paste(text.tidy, collapse = '\n'), '\n'), extra)) 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) ## 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', ' else', 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 termined 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 the R scripts under a directory #' #' This function 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 #' \code{\link{tidy_source}} is using. #' @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}} #' @return Invisible \code{NULL}. #' @author Yihui Xie <\url{http://yihui.name}> #' @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, ...) { flist = list.files(path, pattern = '[.][RrSsQq]$', full.names = TRUE, recursive = recursive) for (f in flist) { message('tidying ', f) try(tidy_source(f, file = f, ...)) } } r-cran-formatr-1.2.1/R/usage.R000066400000000000000000000043171263651653300160140ustar00rootroot00000000000000#' 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 (passed to \code{width.cutoff} in #' \code{\link{tidy_source}}) #' @param tidy whether to reformat the usage code #' @param output whether to write the output to the console (via #' \code{\link{cat}}) #' @return The R code for the usage is returned as a character string #' (invisibly). #' @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) # narrower output usage = function(FUN, width = getOption('width'), tidy = TRUE, output = TRUE) { fn = as.character(substitute(FUN)) res = capture.output(do.call(argsAnywhere, list(fn))) if (identical(res, 'NULL')) return() res[1] = substring(res[1], 9) # rm 'function ' in the beginning isS3 = FALSE 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) { cat(res, sep = '\n') return(invisible(res)) } if (width <= 1) { warning("'width' should no longer be specified as a proportion") width = width * getOption("width") } tidy.res = tidy_source(text = res, output = FALSE, width.cutoff = width) if (output) cat(tidy.res$text.tidy, sep = '\n') invisible(tidy.res$text.tidy) } r-cran-formatr-1.2.1/R/utils.R000066400000000000000000000127331263651653300160510ustar00rootroot00000000000000# 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.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 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(\\W|)', 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()) op = options(keep.source = FALSE); on.exit(options(op)) base::parse(text = code, srcfile = NULL) } # copied from highr parse_source = 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) r-cran-formatr-1.2.1/README.md000066400000000000000000000003251263651653300156360ustar00rootroot00000000000000# 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. r-cran-formatr-1.2.1/build/000077500000000000000000000000001263651653300154565ustar00rootroot00000000000000r-cran-formatr-1.2.1/build/vignette.rds000066400000000000000000000003271263651653300200170ustar00rootroot00000000000000b```b`fbb`b2 1# 'N/M, MArS+)O)M.S(WFS3$$7Mn1`  `aBRȚZ% 5/$~hZ8S+`zP԰Aհe ,s\ܠL t7`~΢r=xAA$Gs=ʕXVr7Jr-cran-formatr-1.2.1/inst/000077500000000000000000000000001263651653300153345ustar00rootroot00000000000000r-cran-formatr-1.2.1/inst/doc/000077500000000000000000000000001263651653300161015ustar00rootroot00000000000000r-cran-formatr-1.2.1/inst/doc/formatR.R000066400000000000000000000143211263651653300176370ustar00rootroot00000000000000## ----eval=FALSE---------------------------------------------------------- # install.packages('formatR', repos = 'http://cran.rstudio.com') # #' to install the development version, run # #' install.packages('formatR', repos = 'http://yihui.name/xran') ## ------------------------------------------------------------------------ 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 ## comments 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=70)---------------- # ## 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 ## comments 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=70) # can set arbitrary width here args(glm) ## ----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 ## comments 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 ## comments 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 ## comments 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 ## comments 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)---------------- # ## 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 ## comments 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 # } r-cran-formatr-1.2.1/inst/doc/formatR.Rmd000066400000000000000000000244751263651653300201730ustar00rootroot00000000000000 # An Introduction to formatR ## 1. Installation You can install **formatR** from [CRAN](http://cran.rstudio.com/package=formatR), or [XRAN](http://yihui.name/xran) 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 = 'http://yihui.name/xran') ``` 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**](http://cran.r-project.org/package=formatR) package was designed to reformat [R](http://www.r-project.org) 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 ## comments 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 = 70)` to get: ```{r example, eval=FALSE, tidy.opts=list(width.cutoff=70)} ``` 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=70) # 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 source code before tidying](http://i.imgur.com/lUgtEAb.png) After hitting the `Tidy` button: ![R source code after tidying](http://i.imgur.com/TBZm0B8.png) ## 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)} ``` ## 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? 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 `#'`). ## 9. Additional problems for R 2.15.x You are strongly recommended to upgrade to R 3.0.x; if you use `R < 3.0.0`, you will suffer from a few additional problems below. ### Character strings with # in the beginning of a line ```r mod = "1+1 ## comments here can bring troubles 2+2" ``` We need to write like this: ```r mod = "1+1\n## comments here can bring troubles\n2+2" ## or mod = paste("1+1", "## comments here can bring troubles", "2+2", sep = '\n') ``` ### Double quotes in comments Note when `comment == TRUE`, you _must not write double quotes in the inline comments_ and _must write literal `#` in double quotes_!! For example, ```r 1 + 1 # double quotes in "comment" are not allowed 1 + 1 # here is a legitimate 'comment' with single quotes "a valid character string with # in it" 'a # in single quotes is not allowed' ``` r-cran-formatr-1.2.1/inst/doc/formatR.html000066400000000000000000000416251263651653300204110ustar00rootroot00000000000000 An Introduction to formatR

An Introduction to formatR

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 = 'http://yihui.name/xran')

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.2.2 (2015-08-14)
## Platform: x86_64-pc-linux-gnu (64-bit)
## Running under: Ubuntu 15.04
## 
## locale:
##  [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C              
##  [3] LC_TIME=en_US.UTF-8        LC_COLLATE=C              
##  [5] LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8   
##  [7] LC_PAPER=en_US.UTF-8       LC_NAME=C                 
##  [9] LC_ADDRESS=C               LC_TELEPHONE=C            
## [11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       
## 
## attached base packages:
## [1] stats     graphics  grDevices utils     datasets  methods   base     
## 
## other attached packages:
## [1] formatR_1.2.1
## 
## loaded via a namespace (and not attached):
## [1] magrittr_1.5  tools_3.2.2   stringi_0.5-5 knitr_1.11.1  stringr_1.0.0
## [6] evaluate_0.8

2. Reformat R code

The formatR package was designed to reformat R code to improve readability; the main workhorse is the function tidy_source(). Features include:

  • long lines of code and comments are reorganized into appropriately shorter ones
  • spaces and indent are added where necessary
  • comments are preserved in most cases
  • the number of spaces to indent the code (i.e. tab width) can be specified (default is 4)
  • an else statement in a separate line without the leading } will be moved one line back
  • = as an assignment operator can be replaced with <-
  • the left brace { can be moved to a new line

Below is an example of what tidy_source() can do. The source code is:

## comments are retained;
# a comment block will be reflowed if it contains long comments;
#' roxygen comments will not be wrapped in any 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  ## comments 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 = 70) 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  ## comments after a long line
## here is a long long long long long long long long long long long long
## long comment which will be wrapped

Two applications of tidy_source():

  • tidy_dir() can reformat all R scripts under a directory
  • usage() can reformat the usage of a function, e.g. compare usage() with the default output of args():

    library(formatR)
    usage(glm, width = 70)  # can set arbitrary width here
    ## glm(formula, family = gaussian, data, weights, subset, na.action, start = NULL, 
    ##     etastart, mustart, offset, control = list(...), model = TRUE, method = "glm.fit", 
    ##     x = FALSE, y = TRUE, contrasts = NULL, ...)
    args(glm)
    ## function (formula, family = gaussian, data, weights, subset, 
    ##     na.action, start = NULL, etastart, mustart, offset, control = list(...), 
    ##     model = TRUE, method = "glm.fit", x = FALSE, y = TRUE, contrasts = NULL, 
    ##     ...) 
    ## NULL
    

3. The Graphical User Interface

If the shiny packages has been installed, the function tidy_app() can launch a Shiny app to reformat R code like this (live demo):

formatR::tidy_app()

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?

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 #').

9. Additional problems for R 2.15.x

You are strongly recommended to upgrade to R 3.0.x; if you use R < 3.0.0, you will suffer from a few additional problems below.

Character strings with # in the beginning of a line

mod = "1+1
## comments here can bring troubles
2+2"

We need to write like this:

mod = "1+1\n## comments here can bring troubles\n2+2"
## or
mod = paste("1+1", "## comments here can bring troubles", "2+2", sep = '\n')

Double quotes in comments

Note when comment == TRUE, you must not write double quotes in the inline comments and must write literal # in double quotes!! For example,

1 + 1  # double quotes in "comment" are not allowed
1 + 1  # here is a legitimate 'comment' with single quotes

"a valid character string with # in it"
'a # in single quotes is not allowed'
h
r-cran-formatr-1.2.1/inst/format/000077500000000000000000000000001263651653300166245ustar00rootroot00000000000000r-cran-formatr-1.2.1/inst/format/messy.R000066400000000000000000000010501263651653300201030ustar00rootroot00000000000000 # 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 r-cran-formatr-1.2.1/inst/shiny/000077500000000000000000000000001263651653300164665ustar00rootroot00000000000000r-cran-formatr-1.2.1/inst/shiny/DESCRIPTION000066400000000000000000000002151263651653300201720ustar00rootroot00000000000000Title: Tidy R code using formatR Author: Yihui Xie AuthorUrl: http://yihui.name License: MIT DisplayMode: Showcase Tags: formatR Type: Shiny r-cran-formatr-1.2.1/inst/shiny/Readme.md000066400000000000000000000003631263651653300202070ustar00rootroot00000000000000This 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. r-cran-formatr-1.2.1/inst/shiny/server.R000066400000000000000000000010021263651653300201100ustar00rootroot00000000000000library(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') ) }) }) r-cran-formatr-1.2.1/inst/shiny/ui.R000066400000000000000000000032641263651653300172330ustar00rootroot00000000000000library(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' ) ) ) ) )) r-cran-formatr-1.2.1/inst/shiny/www/000077500000000000000000000000001263651653300173125ustar00rootroot00000000000000r-cran-formatr-1.2.1/inst/shiny/www/shiny-handler.js000066400000000000000000000014001263651653300224100ustar00rootroot00000000000000Shiny.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); }; } ); r-cran-formatr-1.2.1/man/000077500000000000000000000000001263651653300151325ustar00rootroot00000000000000r-cran-formatr-1.2.1/man/deprecated.Rd000066400000000000000000000011701263651653300175200ustar00rootroot00000000000000% Please edit documentation in R/deprecated.R \name{tidy.source} \alias{formatR-deprecated} \alias{tidy.dir} \alias{tidy.eval} \alias{tidy.source} \title{Deprecated functions} \usage{ tidy.source(...) tidy.dir(...) tidy.eval(...) } \arguments{ \item{...}{arguments passed from the old functions of the style \code{foo.bar()} to the new functions \code{foo_bar()}} } \description{ These functions have been renamed and deprecated in \pkg{formatR}: \code{tidy.source()} (use \code{\link{tidy_source}()}), \code{tidy.dir()} (use \code{\link{tidy_dir}()}), and \code{tidy.eval()} (use \code{\link{tidy_eval}()}). } \keyword{internal} r-cran-formatr-1.2.1/man/tidy_app.Rd000066400000000000000000000005751263651653300172410ustar00rootroot00000000000000% 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() } r-cran-formatr-1.2.1/man/tidy_dir.Rd000066400000000000000000000020301263651653300172230ustar00rootroot00000000000000% Please edit documentation in R/tidy.R \name{tidy_dir} \alias{tidy_dir} \title{Format the R scripts under a directory} \usage{ tidy_dir(path = ".", recursive = FALSE, ...) } \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}}} } \value{ Invisible \code{NULL}. } \description{ This function 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 \code{\link{tidy_source}} is using. } \examples{ library(formatR) path = tempdir() file.copy(system.file("demo", package = "base"), path, recursive = TRUE) tidy_dir(path, recursive = TRUE) } \author{ Yihui Xie <\url{http://yihui.name}> } \seealso{ \code{\link{tidy_source}} } r-cran-formatr-1.2.1/man/tidy_eval.Rd000066400000000000000000000024401263651653300174010ustar00rootroot00000000000000% 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(file.path(system.file(package = "stats"), "demo", "nlm.R")) } \references{ \url{http://yihui.name/formatR} } r-cran-formatr-1.2.1/man/tidy_source.Rd000066400000000000000000000075531263651653300177640ustar00rootroot00000000000000% 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}")) } \author{ Yihui Xie <\url{http://yihui.name}> with substantial contribution from Yixuan Qiu <\url{http://yixuan.cos.name}> } \references{ \url{http://yihui.name/formatR} (an introduction to this package, with examples and further notes) } \seealso{ \code{\link{parse}}, \code{\link{deparse}} } r-cran-formatr-1.2.1/man/usage.Rd000066400000000000000000000017531263651653300165330ustar00rootroot00000000000000% 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) } \arguments{ \item{FUN}{the function name} \item{width}{the width of output (passed to \code{width.cutoff} in \code{\link{tidy_source}})} \item{tidy}{whether to reformat the usage code} \item{output}{whether to write the output to the console (via \code{\link{cat}})} } \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. } \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) # narrower output } \seealso{ \code{\link{tidy_source}} } r-cran-formatr-1.2.1/tests/000077500000000000000000000000001263651653300155215ustar00rootroot00000000000000r-cran-formatr-1.2.1/tests/test-all.R000066400000000000000000000000441263651653300173670ustar00rootroot00000000000000library(testit) test_pkg('formatR') r-cran-formatr-1.2.1/tests/testit/000077500000000000000000000000001263651653300170355ustar00rootroot00000000000000r-cran-formatr-1.2.1/tests/testit/test-tidy.R000066400000000000000000000061621263651653300211130ustar00rootroot00000000000000library(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 = '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')) ) r-cran-formatr-1.2.1/tests/testit/test-utils.R000066400000000000000000000015451263651653300213020ustar00rootroot00000000000000library(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}') ) r-cran-formatr-1.2.1/vignettes/000077500000000000000000000000001263651653300163675ustar00rootroot00000000000000r-cran-formatr-1.2.1/vignettes/formatR.Rmd000066400000000000000000000244751263651653300204610ustar00rootroot00000000000000 # An Introduction to formatR ## 1. Installation You can install **formatR** from [CRAN](http://cran.rstudio.com/package=formatR), or [XRAN](http://yihui.name/xran) 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 = 'http://yihui.name/xran') ``` 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**](http://cran.r-project.org/package=formatR) package was designed to reformat [R](http://www.r-project.org) 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 ## comments 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 = 70)` to get: ```{r example, eval=FALSE, tidy.opts=list(width.cutoff=70)} ``` 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=70) # 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 source code before tidying](http://i.imgur.com/lUgtEAb.png) After hitting the `Tidy` button: ![R source code after tidying](http://i.imgur.com/TBZm0B8.png) ## 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)} ``` ## 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? 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 `#'`). ## 9. Additional problems for R 2.15.x You are strongly recommended to upgrade to R 3.0.x; if you use `R < 3.0.0`, you will suffer from a few additional problems below. ### Character strings with # in the beginning of a line ```r mod = "1+1 ## comments here can bring troubles 2+2" ``` We need to write like this: ```r mod = "1+1\n## comments here can bring troubles\n2+2" ## or mod = paste("1+1", "## comments here can bring troubles", "2+2", sep = '\n') ``` ### Double quotes in comments Note when `comment == TRUE`, you _must not write double quotes in the inline comments_ and _must write literal `#` in double quotes_!! For example, ```r 1 + 1 # double quotes in "comment" are not allowed 1 + 1 # here is a legitimate 'comment' with single quotes "a valid character string with # in it" 'a # in single quotes is not allowed' ```