diff --git a/.github/workflows/bookdown.yaml b/.github/workflows/quarto.yaml similarity index 55% rename from .github/workflows/bookdown.yaml rename to .github/workflows/quarto.yaml index 74d13ee..6c9603e 100644 --- a/.github/workflows/bookdown.yaml +++ b/.github/workflows/quarto.yaml @@ -2,13 +2,12 @@ on: push: branches: [main, master] pull_request: - branches: [main, master] workflow_dispatch: -name: bookdown +name: quarto.yml jobs: - build: + quarto: runs-on: ubuntu-latest env: GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} @@ -21,19 +20,11 @@ jobs: - uses: r-lib/actions/setup-r-dependencies@v2 - - uses: r-lib/actions/setup-pandoc@v2 - - - name: Cache bookdown results - uses: actions/cache@v2 - with: - path: _bookdown_files - key: bookdown-${{ hashFiles('**/*Rmd') }} - restore-keys: bookdown- + - name: Install Quarto + uses: quarto-dev/quarto-actions/install-quarto@v1 - - name: Build site - run: | - bookdown::render_book('index.Rmd', 'bookdown::bs4_book', quiet = TRUE) - shell: Rscript {0} + - name: Render book + run: quarto render - name: Deploy to GitHub pages 🚀 if: github.event_name != 'pull_request' diff --git a/_bookdown.yml b/_bookdown.yml deleted file mode 100755 index 9383749..0000000 --- a/_bookdown.yml +++ /dev/null @@ -1,24 +0,0 @@ -output_dir: "_book" - -delete_merged_file: true - -rmd_files: [ - "index.Rmd", - - "analyses.Rmd", - "files.Rmd", - "syntax.Rmd", - "functions.Rmd", - "pipes.Rmd", - "ggplot2.Rmd", - - "packages.Rmd", - "package-files.Rmd", - "documentation.Rmd", - "tests.Rmd", - "errors.Rmd", - "news.Rmd", - - "git.Rmd", - -] diff --git a/_quarto.yml b/_quarto.yml new file mode 100644 index 0000000..ab22e93 --- /dev/null +++ b/_quarto.yml @@ -0,0 +1,45 @@ +project: + type: book + +book: + title: Tidyverse style guide + author: The tidyverse team + + site-url: https://style.tidyverse.org + repo-url: https://github.com/tidyverse/style/ + repo-branch: main + repo-actions: [edit, issue] + + chapters: + - index.qmd + + - part: Analyses + chapters: + - files.qmd + - syntax.qmd + - functions.qmd + - pipes.qmd + - ggplot2.qmd + + - part: Packages + chapters: + - package-files.qmd + - documentation.qmd + - tests.qmd + - errors.qmd + - news.qmd + + - part: Other + chapters: + - git.qmd + +format: + html: + include-in-header: "plausible.html" + callout-appearance: simple + theme: + - styles.scss + +knitr: + opts_chunk: + eval: false diff --git a/analyses.Rmd b/analyses.Rmd deleted file mode 100644 index 8ce8ade..0000000 --- a/analyses.Rmd +++ /dev/null @@ -1 +0,0 @@ -# (PART) Analyses {-} diff --git a/documentation.Rmd b/documentation.qmd similarity index 98% rename from documentation.Rmd rename to documentation.qmd index b03689f..b1d181d 100644 --- a/documentation.Rmd +++ b/documentation.qmd @@ -1,4 +1,4 @@ -# Documentation +# Documentation {#sec-documentation} ## Introduction @@ -7,10 +7,6 @@ is future-you. Use [roxygen2](https://github.com/klutometis/roxygen) with [markdown](https://github.com/klutometis/roxygen/blob/master/vignettes/markdown.Rmd) support enabled to keep your documentation close to the code. -```{r, include=FALSE} -knitr::opts_chunk$set(eval = FALSE) -``` - ## Title and description Use the first line of your function documentation to provide a concise title that describes the function, dataset, or class. Titles should use sentence case diff --git a/errors.Rmd b/errors.qmd similarity index 99% rename from errors.Rmd rename to errors.qmd index 91e1f87..b701a2f 100644 --- a/errors.Rmd +++ b/errors.qmd @@ -1,9 +1,5 @@ # Error messages -```{r, include = FALSE} -knitr::opts_chunk$set(eval = FALSE) -``` - An error message should start with a general statement of the problem then give a concise description of what went wrong. Consistent use of punctuation and formatting makes errors easier to parse. diff --git a/files.Rmd b/files.qmd similarity index 98% rename from files.Rmd rename to files.qmd index 9b6f8de..ba03cac 100755 --- a/files.Rmd +++ b/files.qmd @@ -1,4 +1,4 @@ -# Files +# Files {#sec-files} ## Names @@ -43,7 +43,7 @@ It's hard to describe exactly how you should organise your code across multiple Use commented lines of `-` and `=` to break up your file into easily readable chunks. -```{r, eval = FALSE} +```{r} # Load data --------------------------- # Plot data --------------------------- diff --git a/functions.Rmd b/functions.qmd similarity index 95% rename from functions.Rmd rename to functions.qmd index ee2fb71..1d57fd0 100755 --- a/functions.Rmd +++ b/functions.qmd @@ -4,7 +4,7 @@ As well as following the general advice for [object names], strive to use verbs for function names: -```{r eval = FALSE} +```{r} # Good add_row() permute() @@ -62,7 +62,7 @@ There are two options if the function name and definition can't fit on a single * **Single-indent**: indent the argument name with a single indent (i.e. two spaces). The trailing `)` and leading `{` go on a new line. - ```{r, eval = FALSE} + ```{r} # Good long_function_name <- function( a = "a long argument", @@ -76,7 +76,7 @@ There are two options if the function name and definition can't fit on a single * **Hanging-indent**: indent the argument name to match the opening `(` of `function`. The trailing `)` and leading `{` go on the same line as the last argument. - ```{r, eval = FALSE} + ```{r} # Good long_function_name <- function(a = "a long argument", b = "another argument", @@ -87,7 +87,7 @@ There are two options if the function name and definition can't fit on a single These styles are designed to clearly separate the function definition from its body. -```{r, eval = FALSE} +```{r} # Bad long_function_name <- function(a = "a long argument", b = "another argument", @@ -103,7 +103,7 @@ If a function argument can't fit on a single line, this is a sign you should rew In S7, the method definition can be long because the function name is replaced by a method call that specifies the generic and dispatch classes. In this case we recommend the single-indent style. -```{r, eval = FALSE} +```{r} method(from_provider, list(openai_provider, class_any)) <- function( provider, x, @@ -117,7 +117,7 @@ method(from_provider, list(openai_provider, class_any)) <- function( If the method definition is too long to fit on one line, use the usual rules to spread the method arguments across multiple lines: -```{r, eval = FALSE} +```{r} method( from_provider, list(openai_provider, class_any, a_very_long_class_name) @@ -136,7 +136,7 @@ method( Only use `return()` for early returns. Otherwise, rely on R to return the result of the last evaluated expression. -```{r eval = FALSE} +```{r} # Good find_abs <- function(x) { if (x > 0) { @@ -156,7 +156,7 @@ add_two <- function(x, y) { Return statements should always be on their own line because they have important effects on the control flow. See also [inline statements](#inline-statements). -```{r, eval = FALSE} +```{r} # Good find_abs <- function(x) { if (x > 0) { @@ -177,7 +177,7 @@ plotting, or saving to disk), it should return the first argument invisibly. This makes it possible to use the function as part of a pipe. `print` methods should usually do this, like this example from [httr](http://httr.r-lib.org/): -```{r eval = FALSE} +```{r} print.url <- function(x, ...) { cat("Url: ", build_url(x), "\n", sep = "") invisible(x) @@ -189,7 +189,7 @@ print.url <- function(x, ...) { In code, use comments to explain the "why" not the "what" or "how". Each line of a comment should begin with the comment symbol and a single space: `# `. -```{r, eval = FALSE} +```{r} # Good # Objects like data frames are treated as leaves @@ -205,7 +205,7 @@ x <- map_if(x, is_bare_list, recurse) Comments should be in sentence case, and only end with a full stop if they contain at least two sentences: -```{r, eval = FALSE} +```{r} # Good # Objects like data frames are treated as leaves diff --git a/ggplot2.Rmd b/ggplot2.qmd similarity index 96% rename from ggplot2.Rmd rename to ggplot2.qmd index f3ab8b5..e2d577f 100755 --- a/ggplot2.Rmd +++ b/ggplot2.qmd @@ -12,7 +12,7 @@ Styling suggestions for `+` used to separate ggplot2 layers are very similar to If you are creating a ggplot off of a dplyr pipeline, there should only be one level of indentation. -```{r eval=FALSE} +```{r} # Good iris |> filter(Species == "setosa") |> @@ -36,7 +36,7 @@ iris |> If the arguments to a ggplot2 layer don't all fit on one line, put each argument on its own line and indent: -```{r, eval = FALSE} +```{r} # Good ggplot(aes(x = Sepal.Width, y = Sepal.Length, color = Species)) + geom_point() + @@ -54,7 +54,7 @@ ggplot(aes(x = Sepal.Width, y = Sepal.Length, color = Species)) + ggplot2 allows you to do data manipulation, such as filtering or slicing, within the `data` argument. Avoid this, and instead do the data manipulation in a pipeline before starting plotting. -```{r eval=FALSE} +```{r} # Good iris |> filter(Species == "setosa") |> diff --git a/git.Rmd b/git.qmd similarity index 100% rename from git.Rmd rename to git.qmd diff --git a/index.Rmd b/index.qmd similarity index 83% rename from index.Rmd rename to index.qmd index 26f4e1b..28d198f 100755 --- a/index.Rmd +++ b/index.qmd @@ -1,12 +1,3 @@ ---- -title: "The tidyverse style guide" -author: ["Hadley Wickham"] -site: bookdown::bookdown_site -output: bookdown::bs4_book -github-repo: tidyverse/style -documentclass: book ---- - # Welcome {-} Good coding style is like correct punctuation: you can manage without it, butitsuremakesthingseasiertoread. This site describes the style used throughout the [tidyverse](http://tidyverse.org). It was derived from Google's original R Style Guide - but @@ -20,7 +11,10 @@ Two R packages support this style guide: selected text, files, or entire projects. It includes an RStudio add-in, the easiest way to re-style existing code. - ```{r, echo = FALSE, fig.align = "center"} + ```{r} + #| eval: true + #| echo: false + #| fig-align: center knitr::include_graphics("styler-addin.png", dpi = 220) ``` diff --git a/news.Rmd b/news.qmd similarity index 98% rename from news.Rmd rename to news.qmd index a4c1c87..36ae40f 100644 --- a/news.Rmd +++ b/news.qmd @@ -6,7 +6,7 @@ Each user-facing change to a package should be accompanied by a bullet in `NEWS. The goal of the bullet is to briefly describe the change so users of the packages can understand what's changed. This can be similar to the commit message, but written with a user (not developer) in mind. It's worth emphasizing this point --- the reader of your NEWS entries is likely unfamiliar with the day-to-day development work or internals of your package. Think carefully about how to concisely but clearly summarize what's changed and why it matters for them. If it doesn't matter (i.e. it's a purely internal change), you don't need a bullet. -New bullets should be added to the top of the file (immediately under the first heading) and should be a single line. Organisation and wrapping will happen later, during the release process (Section \@ref(news-release)). +New bullets should be added to the top of the file (immediately under the first heading) and should be a single line. Organisation and wrapping will happen later, during the release process. ``` # haven (development version) diff --git a/package-files.Rmd b/package-files.qmd similarity index 85% rename from package-files.Rmd rename to package-files.qmd index 9c33e53..798dc44 100644 --- a/package-files.Rmd +++ b/package-files.qmd @@ -1,6 +1,6 @@ # Files {#package-files} -The majority of advice in Chapter \@ref(files) also applies to files in packages. Important differences are described below. +The majority of advice in @sec-files also applies to files in packages. Important differences are described below. ## Names @@ -19,7 +19,7 @@ documentation should appear first, with private functions appearing after all documented functions. If multiple public functions share the same documentation, they should all immediately follow the documentation block. -See \@ref(documentation) for more thorough guidance on documenting functions +See @sec-documentation for more thorough guidance on documenting functions in packages. ```{r} diff --git a/packages.Rmd b/packages.Rmd deleted file mode 100644 index bf2408e..0000000 --- a/packages.Rmd +++ /dev/null @@ -1,2 +0,0 @@ -# (PART) Packages {-} - diff --git a/pipes.Rmd b/pipes.qmd similarity index 94% rename from pipes.Rmd rename to pipes.qmd index 48c61fd..773d589 100755 --- a/pipes.Rmd +++ b/pipes.qmd @@ -6,7 +6,7 @@ Use `|>` to emphasise a sequence of actions, rather than the object that the act The tidyverse has been designed to work particuarly well with the pipe, but you can use it with any code, particularly in conjunction with the `_` placeholder. -```{r, eval = FALSE} +```{r} strings |> str_replace("a", "b"), str_replace("x", "y") @@ -28,7 +28,7 @@ Avoid using the pipe when: `|>` should always have a space before it, and should usually be followed by a new line. After the first step, each line should be indented by two spaces. This structure makes it easier to add new steps (or rearrange existing steps) and harder to overlook a step. -```{r, eval = FALSE} +```{r} # Good iris |> summarize(across(where(is.numeric), mean), .by = Species) |> @@ -46,7 +46,7 @@ arrange(value) If the arguments to a function don't all fit on one line, put each argument on its own line and indent: -```{r, eval = FALSE} +```{r} # Good iris |> summarise( @@ -62,7 +62,7 @@ iris |> For data analysis, we recommend using the pipe whenever a function needs to span multiple lines, even if it's only a single step. -```{r, eval = FALSE} +```{r} # Bad summarise( iris, @@ -76,7 +76,7 @@ summarise( It's ok to write a short pipe on a single line: -```{r, eval = FALSE} +```{r} # Ok iris |> subset(Species == "virginica") |> _$Sepal.Length iris |> summarise(width = Sepal.Width, .by = Species) |> arrange(width) @@ -84,7 +84,7 @@ iris |> summarise(width = Sepal.Width, .by = Species) |> arrange(width) But because short pipes often become longer pipes, we recommend that you generally stick to one function per line: -```{r, eval = FALSE} +```{r} # Better iris |> subset(Species == "virginica") |> @@ -100,7 +100,7 @@ longer pipe. Carefully consider whether the code is more readable with a short inline pipe (which doesn't require a lookup elsewhere) or if it's better to move the code outside the pipe and give it an evocative name. -```{r, eval = FALSE} +```{r} # Good x |> semi_join(y |> filter(is_valid)) @@ -122,7 +122,7 @@ There are three acceptable forms of assignment: * Variable name and assignment on separate lines: - ```{r, eval = FALSE} + ```{r} iris_long <- iris |> gather(measure, value, -Species) |> @@ -131,7 +131,7 @@ There are three acceptable forms of assignment: * Variable name and assignment on the same line: - ```{r, eval = FALSE} + ```{r} iris_long <- iris |> gather(measure, value, -Species) |> arrange(-value) @@ -139,7 +139,7 @@ There are three acceptable forms of assignment: * Assignment at the end of the pipe with `->`: - ```{r, eval = FALSE} + ```{r} iris |> gather(measure, value, -Species) |> arrange(-value) -> @@ -154,7 +154,7 @@ you of the purpose of the pipe. We recommend you use the base `|>` pipe instead of magrittr's `%>%`. -```{r, eval = FALSE} +```{r} # Good iris |> summarise(width = Sepal.Width, .by = Species) |> diff --git a/plausible.html b/plausible.html new file mode 100644 index 0000000..425a01f --- /dev/null +++ b/plausible.html @@ -0,0 +1 @@ + diff --git a/styles.scss b/styles.scss new file mode 100644 index 0000000..9f75dfb --- /dev/null +++ b/styles.scss @@ -0,0 +1,53 @@ +/*-- scss:rules --*/ + +.sidebar-title { + color: #301445; +} + +div.sidebar-item-container .active { + font-weight: bold; +} + +.sidebar nav[role=doc-toc] ul>li>a.active, .sidebar nav[role=doc-toc] ul>li>ul>li>a.active{ + font-weight: bold; +} + +img.quarto-cover-image { + box-shadow: 0 .5rem 1rem rgba(0,0,0,.15); +} + +/* Headings ------------------------------------------------------ */ + +#title-block-header.quarto-title-block.default .quarto-title h1.title { + margin-bottom: 0.5rem; +} + +h2 { + margin-top: 2rem; + margin-bottom: 1rem; + font-size: 1.4rem; + font-weight: 600; +} +h3 { margin-top: 1.5em; font-size: 1.2rem; font-weight: 500; } +h4 { margin-top: 1.5em; font-size: 1.1rem; } +h5 { margin-top: 1.5em; font-size: 1rem; } + +.quarto-section-identifier { + color: #6C6C6C; + font-weight: normal; +} + +/* Code ------------------------------------------------ */ + +code { + color: #3a373c; +} + +code a:any-link { + text-decoration: underline; + text-decoration-color: #ccc; +} + +pre { + background-image: linear-gradient(160deg,#f8f8f8 0,#f1f1f1 100%); +} diff --git a/syntax.Rmd b/syntax.qmd similarity index 94% rename from syntax.Rmd rename to syntax.qmd index b1ee07c..42d9351 100755 --- a/syntax.Rmd +++ b/syntax.qmd @@ -10,7 +10,7 @@ Variable and function names should use only lowercase letters, numbers, and `_`. Use underscores (`_`) (so called snake case) to separate words within a name. -```{r, eval = FALSE} +```{r} # Good day_one day_1 @@ -31,7 +31,7 @@ If you find yourself attempting to cram data into variable names (e.g. `model_20 Generally, variable names should be nouns and function names should be verbs. Strive for names that are concise and meaningful (this is not easy!). -```{r, eval = FALSE} +```{r} # Good day_one @@ -43,7 +43,7 @@ djm1 Where possible, avoid re-using names of common functions and variables. This will cause confusion for the readers of your code. -```{r, eval = FALSE} +```{r} # Bad T <- FALSE c <- 10 @@ -56,7 +56,7 @@ mean <- function(x) sum(x) Always put a space after a comma, never before, just like in regular English. -```{r, eval = FALSE} +```{r} # Good x[, 1] @@ -70,7 +70,7 @@ x[ , 1] Do not put spaces inside or outside parentheses for regular function calls. -```{r, eval = FALSE} +```{r} # Good mean(x, na.rm = TRUE) @@ -81,7 +81,7 @@ mean( x, na.rm = TRUE ) Place a space before and after `()` when used with `if`, `for`, or `while`. -```{r, eval = FALSE} +```{r} # Good if (debug) { show(x) @@ -95,7 +95,7 @@ if(debug){ Place a space after `()` used for function arguments: -```{r, eval = FALSE} +```{r} # Good function(x) {} @@ -108,7 +108,7 @@ function(x){} The embracing operator, `{{ }}`, should always have inner spaces to help emphasise its special behaviour: -```{r, eval = FALSE} +```{r} # Good max_by <- function(data, var, by) { data |> @@ -130,7 +130,7 @@ max_by <- function(data, var, by) { Most infix operators (`==`, `+`, `-`, `<-`, etc.) should always be surrounded by spaces: -```{r, eval = FALSE} +```{r} # Good height <- (feet * 12) + inches mean(x, na.rm = TRUE) @@ -145,7 +145,7 @@ There are a few exceptions, which should never be surrounded by spaces: * The operators with [high precedence][syntax]: `::`, `:::`, `$`, `@`, `[`, `[[`, `^`, unary `-`, unary `+`, and `:`. - ```{r, eval = FALSE} + ```{r} # Good sqrt(x^2 + y^2) df$z @@ -159,7 +159,7 @@ There are a few exceptions, which should never be surrounded by spaces: * Single-sided formulas when the right-hand side is a single identifier: - ```{r, eval = FALSE} + ```{r} # Good ~foo tribble( @@ -177,7 +177,7 @@ There are a few exceptions, which should never be surrounded by spaces: Note that single-sided formulas with a complex right-hand side do need a space: - ```{r, eval = FALSE} + ```{r} # Good ~ .x + .y @@ -188,7 +188,7 @@ There are a few exceptions, which should never be surrounded by spaces: * When used in tidy evaluation `!!` (bang-bang) and `!!!` (bang-bang-bang) (because have precedence equivalent to unary `-`/`+`) - ```{r, eval = FALSE} + ```{r} # Good call(!!xyz) @@ -200,7 +200,7 @@ There are a few exceptions, which should never be surrounded by spaces: * The help operator - ```{r, eval = FALSE} + ```{r} # Good package?stats ?mean @@ -214,7 +214,7 @@ There are a few exceptions, which should never be surrounded by spaces: Adding extra spaces is ok if it improves alignment of `=` or `<-`. -```{r, eval = FALSE} +```{r} # Good list( total = a + b + c, @@ -248,7 +248,7 @@ When you call a function, you typically omit the names of data arguments, because they are used so commonly. If you override the default value of an argument, use the full name: -```{r, eval = FALSE} +```{r} # Good mean(1:10, na.rm = TRUE) @@ -259,7 +259,7 @@ mean(, TRUE, x = c(1:10, NA)) Avoid partial matching, where you supply a unique prefix of a function argument. -```{r, eval = FALSE} +```{r} # Good rep(1:2, times = 3) cut(1:10, breaks = c(0, 4, 11)) @@ -273,7 +273,7 @@ cut(1:10, br = c(0, 4, 11)) Avoid assignment in function calls: -```{r, eval = FALSE} +```{r} # Good x <- complicated_function() if (nzchar(x) < 1) { @@ -288,7 +288,7 @@ if (nzchar(x <- complicated_function()) < 1) { The only exception is in functions that capture side-effects: -```{r, eval = FALSE} +```{r} output <- capture.output(x <- f()) ``` @@ -310,7 +310,7 @@ hierarchy easy to see: It is occassionally useful to have an empty curly braces block, in which case it should be written `{}`. -```{r, eval = FALSE} +```{r} # Good if (y < 0 && debug) { message("y is negative") @@ -376,7 +376,7 @@ while (waiting_for_something()) { If you want to rewrite a simple but lengthy `if` block: - ```{r, eval = FALSE} + ```{r} if (x > 10) { message <- "big" } else { @@ -386,7 +386,7 @@ while (waiting_for_something()) { Just write it all on one line: - ```{r, eval = FALSE} + ```{r} message <- if (x > 10) "big" else "small" ``` @@ -431,7 +431,7 @@ find_abs <- function(x) { Avoid implicit type coercion (e.g. from numeric to logical) in `if` statements: -```{r, eval = FALSE} +```{r} # Good if (length(x) > 0) { # do something @@ -450,7 +450,7 @@ if (length(x)) { * Elements that fall through to the following element should have a space after `=`. * Provide a fall-through error unless you have previously validated the input. -```{r, eval = FALSE} +```{r} # Good switch(x, a = , @@ -483,7 +483,7 @@ If a function call is too long to fit on a single line, use one line each for the function name, each argument, and the closing `)`. This makes the code easier to read and to change later. -```{r, eval = FALSE} +```{r} # Good do_something_very_complicated( something = "that", @@ -501,7 +501,7 @@ As described under [Named arguments](#argument-names), you can omit the argument for very common arguments (i.e. for arguments that are used in almost every invocation of the function). If this introduces a large disparity between the line lengths, you may want to supply names anyway: -```{r, eval = FALSE} +```{r} # Good my_function( x, @@ -524,7 +524,7 @@ related to each other. A common example of this is creating strings with `paste()`. In such cases, it's often beneficial to match one line of code to one line of output. -```{r, eval = FALSE} +```{r} # Good paste0( "Requirement: ", requires, "\n", @@ -562,7 +562,7 @@ x = 5 Use `"`, not `'`, for quoting text. The only exception is when the text already contains double quotes and no single quotes. -```{r, eval=FALSE} +```{r} # Good "Text" 'Text with "quotes"' diff --git a/tests.Rmd b/tests.qmd similarity index 100% rename from tests.Rmd rename to tests.qmd