Skip to content

Commit 0154a18

Browse files
Merge pull request #434 from jonmcalder/style-rnw
- Add support for .Rnw files (#434).
2 parents 016e776 + 6682607 commit 0154a18

40 files changed

+613
-84
lines changed

R/addins.R

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,13 @@ style_active_file <- function() {
2525
)
2626
context <- get_rstudio_context()
2727
if (is_rmd_file(context$path)) {
28-
out <- transform_rmd(context$contents, transformer)
28+
out <- transform_mixed(context$contents, transformer, filetype = "Rmd")
29+
} else if (is_rnw_file(context$path)) {
30+
out <- transform_mixed(context$contents, transformer, filetype = "Rnw")
2931
} else if (is_plain_r_file(context$path) | is_unsaved_file(context$path)) {
3032
out <- try_transform_as_r_file(context, transformer)
3133
} else {
32-
stop("Can only style .R and .Rmd files.", call. = FALSE)
34+
stop("Can only style .R, .Rmd and .Rnw files.", call. = FALSE)
3335
}
3436
rstudioapi::modifyRange(
3537
c(1, 1, length(context$contents) + 1, 1),

R/set-assert-args.R

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,10 @@ set_and_assert_arg_filetype <- function(filetype) {
3838
#' @param lowercase_filetype A vector with file types to check, all lower case.
3939
#' @keywords internal
4040
assert_filetype <- function(lowercase_filetype) {
41-
if (!all(lowercase_filetype %in% c("r", "rmd"))) {
41+
if (!all(lowercase_filetype %in% c("r", "rmd", "rnw"))) {
4242
stop(
4343
"filetype must not contain other values than 'R'",
44-
"or 'Rmd' (case is ignored).",
44+
"'Rmd' or 'Rnw' (case is ignored).",
4545
call. = FALSE
4646
)
4747
}

R/testing.R

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ test_collection <- function(test, sub_test = NULL,
2727

2828
pattern <- paste0(
2929
if (!is.null(sub_test)) paste0("^", sub_test, ".*"),
30-
"in\\.R(?:|md)$"
30+
"in\\.R(?:|md|nw)$"
3131
)
3232

3333
in_names <- list.files(
@@ -66,7 +66,7 @@ test_collection <- function(test, sub_test = NULL,
6666
#' ))
6767
#' @keywords internal
6868
construct_out <- function(in_paths) {
69-
gsub("\\-in([.]R(?:|md))$", "\\-out\\1", in_paths)
69+
gsub("\\-in([.]R(?:|md|nw))$", "\\-out\\1", in_paths)
7070
}
7171

7272
#' Construct paths of a tree object given the paths of *-in.R files

R/transform-code.R

Lines changed: 46 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
#' Transform code from R or Rmd files
1+
#' Transform code from R, Rmd or Rnw files
22
#'
33
#' A wrapper for [enc::transform_lines_enc()] which initiates the styling of
4-
#' either R or Rmd files by passing the relevant transformer function for each
4+
#' either R, Rmd or Rnw files by passing the relevant transformer function for each
55
#' case.
66
#'
77
#' @inheritParams enc::transform_lines_enc
@@ -12,43 +12,49 @@ transform_code <- function(path, fun, verbose = FALSE, ...) {
1212
enc::transform_lines_enc(path, fun = fun, ..., verbose = verbose)
1313
} else if (is_rmd_file(path)) {
1414
enc::transform_lines_enc(path,
15-
fun = partial(transform_rmd, transformer_fun = fun), ...,
15+
fun = partial(transform_mixed, transformer_fun = fun, filetype = "Rmd"), ...,
16+
verbose = verbose
17+
)
18+
} else if (is_rnw_file(path)) {
19+
enc::transform_lines_enc(path,
20+
fun = partial(transform_mixed, transformer_fun = fun, filetype = "Rnw"), ...,
1621
verbose = verbose
1722
)
1823
} else {
19-
stop(path, " is not an R or Rmd file")
24+
stop(path, " is not an R, Rmd or Rnw file")
2025
}
2126
}
2227

23-
#' Transform Rmd contents
28+
#' Transform mixed contents
2429
#'
2530
#' Applies the supplied transformer function to code chunks identified within
26-
#' an Rmd file and recombines the resulting (styled) code chunks with the text
31+
#' an Rmd or Rnw file and recombines the resulting (styled) code chunks with the text
2732
#' chunks.
2833
#'
29-
#' @param lines A character vector of lines from an Rmd file
30-
#' @param transformer_fun A styler transformer function
34+
#' @param transformer_fun A styler transformer function.
35+
#' @inheritParams separate_chunks
3136
#' @importFrom purrr flatten_chr
3237
#' @keywords internal
33-
transform_rmd <- function(lines, transformer_fun) {
34-
chunks <- separate_chunks(lines)
38+
transform_mixed <- function(lines, transformer_fun, filetype) {
39+
chunks <- separate_chunks(lines, filetype)
3540
chunks$r_chunks <- map(chunks$r_chunks, transformer_fun)
3641

3742
map2(chunks$text_chunks, c(chunks$r_chunks, list(character(0))), c) %>%
3843
flatten_chr()
3944
}
4045

41-
42-
#' Separate chunks within Rmd contents
46+
#' Separate chunks within Rmd and Rnw contents
4347
#'
4448
#' Identifies and separates the code and text chunks (the latter includes non-R
45-
#' code) within an Rmd file, and returns these separately.
46-
#' @param lines a character vector of lines from an Rmd file
49+
#' code) within an Rmd or Rnw file, and returns these separately.
50+
#' @param lines A character vector of lines from an Rmd or Rnw file.
51+
#' @param filetype A string indicating the filetype - either 'Rmd' or 'Rnw'.
4752
#' @importFrom purrr map2
4853
#' @importFrom rlang seq2
4954
#' @keywords internal
50-
separate_chunks <- function(lines) {
51-
r_raw_chunks <- identify_r_raw_chunks(lines)
55+
separate_chunks <- function(lines, filetype) {
56+
r_raw_chunks <- identify_raw_chunks(lines, filetype = filetype)
57+
5258
r_chunks <- map2(
5359
r_raw_chunks$starts, r_raw_chunks$ends, ~lines[seq2(.x + 1, .y - 1)]
5460
)
@@ -60,32 +66,39 @@ separate_chunks <- function(lines) {
6066
lst(r_chunks, text_chunks)
6167
}
6268

63-
#' Identifies raw R code chunks
69+
#' Identifies raw Rmd or Rnw code chunks
6470
#'
6571
#' Raw in the sense that these chunks don't contain pure R code, but they
6672
#' contain a header and footer of markdown. Only code chunks that have an engine
6773
#' whose name matches `engine-pattern` are considered as R code.
6874
#' @inheritParams separate_chunks
6975
#' @param engine_pattern A regular expression that must match the engine name.
7076
#' @keywords internal
71-
identify_r_raw_chunks <- function(lines, engine_pattern = get_engine_pattern()) {
72-
pattern <- get_knitr_pattern(lines)
77+
identify_raw_chunks <- function(lines, filetype, engine_pattern = get_engine_pattern()) {
78+
pattern <- get_knitr_pattern(filetype)
7379
if (is.null(pattern$chunk.begin) || is.null(pattern$chunk.end)) {
7480
stop("Unrecognized chunk pattern!", call. = FALSE)
7581
}
76-
chunks <- grep("^[\t >]*```+\\s*", lines, perl = TRUE)
77-
starts <- odd(chunks)
78-
ends <- even(chunks)
82+
83+
if (filetype == "Rmd") {
84+
chunks <- grep("^[\t >]*```+\\s*", lines, perl = TRUE)
85+
starts <- odd(chunks)
86+
ends <- even(chunks)
87+
is_r_code <- grepl(
88+
paste0("^[\t >]*```+\\s*\\{\\s*", engine_pattern, "[\\s\\},]"),
89+
lines[starts],
90+
perl = TRUE
91+
)
92+
} else if (filetype == "Rnw") {
93+
starts <- grep(pattern$chunk.begin, lines, perl = TRUE)
94+
ends <- grep(pattern$chunk.end, lines, perl = TRUE)
95+
is_r_code <- rep(TRUE, length(starts))
96+
}
7997

8098
if (length(starts) != length(ends)) {
8199
stop("Malformed file!", call. = FALSE)
82100
}
83101

84-
is_r_code <- grepl(
85-
paste0("^[\t >]*```+\\s*\\{\\s*", engine_pattern, "[\\s\\},]"),
86-
lines[starts],
87-
perl = TRUE
88-
)
89102
list(starts = starts[is_r_code], ends = ends[is_r_code])
90103
}
91104

@@ -110,6 +123,10 @@ get_engine_pattern <- function() {
110123
#'
111124
#' @inheritParams separate_chunks
112125
#' @keywords internal
113-
get_knitr_pattern <- function(lines) {
114-
knitr::all_patterns[["md"]]
126+
get_knitr_pattern <- function(filetype) {
127+
if(filetype == "Rnw") {
128+
knitr::all_patterns[["rnw"]]
129+
} else if (filetype == "Rmd") {
130+
knitr::all_patterns[["md"]]
131+
}
115132
}

R/ui.R

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ NULL
66
#' Prettify R source code
77
#'
88
#' Performs various substitutions in all `.R` files in a package
9-
#' (code and tests).
9+
#' (code and tests). One can also (optionally) style `.Rmd` and/or
10+
#' `.Rnw` files (vignettes and readme) by changing the `filetype` argument.
1011
#' Carefully examine the results after running this function!
1112
#'
1213
#' @param pkg Path to a (subdirectory of an) R package.
@@ -19,8 +20,8 @@ NULL
1920
#' conveniently constructed via the `style` argument and `...`. See
2021
#' 'Examples'.
2122
#' @param filetype Vector of file extensions indicating which file types should
22-
#' be styled. Case is ignored, and the `.` is optional, e.g. `c(".R", ".Rmd")`
23-
#' or `c("r", "rmd")`.
23+
#' be styled. Case is ignored, and the `.` is optional, e.g. `c(".R", ".Rmd",
24+
#' ".Rnw")` or `c("r", "rmd", "rnw")`.
2425
#' @param exclude_files Character vector with paths to files that should be
2526
#' excluded from styling.
2627
#' @param include_roxygen_examples Whether or not to style code in roxygen
@@ -58,7 +59,7 @@ NULL
5859
#' @family stylers
5960
#' @examples
6061
#' \dontrun{
61-
#'
62+
#'
6263
#' style_pkg(style = tidyverse_style, strict = TRUE)
6364
#' style_pkg(
6465
#' scope = "line_breaks",
@@ -102,6 +103,16 @@ prettify_pkg <- function(transformers,
102103
readme <- dir(pattern = "^readme\\.rmd$", ignore.case = TRUE)
103104
}
104105

106+
if ("\\.rnw" %in% filetype) {
107+
vignette_files <- append(
108+
vignette_files,
109+
dir(
110+
path = "vignettes", pattern = "\\.rnw$",
111+
ignore.case = TRUE, recursive = TRUE, full.names = TRUE
112+
)
113+
)
114+
}
115+
105116
files <- setdiff(c(r_files, vignette_files, readme), exclude_files)
106117
transform_files(files, transformers, include_roxygen_examples)
107118
}
@@ -136,7 +147,8 @@ style_text <- function(text,
136147

137148
#' Prettify arbitrary R code
138149
#'
139-
#' Performs various substitutions in all `.R` files in a directory.
150+
#' Performs various substitutions in all `.R`, `.Rmd` and/or `.Rnw` files
151+
#' in a directory (by default only `.R` files are styled - see `filetype` argument).
140152
#' Carefully examine the results after running this function!
141153
#' @param path Path to a directory with files to transform.
142154
#' @param recursive A logical value indicating whether or not files in subdirectories
@@ -188,7 +200,7 @@ prettify_any <- function(transformers,
188200
)
189201
}
190202

191-
#' Style `.R` and/or `.Rmd` files
203+
#' Style `.R`, `.Rmd` or `.Rnw` files
192204
#'
193205
#' Performs various substitutions in the files specified.
194206
#' Carefully examine the results after running this function!

R/utils.R

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,10 @@ is_rmd_file <- function(path) {
9393
grepl("\\.Rmd$", path, ignore.case = TRUE)
9494
}
9595

96+
is_rnw_file <- function(path) {
97+
grepl("\\.Rnw$", path, ignore.case = TRUE)
98+
}
99+
96100
is_unsaved_file <- function(path) {
97101
path == ""
98102
}

man/get_knitr_pattern.Rd

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

man/identify_r_raw_chunks.Rd renamed to man/identify_raw_chunks.Rd

Lines changed: 8 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

man/prettify_any.Rd

Lines changed: 1 addition & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

man/separate_chunks.Rd

Lines changed: 6 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)