Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow use of a function for fig, eq or tab labels #1120

Merged
merged 13 commits into from
Apr 1, 2021
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Package: bookdown
Type: Package
Title: Authoring Books and Technical Documents with R Markdown
Version: 0.21.7
Version: 0.21.8
Authors@R: c(
person("Yihui", "Xie", role = c("aut", "cre"), email = "xie@yihui.name", comment = c(ORCID = "0000-0003-0645-5666")),
person("JJ", "Allaire", role = "ctb"),
Expand Down
2 changes: 2 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

- `hypothesis` environment is now supported among [Theorems and Proof](https://bookdown.org/yihui/bookdown/markdown-extensions-by-bookdown.html#theorems) (thanks, @shirdekel, #1102).

- In `_bookdown.yaml`, `label` fields `fig`, `tab` and `eq` can now take a function as `ui` fields `chapter_name` and `appendix`. This function takes the reference number as only argument and must return a character to be used as full label. The default is a string prepended before the reference number. This new feature gives more flexibility to change the default for other language, e.g append the label name after the number. See updated doc about [Internationalization](https://bookdown.org/yihui/bookdown/internationalization.html)(thanks, Tamás Ferenc, #1114)

## BUG FIXES

- Adapt CSS in `gitbook()` and `html_book()` for correct diplaying of `<details><summary>` content (thanks, @maelle, #971)
Expand Down
6 changes: 3 additions & 3 deletions R/ebook.R
Original file line number Diff line number Diff line change
Expand Up @@ -114,13 +114,13 @@ resolve_refs_md = function(content, ref_table, to_md = output_md()) {
if (grepl(m, content[i])) {
id = ''; sep = ':'
type = gsub('^([^:]+).*$', '\\1', j)
if (type %in% names(theorem_abbr)) {
if (type %in% theorem_abbr) {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is not part of the feature request but I think it was not working as expected before, and the span was never inserted. So I fixed it here.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good. Thanks for catching it!

id = sprintf('<span id="%s"></span>', j)
sep = ''
}
label = label_prefix(type)
label = label_prefix(type, sep = sep)(ref_table[j])
content[i] = sub(
m, paste0(id, label, ref_table[j], sep, ' '), content[i]
m, paste0(id, label, ' '), content[i]
)
break
}
Expand Down
23 changes: 18 additions & 5 deletions R/html.R
Original file line number Diff line number Diff line change
Expand Up @@ -663,13 +663,13 @@ parse_fig_labels = function(content, global = FALSE) {
labs[[i]] = character(length(lab))
next
}
labs[[i]] = paste0(label_prefix(type), num, ': ')
labs[[i]] = label_prefix(type, sep = ': ')(num)
k = max(figs[figs <= i])
content[k] = paste(c(content[k], sprintf('<span id="%s"></span>', lab)), collapse = '')
}, tab = {
if (length(grep('^\\s*<caption', content[i - 0:1])) == 0) next
labs[[i]] = sprintf(
'<span id="%s">%s</span>', lab, paste0(label_prefix(type), num, ': ')
labs[[i]] = sprintf('<span id="%s">%s</span>',
lab, label_prefix(type, sep = ': ')(num)
)
}, eq = {
labs[[i]] = sprintf('\\tag{%s}', num)
Expand All @@ -678,7 +678,7 @@ parse_fig_labels = function(content, global = FALSE) {
'(<span class="math display")', sprintf('\\1 id="%s"', lab), content[k]
)
}, {
labs[[i]] = paste0(label_prefix(type), num, ' ')
labs[[i]] = label_prefix(type, sep = ' ')(num)
})
}

Expand All @@ -693,7 +693,20 @@ parse_fig_labels = function(content, global = FALSE) {


# given a label, e.g. fig:foo, figure out the appropriate prefix
label_prefix = function(type, dict = label_names) i18n('label', type, dict)
label_prefix = function(type, dict = label_names, sep = '') {
label = i18n('label', type, dict)
supported_type = c('fig', 'tab', 'eq')
if (is.function(label)) {
if (type %in% supported_type) return(label)
msg = knitr::combine_words(supported_type)
stop("Using a function is only supported for ", msg)
}
default_fun = function(num = NULL) {
if (is.null(num)) return(label)
paste0(label, num, sep)
}
default_fun
}

ui_names = list(edit = 'Edit', chapter_name = '', appendix_name = '')
ui_language = function(key, dict = ui_names) i18n('ui', key, ui_names)
Expand Down
12 changes: 9 additions & 3 deletions R/latex.R
Original file line number Diff line number Diff line change
Expand Up @@ -234,18 +234,24 @@ restore_block2 = function(x, global = FALSE) {
# fenced div (\begin) is used
if (length(grep(sprintf('^\\\\(BeginKnitrBlock|begin)\\{(%s)\\}', paste(all_math_env, collapse = '|')), x)) &&
length(grep('^\\s*\\\\newtheorem\\{theorem\\}', head(x, i))) == 0) {
theorem_label = vapply(theorem_abbr, function(a) {
label_prefix(a)()
}, character(1), USE.NAMES = FALSE)
theorem_defs = sprintf(
'%s\\newtheorem{%s}{%s}%s', theorem_style(names(theorem_abbr)), names(theorem_abbr),
str_trim(vapply(theorem_abbr, label_prefix, character(1), USE.NAMES = FALSE)),
'%s\\newtheorem{%s}{%s}%s', theorem_style(names(theorem_abbr)),
names(theorem_abbr), str_trim(theorem_label),
if (global) '' else {
if (length(grep('^\\\\chapter[*]?', x))) '[chapter]' else '[section]'
}
)
# the proof environment has already been defined by amsthm
proof_envs = setdiff(names(label_names_math2), 'proof')
proof_labels = vapply(
lapply(proof_envs, label_prefix, dict = label_names_math2),
function(fun) fun(), character(1), USE.NAMES = FALSE)
proof_defs = sprintf(
'%s\\newtheorem*{%s}{%s}', theorem_style(proof_envs), proof_envs,
gsub('^\\s+|[.]\\s*$', '', vapply(proof_envs, label_prefix, character(1), label_names_math2))
gsub('^\\s+|[.]\\s*$', '', proof_labels)
)
x = append(x, c('\\usepackage{amsthm}', theorem_defs, proof_defs), i - 1)
}
Expand Down
2 changes: 1 addition & 1 deletion R/utils.R
Original file line number Diff line number Diff line change
Expand Up @@ -505,7 +505,7 @@ eng_proof = function(options) {
"The type of proof '", type, "' is not supported yet."
)
options$type = type
label = label_prefix(type, label_names_math2)
label = label_prefix(type, label_names_math2)()
name = options$name; to_md = output_md()
if (length(name) == 1) {
if (!to_md) options$latex.options = sprintf('[%s]', sub('[.]\\s*$', '', name))
Expand Down
12 changes: 11 additions & 1 deletion inst/examples/04-customization.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,16 @@ language:
fig: "FIGURE "
```

The fields under `ui` are used to specify some terms in the user interface. The `edit` field specifies the text associated with the `edit` link in `_bookdown.yml` (Section \@ref(configuration)). The `chapter_name` field can be either a character string to be prepended to chapter numbers in chapter titles (e.g., `'CHAPTER '`), or an R function that takes the chapter number as the input and returns a string as the new chapter number (e.g., `!expr function(i) paste('Chapter', i)`). If it is a character vector of length 2, the chapter title prefix will be `paste0(chapter_name[1], i, chapter_name[2])`, where `i` is the chapter number. Similarly, the `appendix_name` field will be prepended to appendix counters in appendix titles (e.g., `'Appendix '`). Again, a function can also be used.
The fields under `ui` are used to specify some terms in the user interface. The `edit` field specifies the text associated with the `edit` link in `_bookdown.yml` (Section \@ref(configuration)). The fields `chapter_name`, `appendix_name`, `fig`, `tab` and `eq` can be either a character string to be prepended to chapter (e.g., `'CHAPTER '`) or reference number (e.g., `'FIGURE '`), or an R function that takes a number (chapter or reference number) as the input and returns a string. (e.g., `!expr function(i) paste('Chapter', i)`). Here is an example for Hungarian:

```yaml
language:
label:
fig: !expr function(i) paste(i, 'ábra')
ui:
chapter_name: !expr function(i) paste0(i, '. fejezet')
```

For `chapter_name` and `appendix_name` only, if it is a character vector of length 2, the chapter title prefix will be `paste0(chapter_name[1], i, chapter_name[2])`, where `i` is the chapter number.

There is one caveat when you write in a language that uses multibyte characters, such as Chinese, Japanese, and Korean (CJK): Pandoc cannot generate identifiers from section headings that are pure CJK characters, so you will not be able to cross-reference sections (they do not have labels), unless you manually assign identifiers to them by appending `{#identifier}` to the section heading, where `identifier` is an identifier of your choice.
26 changes: 26 additions & 0 deletions tests/testit/test-html.R
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,29 @@ assert("biblio references section is correcly found", {
(parse_references(html)$div %==% html[[4]])
(length(parse_references(html)$refs) == 1)
})

assert("i18n config can be retrieved ", {
opts$set(config = list())
# default
(i18n("label", "tab", label_names) %==% "Table ")
(i18n("ui", "chapter_name", ui_names) %==% "")
(i18n("ui", "dummy", ui_names) %==% NULL)
# config set
opts$set(config = list(language = list(
label = list(tab = "TABLE "),
ui = list(chapter_name = "CHAPTER "))
))
(i18n("label", "tab") %==% "TABLE ")
(i18n("ui", "chapter_name") %==% "CHAPTER ")
opts$set(config = list())
})

assert("label_prefix retrieves correct config", {
fun = function(i) paste0("TAB-", i)
opts$set(config = list(language = list(label = list(tab = fun))))
(label_prefix("tab") %==% fun)
(is.function(label_prefix("fig")))
(label_prefix("fig")(1) %==% "Figure 1")
(label_prefix("fig", sep = ":")(1) %==% "Figure 1:")
opts$set(config = list())
})