-
Notifications
You must be signed in to change notification settings - Fork 71
Expand file tree
/
Copy pathui-styling.R
More file actions
362 lines (351 loc) · 12.7 KB
/
ui-styling.R
File metadata and controls
362 lines (351 loc) · 12.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
#' Prettify R source code
#'
#' Performs various substitutions in all `.R` files in a package
#' (code and tests), `.Rmd`, `.Rmarkdown` and/or
#' `.qmd`, `.Rnw` files (vignettes and readme).
#' Carefully examine the results after running this function!
#'
#' @param pkg Path to a (subdirectory of an) R package.
#' @param ... Arguments passed on to the `style` function,
#' see [tidyverse_style()] for the default argument.
#' @param style A function that creates a style guide to use, by default
#' [`tidyverse_style`]. Not used
#' further except to construct the argument `transformers`. See
#' [style_guides()] for details.
#' @param transformers A set of transformer functions. This argument is most
#' conveniently constructed via the `style` argument and `...`. See
#' 'Examples'.
#' @inheritParams prettify_pkg
#' @section Warning:
#' This function overwrites files (if styling results in a change of the
#' code to be formatted and `dry = "off"`). It is strongly suggested to only
#' style files that are under version control or to create a backup copy.
#'
#' We suggest to first style with `scope < "tokens"` and inspect and commit
#' changes, because these changes are guaranteed to leave the abstract syntax
#' tree (AST) unchanged. See section 'Round trip validation' for details.
#'
#' Then, we suggest to style with `scope = "tokens"` (if desired) and carefully
#' inspect the changes to make sure the AST is not changed in an unexpected way
#' that invalidates code.
#' @section Round trip validation:
#' The following section describes when and how styling is guaranteed to
#' yield correct code.
#'
#' If tokens are not in the styling scope (as specified with the `scope`
#' argument), no tokens are changed and the abstract syntax tree (AST) should
#' not change.
#' Hence, it is possible to validate the styling by comparing whether the parsed
#' expression before and after styling have the same AST.
#' This comparison omits roxygen code examples and comments. styler throws an
#' error if the AST has changed through styling.
#'
#' Note that if tokens are to be styled, such a comparison is not conducted because
#' the AST might well change and such a change is intended. There is no way
#' styler can validate styling, that is why we inform the user to carefully
#' inspect the changes.
#'
#' See section 'Warning' for a good strategy to apply styling safely.
#' @inheritSection transform_files Value
#' @family stylers
#' @examplesIf FALSE
#' # the following is identical (because of ... and defaults)
#' # but the first is most convenient:
#' style_pkg(strict = TRUE)
#' style_pkg(style = tidyverse_style, strict = TRUE)
#' style_pkg(transformers = tidyverse_style(strict = TRUE))
#'
#' # more options from `tidyverse_style()`
#' style_pkg(
#' scope = "line_breaks",
#' math_token_spacing = specify_math_token_spacing(zero = "'+'")
#' )
#'
#' # don't write back and fail if input is not already styled
#' style_pkg("/path/to/pkg/", dry = "fail")
#' @export
style_pkg <- function(pkg = ".",
...,
style = tidyverse_style,
transformers = style(...),
filetype = c("R", "Rprofile", "Rmd", "Rmarkdown", "Rnw", "qmd"),
exclude_files = c("R/RcppExports\\.R", "R/cpp11\\.R", "R/import-standalone.*\\.R"),
exclude_dirs = c("packrat", "renv"),
include_roxygen_examples = TRUE,
base_indention = 0L,
dry = "off") {
pkg_root <- rprojroot::find_package_root_file(path = pkg)
changed <- withr::with_dir(pkg_root, prettify_pkg(
transformers,
filetype, exclude_files, exclude_dirs, include_roxygen_examples,
base_indention,
dry
))
invisible(changed)
}
#' Prettify a package
#'
#' @param filetype Vector of file extensions indicating which file types should
#' be styled. Case is ignored, and the `.` is optional, e.g. `c(".R",".Rmd")`,
#' or `c("r", "rmd")`. Supported values (after standardization) are:
#' "qmd", "r", "rmd", "rmarkdown", "rnw", and "rprofile".
#' Rmarkdown is treated as Rmd.
#' @param exclude_files Character vector with regular expressions to files
#' that should be excluded from styling.
#' @param exclude_dirs Character vector with directories to exclude
#' (recursively). Note that the default values were set for consistency with
#' [style_dir()] and as these directories are anyways not styled.
#' @inheritParams transform_files
#' @keywords internal
prettify_pkg <- function(transformers,
filetype,
exclude_files,
exclude_dirs,
include_roxygen_examples,
base_indention,
dry) {
filetype_ <- set_and_assert_arg_filetype(filetype)
r_files <- rprofile_files <- vignette_files <- readme <- NULL
all_files <- list.files(".", recursive = TRUE, all.files = TRUE)
exclude_files <- grep(paste(exclude_files, collapse = "|"), all_files, value = TRUE)
exclude_files <- set_arg_paths(exclude_files)
exclude_files <- c(
exclude_files,
dir_without_.(exclude_dirs, pattern = map_filetype_to_pattern(filetype))
)
if ("\\.r" %in% filetype_) {
r_files <- dir_without_.(
path = c("R", "tests", "data-raw", "demo"),
pattern = "\\.r$"
)
}
if ("\\.rprofile" %in% filetype_) {
rprofile_files <- dir_without_.(
path = ".", pattern = "^\\.rprofile$"
)
}
if ("\\.rmd" %in% filetype_) {
vignette_files <- dir_without_.(
path = "vignettes", pattern = "\\.rmd$"
)
readme <- dir_without_.(
path = ".",
pattern = "^readme\\.rmd$"
)
}
if ("\\.rmarkdown" %in% filetype_) {
vignette_files <- append(
vignette_files,
dir_without_.(
path = "vignettes", pattern = "\\.rmarkdown$"
)
)
readme <- append(
readme,
dir_without_.(
path = ".", pattern = "^readme\\.rmarkdown$"
)
)
}
if ("\\.rnw" %in% filetype_) {
vignette_files <- append(
vignette_files,
dir_without_.(
path = "vignettes", pattern = "\\.rnw$"
)
)
}
if ("\\.qmd" %in% filetype_) {
vignette_files <- append(
vignette_files,
dir_without_.(
path = ".",
pattern = "\\.qmd$"
)
)
}
files <- setdiff(
c(r_files, rprofile_files, vignette_files, readme),
exclude_files
)
transform_files(files,
transformers = transformers,
include_roxygen_examples = include_roxygen_examples,
base_indention = base_indention,
dry = dry
)
}
#' Style a string
#'
#' Styles a character vector. Each element of the character vector corresponds
#' to one line of code.
#' @param text A character vector with text to style.
#' @inheritParams style_pkg
#' @family stylers
#' @examples
#' style_text("call( 1)")
#' style_text("1 + 1", strict = FALSE)
#'
#' # the following is identical (because of ... and defaults)
#' # but the first is most convenient:
#' style_text("a<-3++1", strict = TRUE)
#' style_text("a<-3++1", style = tidyverse_style, strict = TRUE)
#' style_text("a<-3++1", transformers = tidyverse_style(strict = TRUE))
#'
#' # more invasive scopes include less invasive scopes by default
#' style_text("a%>%b", scope = "spaces")
#' style_text("a%>%b; a", scope = "line_breaks")
#' style_text("a%>%b; a", scope = "tokens")
#'
#' # opt out with I() to only style specific levels
#' style_text("a%>%b; a", scope = I("tokens"))
#' @export
style_text <- function(text,
...,
style = tidyverse_style,
transformers = style(...),
include_roxygen_examples = TRUE,
base_indention = 0L) {
transformer <- make_transformer(transformers,
include_roxygen_examples = include_roxygen_examples,
base_indention = base_indention
)
styled_text <- transformer(text)
construct_vertical(styled_text)
}
#' Prettify arbitrary R code
#'
#' Performs various substitutions in all `.R`, `.Rmd`, `.Rmarkdown`, `qmd`
#' and/or `.Rnw` files in a directory (by default only `.R` files are styled -
#' see `filetype` argument).
#' Carefully examine the results after running this function!
#' @param path Path to a directory with files to transform.
#' @param recursive A logical value indicating whether or not files in
#' sub directories of `path` should be styled as well.
#' @param exclude_dirs Character vector with directories to exclude
#' (recursively).
##' @inheritParams style_pkg
#' @inheritSection transform_files Value
#' @inheritSection style_pkg Warning
#' @inheritSection style_pkg Round trip validation
#' @family stylers
#' @examplesIf FALSE
#' style_dir("path/to/dir", filetype = c("rmd", ".R"))
#'
#' # the following is identical (because of ... and defaults)
#' # but the first is most convenient:
#' style_dir(strict = TRUE)
#' style_dir(style = tidyverse_style, strict = TRUE)
#' style_dir(transformers = tidyverse_style(strict = TRUE))
#' @export
style_dir <- function(path = ".",
...,
style = tidyverse_style,
transformers = style(...),
filetype = c("R", "Rprofile", "Rmd", "Rmarkdown", "Rnw", "Qmd"),
recursive = TRUE,
exclude_files = NULL,
exclude_dirs = c("packrat", "renv"),
include_roxygen_examples = TRUE,
base_indention = 0L,
dry = "off") {
changed <- withr::with_dir(
path, prettify_any(
transformers,
filetype, recursive, exclude_files, exclude_dirs,
include_roxygen_examples, base_indention, dry
)
)
invisible(changed)
}
# nolint: start
#' Prettify R code in current working directory
#'
#' This is a helper function for style_dir.
#' @inheritParams style_pkg
#' @param recursive A logical value indicating whether or not files in
#' subdirectories should be styled as well.
#' @keywords internal
prettify_any <- function(transformers,
filetype,
recursive,
exclude_files,
exclude_dirs,
include_roxygen_examples,
base_indention = 0L,
dry) {
exclude_files <- set_arg_paths(exclude_files)
exclude_dirs <- exclude_dirs %>%
list.dirs(recursive = TRUE, full.names = TRUE) %>%
set_arg_paths()
files_root <- dir(
path = ".", pattern = map_filetype_to_pattern(filetype),
ignore.case = TRUE, recursive = FALSE, all.files = TRUE
)
if (recursive) {
files_other <- list.dirs(full.names = FALSE, recursive = TRUE) %>%
setdiff(c("", exclude_dirs)) %>%
dir_without_.(
pattern = map_filetype_to_pattern(filetype),
recursive = FALSE
)
} else {
files_other <- NULL
}
transform_files(
setdiff(c(files_root, files_other), exclude_files),
transformers,
include_roxygen_examples,
base_indention,
dry
)
}
# nolint: end
#' Style files with R source code
#'
#' Performs various substitutions in the files specified.
#' Carefully examine the results after running this function!
#' @section Encoding:
#' UTF-8 encoding is assumed. Please convert your code to UTF-8 if necessary
#' before applying styler.
#' @param path A character vector with paths to files to style. Supported
#' extensions: `.R`, `.Rmd`, `.Rmarkdown`, `.qmd` and `.Rnw`.
#' @inheritParams style_pkg
#' @inheritSection transform_files Value
#' @inheritSection style_pkg Warning
#' @inheritSection style_pkg Round trip validation
#' @examples
#' file <- tempfile("styler", fileext = ".R")
#' writeLines("1++1", file)
#'
#' # the following is identical (because of ... and defaults),
#' # but the first is most convenient:
#' style_file(file, strict = TRUE)
#' style_file(file, style = tidyverse_style, strict = TRUE)
#' style_file(file, transformers = tidyverse_style(strict = TRUE))
#'
#' # only style indention and less invasive levels (i.e. spaces)
#' style_file(file, scope = "indention", strict = TRUE)
#' # name levels explicitly to not style less invasive levels
#' style_file(file, scope = I(c("tokens", "spaces")), strict = TRUE)
#'
#' readLines(file)
#' unlink(file)
#' @family stylers
#' @export
style_file <- function(path,
...,
style = tidyverse_style,
transformers = style(...),
include_roxygen_examples = TRUE,
base_indention = 0L,
dry = "off") {
path <- set_arg_paths(path)
changed <- transform_files(path,
transformers = transformers,
include_roxygen_examples = include_roxygen_examples,
base_indention = base_indention,
dry = dry
)
invisible(changed)
}