-
Notifications
You must be signed in to change notification settings - Fork 308
Expand file tree
/
Copy pathair.R
More file actions
215 lines (188 loc) · 6.8 KB
/
Copy pathair.R
File metadata and controls
215 lines (188 loc) · 6.8 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
#' Configure a project to use Air
#'
#' @description
#' [Air](https://posit-dev.github.io/air) is an extremely fast R code
#' formatter. This function sets up a project to use Air. Specifically, it:
#'
#' - Creates an empty `air.toml` configuration file. If either an `air.toml` or
#' `.air.toml` file already existed, nothing is changed. If the project is an
#' R package, `.Rbuildignore` is updated to ignore this file.
#'
#' - Creates a `.vscode/` directory and adds recommended settings to
#' `.vscode/settings.json` and `.vscode/extensions.json`. These settings are
#' used by the Air extension installed through either VS Code or Positron, see
#' the Installation section for more details. Specifically it:
#'
#' - Sets `editor.formatOnSave = true` for R and Quarto files to enable
#' formatting on every save.
#'
#' - Sets `editor.defaultFormatter` to Air for R files to ensure that Air is
#' always selected as the formatter for this project. Likewise, sets the
#' default formatter for Quarto.
#'
#' - Sets the Air extension as a "recommended" extension for this project,
#' which triggers a notification for contributors coming to this project
#' that don't yet have the Air extension installed.
#'
#' If the project is an R package, `.Rbuildignore` is updated to ignore the
#' `.vscode/` directory.
#'
#' If you'd like to opt out of VS Code / Positron specific setup, set `vscode
#' = FALSE`, but remember that even if you work in RStudio, other contributors
#' may prefer another editor.
#'
#' Note that `use_air()` does not actually invoke Air, it just configures your
#' project with the recommended settings. Consult [Air's editors
#' guide](https://posit-dev.github.io/air/editors.html) to learn how to invoke
#' Air in your preferred editor.
#'
#' ## Installation
#'
#' Note that this setup does not install an Air binary, so there may be an
#' additional manual step you must take before using Air for the first time:
#'
#' - For RStudio, follow the [installation
#' guide](https://posit-dev.github.io/air/editor-rstudio.html).
#'
#' - For Positron, the [Air extension](https://open-vsx.org/extension/posit/air-vscode)
#' is installed by default and that already includes the Air binary. A typical
#' Positron user does not need to do anything to install Air.
#'
#' - For VS Code, install the [VS Code
#' Extension](https://marketplace.visualstudio.com/items?itemName=Posit.air-vscode).
#'
#' - For other editors, check to [see if that editor is
#' supported](https://posit-dev.github.io/air/editors.html) by Air.
#'
#' @param vscode Either:
#' - `TRUE` to set up VS Code and Positron specific Air settings. This is the
#' default.
#' - `FALSE` to opt out of those settings.
#'
#' @export
#' @examples
#' \dontrun{
#' # Prepare an R package or project to use Air
#' use_air()
#' }
use_air <- function(vscode = TRUE) {
check_bool(vscode)
ignore <- is_package()
# Create empty `air.toml` if it doesn't exist
create_air_toml(ignore = ignore)
if (vscode) {
create_vscode_directory(ignore = ignore)
# Create project level `settings.json` if it doesn't exist,
# and write in Air specific formatter settings
path <- create_vscode_json_file("settings.json")
write_air_vscode_settings_json(path)
# Create project level `extensions.json` if it doesn't exist,
# and write in Air as a recommended extension for this project
path <- create_vscode_json_file("extensions.json")
write_air_vscode_extensions_json(path)
}
ui_bullets(c(
"_" = "Read the {.href [Air editors guide](https://posit-dev.github.io/air/editors.html)}
to learn how to invoke Air in your preferred editor."
))
invisible(TRUE)
}
#' Creates an empty `air.toml`
#'
#' If either `air.toml` or `.air.toml` already exist, no new file is created.
#'
#' @keywords internal
#' @noRd
create_air_toml <- function(ignore = FALSE) {
path <- path_first_existing(proj_path(c("air.toml", ".air.toml")))
if (is.null(path)) {
# No pre-existing configuration file, create it
path <- proj_path("air.toml")
file_create(path)
ui_bullets(c("v" = "Creating {.path {pth(path)}}."))
}
if (ignore) {
use_build_ignore(air_toml_regex(), escape = FALSE)
}
invisible(path)
}
air_toml_regex <- function() {
# Pre-escaped regex allowing both `air.toml` and `.air.toml`
"^[.]?air[.]toml$"
}
create_vscode_json_file <- function(name) {
arg_match(name, values = c("settings.json", "extensions.json"))
path <- proj_path(".vscode", name)
if (!file_exists(path)) {
file_create(path)
ui_bullets(c("v" = "Creating {.path {pth(path)}}."))
}
# Tools like jsonlite fail to read empty json files,
# so if we've just created it, write in `{}`. The easiest
# way to do that is to write an empty named list.
if (is_file_empty(path)) {
jsonlite::write_json(set_names(list()), path = path, pretty = TRUE)
}
invisible(path)
}
write_air_vscode_settings_json <- function(path) {
settings <- jsonlite::read_json(path) %||% set_names(list())
patch <- list(
`[r]` = list(
"editor.formatOnSave" = TRUE,
"editor.defaultFormatter" = "Posit.air-vscode"
),
`[quarto]` = list(
"editor.formatOnSave" = TRUE,
"editor.defaultFormatter" = "quarto.quarto"
)
)
settings <- utils::modifyList(settings, patch)
write_vscode_json(x = settings, path = path)
}
write_air_vscode_extensions_json <- function(path) {
settings <- jsonlite::read_json(path)
settings_recommendations <- settings[["recommendations"]]
if (is.null(settings_recommendations)) {
# Mock it
settings_recommendations <- list()
}
already_recommended <- any(map_lgl(
settings_recommendations,
function(recommendation) {
identical(recommendation, "Posit.air-vscode")
}
))
if (!already_recommended) {
settings_recommendations <- c(
settings_recommendations,
list("Posit.air-vscode")
)
}
settings[["recommendations"]] <- settings_recommendations
write_vscode_json(x = settings, path = path)
}
#' Write JSON to a VS Code settings file
#'
#' @description
#' Small shim to use in place of [jsonlite::write_json()] when writing to
#' `.vscode/settings.json` or `.vscode/extensions.json`.
#'
#' Notably:
#'
#' - 4 space indent, as that is the standard indent level for these files
#'
#' - Auto unbox, because we want `TRUE` to show up as `true` not `[true]`.
#'
#' - Trims newlines from the right hand side after the ending `}`. Unfortunately
#' setting `pretty = 4L` causes the special libyajl formatter to kick in, and
#' that always adds a trailing newline after every `]` or `}`, even the last
#' one, which we don't want.
#'
#' @keywords internal
#' @noRd
write_vscode_json <- function(x, path) {
json <- jsonlite::toJSON(x, pretty = 4L, auto_unbox = TRUE)
json <- base::trimws(json, which = "right")
base::writeLines(json, path, useBytes = TRUE)
}