Skip to content

Commit dfa56b5

Browse files
maellecderv
andauthored
Greater flexibility in repo specification (#1036)
This adds support in `bs4_book()` for customizing base url, branch, subdir and icon all of which allow tweaking the look of the source edit button in the book Co-authored-by: Christophe Dervieux <christophe.dervieux@gmail.com> Co-authored-by: Christophe Dervieux <cderv@rstudio.com>
1 parent 469c585 commit dfa56b5

File tree

9 files changed

+324
-41
lines changed

9 files changed

+324
-41
lines changed

DESCRIPTION

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
Package: bookdown
22
Type: Package
33
Title: Authoring Books and Technical Documents with R Markdown
4-
Version: 0.22.4
4+
Version: 0.22.5
55
Authors@R: c(
66
person("Yihui", "Xie", role = c("aut", "cre"), email = "xie@yihui.name", comment = c(ORCID = "0000-0003-0645-5666")),
77
person("JJ", "Allaire", role = "ctb"),
@@ -81,10 +81,13 @@ Suggests:
8181
testit (>= 0.9),
8282
tufte,
8383
xml2,
84-
webshot
84+
webshot,
85+
testthat (>= 3.0.0),
86+
withr (>= 2.3.0)
8587
URL: https://github.com/rstudio/bookdown, https://pkgs.rstudio.com/bookdown/
8688
BugReports: https://github.com/rstudio/bookdown/issues
8789
SystemRequirements: Pandoc (>= 1.17.2)
8890
RoxygenNote: 7.1.1
8991
Encoding: UTF-8
92+
Config/testthat/edition: 3
9093
VignetteBuilder: knitr

NEWS.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# CHANGES IN bookdown VERSION 0.23
22

3+
- `repo` specification in `bs4_book()` can now be done in a more flexible way: base url, branch name, subdir and icon can be specify. See `?bookdown::bs4_book()` for details (thanks, @maelle, #1036).
4+
35
- The `bookdown::gitbook` output format now supports an alternative search engine, namely `fuse.js`, which has several advantages over `lunr.js`, the previous search engine for `gitbook`. Using `fuse.js` will fix a number of long-standing issues such as #734, #735, and #792. To enable `fuse.js`, set the search engine to be `fuse` in `gitbook`'s config in YAML, e.g.,
46

57
```yaml

R/bs4_book.R

Lines changed: 91 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -35,22 +35,55 @@
3535
#' The default, `bs4_book_theme()`, resets the base font size to 1rem to
3636
#' make reading easier and uses a primary colour with greater constrast
3737
#' against the background.
38-
#' @param repo Link to repository where book is hosted, used to generate
39-
#' view source and edit buttons. Currently assumes GitHub and that the book
40-
#' is in the root directory of the repo.
38+
#' @param repo Either link to repository where book is hosted, used to generate
39+
#' view source and edit buttons or a list with repository `base` link, default
40+
#' `branch`, `subdir` and `icon` (see "Specifying the repository").
4141
#' @param lib_dir,pandoc_args,extra_dependencies,... Passed on to
4242
#' [rmarkdown::html_document()].
43+
#'
44+
#' @section Specifying the repository:
45+
#'
46+
#' If your book has a default branch called main you can use
47+
#'
48+
#' ```yaml
49+
#' bookdown::bs4_book:
50+
#' repo:
51+
#' base: https://github.com/hadley/ggplot2-book
52+
#' branch: main
53+
#' ```
54+
#'
55+
#' If your book is furthermore located in a subdirectory called "book" you can use
56+
#'
57+
#' ```yaml
58+
#' bookdown::bs4_book:
59+
#' repo:
60+
#' base: https://github.com/hadley/ggplot2-book
61+
#' branch: main
62+
#' subdir: book
63+
#' ```
64+
#'
65+
#' By default if the repo URL contains "github" it will get a GitHub font-awesome
66+
#' icon, and otherwise a GitLab font-awesome icon.
67+
#' To use another icon, specify it with the correct prefix (`fas`, `fab`, ...) (Font Awesome 5).
68+
#'
69+
#' ```yaml
70+
#' bookdown::bs4_book:
71+
#' repo:
72+
#' base: https://github.com/hadley/ggplot2-book
73+
#' branch: main
74+
#' subdir: book
75+
#' icon: "fas fa-air-freshener"
76+
#' ```
77+
#'
4378
#' @export
4479
#' @md
45-
bs4_book <- function(
46-
theme = bs4_book_theme(),
80+
bs4_book <- function(theme = bs4_book_theme(),
4781
repo = NULL,
4882
...,
4983
lib_dir = "libs",
5084
pandoc_args = NULL,
51-
extra_dependencies = NULL
52-
) {
53-
check_packages(c("bslib", "downlit", "jsonlite", "xml2"))
85+
extra_dependencies = NULL) {
86+
check_packages(bs4_book_deps())
5487
bs4_check_dots(...)
5588

5689
# Allow theme specification in yaml metadata
@@ -102,8 +135,7 @@ bs4_book_theme <- function(primary = "#0068D9", ...) {
102135
bs4_book_build <- function(output = "bookdown.html",
103136
repo = NULL,
104137
lib_dir = "libs",
105-
output_dir = opts$get("output_dir")
106-
) {
138+
output_dir = opts$get("output_dir")) {
107139
move_files_html(output, lib_dir)
108140

109141
rmd_index <- new.env(parent = emptyenv())
@@ -121,7 +153,7 @@ bs4_book_build <- function(output = "bookdown.html",
121153
output_dir <- "_book"
122154
}
123155

124-
if (isTRUE(opts$get('preview'))) {
156+
if (isTRUE(opts$get("preview"))) {
125157
bs4_chapter_tweak(
126158
output2,
127159
repo = repo,
@@ -179,8 +211,8 @@ build_toc <- function(output) {
179211
if (any(is_appendix)) {
180212
app <- toc[
181213
seq_along(is_appendix) > which(is_appendix)[[1]] &
182-
toc$level == 1 &
183-
!is.na(toc$num),
214+
toc$level == 1 &
215+
!is.na(toc$num),
184216
]
185217
app$label <- LETTERS[seq_len(nrow(app))]
186218
# TODO: make less of a hack
@@ -202,17 +234,17 @@ build_toc <- function(output) {
202234
toc
203235
}
204236

205-
bs4_book_page = function(head,
206-
toc,
207-
chapter,
208-
link_prev,
209-
link_next,
210-
rmd_cur,
211-
html_cur,
212-
foot,
213-
rmd_index = NULL) {
237+
bs4_book_page <- function(head,
238+
toc,
239+
chapter,
240+
link_prev,
241+
link_next,
242+
rmd_cur,
243+
html_cur,
244+
foot,
245+
rmd_index = NULL) {
214246
rmd_index[[html_cur]] <- rmd_cur
215-
paste(c(head, toc, chapter, foot), collapse = '\n')
247+
paste(c(head, toc, chapter, foot), collapse = "\n")
216248
}
217249

218250
bs4_book_dependency <- function(theme) {
@@ -239,7 +271,6 @@ bs4_chapters_tweak <- function(output,
239271
rmd_index = NULL,
240272
repo = NULL,
241273
output_dir = opts$get("output_dir")) {
242-
243274
toc <- build_toc(output)
244275
files <- toc[!duplicated(toc$file_name) & !is.na(toc$file_name), ]
245276
files$path <- file.path(output_dir, files$file_name)
@@ -283,7 +314,6 @@ bs4_chapter_tweak <- function(path, toc, rmd_index = NULL, repo = NULL) {
283314
chapter = h1,
284315
path = basename(path)
285316
)
286-
287317
}
288318

289319
tweak_chapter <- function(html) {
@@ -390,17 +420,36 @@ tweak_tables <- function(html) {
390420
}
391421

392422
tweak_navbar <- function(html, toc, active = "", rmd_index = NULL, repo = NULL) {
423+
if (!is.null(repo) && length(repo) == 1) {
424+
repo <- list(
425+
base = repo,
426+
branch = "master",
427+
subdir = NULL
428+
)
429+
}
393430

394431
# Source links ------------------------------------------------------------
395432
if (!is.null(repo) && active %in% names(rmd_index)) {
396-
repo_edit <- paste0(repo, "/edit/master/", rmd_index[[active]])
397-
repo_view <- paste0(repo, "/blob/master/", rmd_index[[active]])
433+
if (!is.null(repo$subdir)) {
434+
repo$subdir <- paste0(repo$subdir, "/")
435+
}
436+
437+
repo_edit <- paste0(repo$base, "/edit/", repo$branch, "/", repo$subdir, rmd_index[[active]])
438+
repo_view <- paste0(repo$base, "/blob/", repo$branch, "/", repo$subdir, rmd_index[[active]])
398439
} else {
399440
repo_edit <- NULL
400441
repo_view <- NULL
401442
}
402443

403-
template_link(html, ".//a[@id='book-repo']", repo)
444+
if (!is.null(repo$base)) {
445+
icon <- repo$icon %n%
446+
ifelse(grepl("github\\.com", repo$base), "fab fa-github", "fab fa-gitlab")
447+
template_link_icon(html, ".//a[@id='book-repo']", icon)
448+
template_link_icon(html, ".//a[@id='book-source']", icon)
449+
template_link_icon(html, ".//a[@id='book-edit']", icon)
450+
}
451+
452+
template_link(html, ".//a[@id='book-repo']", repo$base)
404453
template_link(html, ".//a[@id='book-source']", repo_view)
405454
template_link(html, ".//a[@id='book-edit']", repo_edit)
406455

@@ -516,10 +565,16 @@ template_link <- function(html, xpath, href) {
516565
}
517566
}
518567

568+
template_link_icon <- function(html, xpath, icon) {
569+
icon_node <- xml2::xml_child(xml2::xml_find_first(html, xpath))
570+
xml2::xml_attr(icon_node, "class") <- icon
571+
}
572+
519573
# index -------------------------------------------------------------------
520574

521575
bs4_index_data <- function(node, chapter, path) {
522-
children <- xml2::xml_find_all(node,
576+
children <- xml2::xml_find_all(
577+
node,
523578
"./*[not(self::div and contains(@class, 'section'))]"
524579
)
525580
if (length(children) == 0 || !is_heading(children[[1]])) {
@@ -616,3 +671,10 @@ bs4_check_dots <- function(...) {
616671
)
617672
}
618673
}
674+
675+
# these dependencies are required to use bs4_book() but are suggested deps
676+
# of bookdown. Hence the need to check they are available
677+
# TODO: remove this and the check in bs4_book when we add them as Imports (if we do it)
678+
bs4_book_deps <- function() {
679+
c("bslib", "downlit", "jsonlite", "xml2")
680+
}

R/skeleton.R

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,11 @@ bookdown_skeleton = function(path) {
3939
#' @param chapters The chapter titles.
4040
#' @param documentclass The LaTeX document class.
4141
#' @param references The title of the references section.
42+
#' @param path The directory in which to create the book.
4243
#' @noRd
4344
book_skeleton = function(
4445
name, title, author, chapters = c('Preface {-}', 'Introduction'),
45-
documentclass = 'book', references = 'References'
46+
documentclass = 'book', references = 'References', path = getwd()
4647
) {
4748
rmd_files = gsub('[^-a-zA-Z0-9]', '', gsub('\\s+', '-', c(chapters, references)))
4849
rmd_files = sprintf('%02d-%s.Rmd', seq_along(rmd_files) - 1, rmd_files)
@@ -55,20 +56,24 @@ book_skeleton = function(
5556
titles = paste('#', titles)
5657
for (i in seq_along(rmd_files)) {
5758
content = c(titles[i], '')
59+
# special handling for file which will be index.Rmd
5860
if (i == 1) {
61+
index_metadata = list(title = title, author = author,
62+
documentclass = documentclass,
63+
site = 'bookdown::bookdown_site')
5964
content = c(
60-
'---', sprintf('title: "%s"', title), sprintf('author: "%s"', author),
61-
sprintf('documentclass: "%s"', documentclass),
62-
'site: bookdown::bookdown_site', '---', '', content,
65+
'---', yaml::as.yaml(index_metadata), '---', '',
66+
content,
6367
'Start writing your book here. If you are in RStudio,',
6468
'Click the Build button to build the book.'
6569
)
6670
}
67-
write_file(content, rmd_files[i])
71+
write_file(content, file.path(path, rmd_files[i]))
6872
}
6973
write_file(
70-
sprintf('bookdown::%s: default', c('gitbook', 'pdf_book', 'epub_book')), '_output.yml'
74+
sprintf('bookdown::%s: default', c('gitbook', 'pdf_book', 'epub_book', 'bs4_book')),
75+
file.path(path, '_output.yml')
7176
)
72-
write_file(sprintf('book_filename: %s', name), '_bookdown.yml')
77+
write_file(sprintf('book_filename: %s', name), file.path(path, '_bookdown.yml'))
7378
}
7479

man/bs4_book.Rd

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

tests/testthat.R

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
library(testthat)
2+
library(bookdown)
3+
4+
test_check("bookdown")

tests/testthat/helper-bs4_book.R

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# TODO: Replace if helper in testthat gets vectorised
2+
# https://github.com/r-lib/testthat/issues/1398
3+
skip_if_bs4_book_deps_missing <- function() {
4+
check <- vapply(bs4_book_deps(), skip_if_not_installed, logical(1L), USE.NAMES = FALSE)
5+
invisible(check)
6+
}
7+
8+
local_bs4_book <- function(name = "book",
9+
title = "Awesome Cookbook",
10+
author = "Yoda",
11+
output_options = NULL,
12+
env = parent.frame()) {
13+
14+
# don't run test using this book skeleton when Pandoc is not available
15+
skip_if_not_pandoc()
16+
17+
path <- withr::local_tempdir(.local_envir = env)
18+
19+
book_skeleton(
20+
name = name,
21+
title = title,
22+
author = author,
23+
path = path
24+
)
25+
26+
suppressMessages(
27+
render_book(
28+
path,
29+
output_format = "bookdown::bs4_book",
30+
output_options = output_options,
31+
quiet = TRUE
32+
)
33+
)
34+
35+
return(path)
36+
}

tests/testthat/helper.R

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# From rmarkdown helper.R
2+
3+
# Use to test pandoc availability or version lower than
4+
skip_if_not_pandoc <- function(ver = NULL) {
5+
if (!rmarkdown::pandoc_available(ver)) {
6+
msg <- if (is.null(ver)) {
7+
"Pandoc is not available"
8+
} else {
9+
sprintf("Version of Pandoc is lower than %s.", ver)
10+
}
11+
skip(msg)
12+
}
13+
}
14+
15+
# Use to test version greater than
16+
skip_if_pandoc <- function(ver = NULL) {
17+
if (rmarkdown::pandoc_available(ver)) {
18+
msg <- if (is.null(ver)) {
19+
"Pandoc is available"
20+
} else {
21+
sprintf("Version of Pandoc is greater than %s.", ver)
22+
}
23+
skip(msg)
24+
}
25+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy