-
Notifications
You must be signed in to change notification settings - Fork 0
/
README.Rmd
256 lines (177 loc) · 11.5 KB
/
README.Rmd
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
---
output: pal::gitlab_document
---
```{r}
#| label: init
#| include: false
knitr::opts_knit$set(root.dir = getwd())
```
# `r pal::desc_value("Package")`
```{r}
#| label: pkg-desc
#| child: !expr pkgsnip::snip_path("pkg-desc.Rmd")
```
The basic idea behind the concept this package implements originates from Yihui Xie. See his blog post [*Write An R Package Using Literate Programming
Techniques*](https://yihui.org/rlp/) for more details, it's definitively worth reading. This package's function
[`pkgpurl::purl_rmd()`](https://pkgpurl.rpkg.dev/dev/reference/purl_rmd.html) is just a less cumbersome alternative to the Makefile approach outlined by him.
## Pros and cons
The [R Markdown](https://rmarkdown.rstudio.com/) format provides several **advantages** over the bare R source format when developing an R package:
<details>
<summary>👍 <strong>Mix Markdown and Code</strong></summary>
It allows the actual code to be freely mixed with explanatory and supplementary information in expressive [Markdown](https://en.wikipedia.org/wiki/Markdown)
format instead of having to rely on [`#` comments](https://cran.r-project.org/doc/manuals/r-release/R-lang.html#Comments) only. In general, this should
encourage to actually record code-accompanying information because you're able to use the full spectrum of [Pandoc's Markdown
syntax](https://pandoc.org/MANUAL.html#pandocs-markdown) like inline formatting, lists, tables, quotations or math[^1].
It is especially powerful in combination with the [Visual R Markdown](https://rstudio.github.io/visual-markdown-editing/) feature introduced in RStudio 1.4,
which -- in addition to the visual editor -- offers a feature whose utility can hardly be overestimated: Pandoc Markdown
[canonicalization](https://en.wikipedia.org/wiki/Canonicalization) (on file save[^2]). For example, it allows paragraphs being wrapped automatically at the
desired line width; or to write a minimal sloppy [pipe table](https://pandoc.org/MANUAL.html#extension-pipe_tables) that is automatically normalized to a
beautifully formatted and actually readable one.
The relevant editor options which adjust the canonical Markdown generation can either be set
- [per `.Rmd` file](https://rstudio.github.io/visual-markdown-editing/#/markdown?id=writer-options), e.g.
``` rmd
---
editor_options:
markdown:
wrap: 160
references:
location: section
canonical: true
---
```
- or [per project](https://rstudio.github.io/visual-markdown-editing/#/options?id=project-options) in the usual `PACKAGE_NAME.Rproj` file, e.g.
``` ini
MarkdownWrap: Column
MarkdownWrapAtColumn: 160
MarkdownReferences: Section
MarkdownCanonical: Yes
```
(I'd recommend to set them *per project*, so they apply to the whole package including any `.Rmd` vignettes.)
</details>
<details>
<summary>👍 <strong>All your code in a single, well-structured file</strong></summary>
The [traditional recommendation](https://style.tidyverse.org/package-files.html) to not lose overview of your package's R source code is to split it over
multiple files. The popular (and very useful) book *R Packages* [gives the following advice](https://r-pkgs.org/code.html#sec-code-organising):
> If it's very hard to predict which file a function lives in, that suggests it's time to separate your functions into more files or reconsider how you are
> naming your functions and/or files.
I think this is just ridiculous.
Instead, I encourage you to keep all your code (as far as possible) in a single file `Rmd/PACKAGE_NAME.Rmd` and structure it according to the [rules described
here](https://pkgpurl.rpkg.dev/dev/reference/gen_pkgdown_ref.html#details), which even allows the [pkgdown `Reference:`
index](https://pkgdown.r-lib.org/reference/build_reference.html#reference-index) to be automatically in sync with the source code structure. As a result, you
re-organize (and thus most likely improve) your package's code structure whenever you intend to improve the pkgdown reference -- and vice versa. For a basic
example, see [this very package's main source file](https://gitlab.com/rpkg.dev/pkgpurl/-/blob/master/Rmd/pkgpurl.Rmd?plain=0).
Keeping all code in a single file frees you from the traditional hassle of finding a viable (but in the end still unsatisfactory) way to organize your R source
code across multiple files. Of course, there are still good reasons to outsource code into separate files *in certain situations*, which nothing is stopping you
from doing. You can also [exclude whole `.Rmd` files from purling using the `.nopurl.Rmd` filename
suffix](https://pkgpurl.rpkg.dev/dev/reference/purl_rmd.html#-rmd-files-excluded-from-purling).
</details>
<details>
<summary>👍 <strong>Improved overview and navigation</strong></summary>
You can rely on RStudio's [code outline](https://rviews.rstudio.com/2016/11/11/easy-tricks-you-mightve-missed/#code-outline) to easily navigate through longer
`.Rmd` files. IMHO it provides significantly better usability than the [code section
standard](https://support.posit.co/hc/en-us/articles/200484568-Code-Folding-and-Sections) of `.R` files. It makes it easy to find your way around source files
that are thousands of lines long.
RStudio's [*Go to File/Function* shortcut](https://support.posit.co/hc/en-us/articles/200711853-Keyboard-Shortcuts) works the same for `.Rmd` files as it does
for `.R` files.
</details>
<details>
<summary>👍 <strong>Improved visual clarity</strong></summary>
If you use RStudio or any other editor with proper R Markdown syntax highlighting, you will probably like the gained visual clarity for distinguishing
individual functions/code parts (by putting them in separate R code chunks). This also facilitates creating a meaningful document structure (in Markdown)
alongside the actual R source code.
</details>
<details>
<summary>👍 <strong>Easily toggle code inclusion</strong></summary>
You can put development-only code which never lands in the generated R source files (and thus the R package) in separate code chunks with the chunk option
[`purl = FALSE`](https://yihui.org/knitr/options/#extracting-source-code). This turns out to be very convenient in certain situations.
For example, this is a good way to reproducibly document the generation of cleaned versions of [exported data](https://r-pkgs.org/data.html#sec-data-data) as
well as [internal data](https://r-pkgs.org/data.html#sec-data-sysdata). This avoids having to outsource the code to separate files under `data-raw/` and adding
the directory to `.Rbuildignore`, i.e. no need to use `usethis::use_data_raw()`. Instead, you just set `purl = FALSE` for the relevant code chunk(s). You can
(and should) still use [`usethis::use_data()`](https://usethis.r-lib.org/reference/use_data.html) (optionally with `overwrite = TRUE`) to generate the files
under `data/` holding external package data as well as the `R/sysdata.rda` file (using `internal = TRUE`) holding internal package data.
</details>
<details>
<summary>👍 <strong>Easily toggle styler</strong></summary>
If you use [styler](https://styler.r-lib.org/) to auto-format your code globally by [setting
`knitr::opts_chunk$set(tidy = "styler")`](https://styler.r-lib.org/articles/third-party-integrations.html), you can still opt-out on a per-chunk basis by
setting [`tidy = FALSE`](https://github.com/r-lib/styler/releases/tag/v1.5.1). This gives pleasant flexibility.
</details>
Unfortunately, there are also a few notable **drawbacks** of the R Markdown format:
<details>
<summary>👎 <strong>Additional workflow step</strong></summary>
The pkgpurl approach on writing R packages in the R Markdown format introduces *one* additional step at the very beginning of typical package development
workflows: Running [`pkgpurl::purl_rmd()`](https://pkgpurl.rpkg.dev/dev/reference/purl_rmd.html) to generate the `R/*.gen.R` files from the original `Rmd/*.Rmd`
sources before documenting/checking/testing/building the package. Given sufficient user demand, this could probably be integrated into
[devtools](https://devtools.r-lib.org/)' functions in the future, so that no additional action has to be taken by the user when relying on RStudio's built-in
package building infrastructure.
For the time being, it's recommended to set up a custom shortcut[^3] for one or both of
[`pkgpurl::purl_rmd()`](https://pkgpurl.rpkg.dev/dev/reference/purl_rmd.html) and
[`pkgpurl::process_pkg()`](https://pkgpurl.rpkg.dev/dev/reference/process_pkg.html) which are registered as [RStudio
add-ins](https://rstudio.github.io/rstudioaddins/).
</details>
<details>
<summary>👎 <strong>Differing setup</strong></summary>
Setting up a new project to write an R package in the R Markdown differs slightly from the classic approach. A suitable convenience function like
`create_rmd_package()` to set up all the necessary parts could probably be added to [usethis](https://usethis.r-lib.org/) in the future.
For the time being, you can use my ready-to-go [R Markdown Package Development Template](https://gitlab.com/salim_b/r/pkg-dev-tpl) as a starting point for
creating new R packages in the R Markdown format.
</details>
<details>
<summary>👎 <strong>Unwieldy debugging</strong></summary>
Debugging can be a bit more laborious since line numbers in warning and error messages always refer to the generated `R/*.gen.R` file(s), not the underlying
`Rmd/*.Rmd` source code file(s). If need be, you first have to look up the line numbers in the `R/*.gen.R` file(s) to understand which function / code parts
cause the issue in order to know where to fix it in the `Rmd/*.Rmd` source(s).
</details>
<details>
<summary>👎 <strong>Missing roxygen2 auto-completion</strong></summary>
Other than in `.R` files, RStudio currently doesn't support auto-completion of [roxygen2 tags](https://roxygen2.r-lib.org/articles/rd.html) in `.Rmd` files and
its <kbd>Reflow Comment</kbd> command doesn't properly work on them. These are [known
issues](https://github.com/rstudio/rstudio/issues/5809#issuecomment-932228146) which will hopefully be resolved in the near future.
</details>
```{r}
#| label: pkg-doc
#| eval: !expr '!isTRUE(getOption("pal.build_readme.is_pkgdown"))'
#| results: asis
#| echo: false
pkgsnip::md_snip(id = "pkgdown_site") %>%
paste0("## Documentation\n\n",
"[![Netlify Status](https://api.netlify.com/api/v1/badges/f639cb7f-f5fe-454c-b20d-1e06916daffc/deploy-status)]",
"(https://app.netlify.com/sites/pkgpurl-rpkg-dev/deploys)\n\n",
.) %>%
pal::cat_lines()
```
[^1]: Actually, you could write anything you like in any syntax outside of R code chunks as long as you don't mind the file to be *knittable* (which it doesn't
have to be).
[^2]: It basically sends the (R) Markdown file on a "Pandoc round trip" on every file save.
[^3]: I personally recommend to use the shortcut <kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>V</kbd> since it's not occupied yet by any of the predefined [RStudio
shortcuts](https://support.posit.co/hc/en-us/articles/200711853-Keyboard-Shortcuts).
## Installation
```{r}
#| label: pkg-instl-dev
#| child: !expr pkgsnip::snip_path("pkg-instl-dev-gitlab.Rmd")
```
```{r}
#| label: pkg-usage
#| eval: !expr isTRUE(getOption("pal.build_readme.is_pkgdown"))
#| results: asis
#| echo: false
pkgsnip::md_snip(id = "pkg_usage") %>%
paste0("## Usage\n\n", .) %>%
pal::cat_lines()
```
## Package configuration
```{r}
#| label: pkg-config
#| child: !expr pkgsnip::snip_path("pkg-config.Rmd")
```
## Development
### R Markdown format
```{r}
#| label: pkgpurl
#| child: !expr pkgsnip::snip_path("pkgpurl.Rmd")
```
### Coding style
```{r}
#| label: pkg-code-style
#| child: !expr pkgsnip::snip_path("pkg-code-style.Rmd")
```