Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
96 commits
Select commit Hold shift + click to select a range
5130454
Correctly detect supported custom class
cderv Aug 28, 2020
f184c09
refactor debuging helper
cderv Aug 28, 2020
c693e43
Get the label for reference from div identifier
cderv Aug 31, 2020
995f8a4
label should be local to the program
cderv Aug 31, 2020
0be16a1
Create the latex environment based on the fenced_divs values.
cderv Aug 31, 2020
35b4bab
insert the part to allow reference
cderv Aug 31, 2020
3158734
make it conditional to label existance.
cderv Aug 31, 2020
7322714
test empty rather than nil for id
cderv Aug 31, 2020
965c7aa
first working version for HTML
cderv Aug 31, 2020
da0a20b
directly write raw tex to not rely on resolve_refs_latex
cderv Sep 2, 2020
773943a
remove trailing space
cderv Sep 3, 2020
d91cb6f
For now warn is no id is set
cderv Sep 3, 2020
5764b9f
correct alignment
cderv Sep 3, 2020
70c70e7
add the supported custom environments
cderv Sep 3, 2020
50e7183
we need rmarkdown dev
cderv Sep 3, 2020
c1410ec
Refactor to clarify
cderv Sep 3, 2020
b1d6ce5
do not separate in two rawblock
cderv Sep 3, 2020
4c18841
missing string.format
cderv Sep 3, 2020
cebd653
also add the environment definition in tex header if only pandoc fenc…
cderv Sep 3, 2020
53e1700
add rough support for eng_proof
cderv Sep 3, 2020
af25c92
Correctly get the content on the same line as Proof.
cderv Sep 7, 2020
d18d277
Find a way to skip if it is a block already processed by eng_proof
cderv Sep 7, 2020
43b2586
Add the dot after the provided name
cderv Sep 7, 2020
473e131
Add the space only if name if provided
cderv Sep 7, 2020
d696340
do not rely on position and check all inlines
cderv Sep 7, 2020
302a2ab
keep the id on the span even for proof engine
cderv Sep 8, 2020
0caa2eb
correct test for warning
cderv Sep 10, 2020
16131f3
Pass _bookdown.yml to pandoc
cderv Sep 10, 2020
31e76c7
can't index a nil value in Lua
cderv Sep 10, 2020
a45ed6b
Really don't need the pre_processor.
cderv Sep 10, 2020
85562c1
add pandoc lua filters helpers functions
cderv Sep 10, 2020
0cc8320
add custom environment lua filters by default for now in gitbook pdf_…
cderv Sep 10, 2020
bc91db0
some garbage from previous tests
cderv Sep 10, 2020
883d27a
obviously only pass as metadata-file if _bookdown.yml exists
cderv Sep 10, 2020
586782b
reword
cderv Sep 11, 2020
d421a5c
ignore data-latex attributes so that latex-divs.lua in rmarkdown does…
cderv Sep 11, 2020
dfc22e4
= not ==
cderv Sep 11, 2020
65eccba
use a temp yml file to avoid conflict with other pandoc metadata
cderv Sep 15, 2020
019b049
Require in dev rmarkdown version currently a specific PR
cderv Sep 16, 2020
29dd63e
no need to create multiple file. Use only one that is overriden
cderv Sep 17, 2020
4070303
add tests for custom_bookdown_filters
cderv Sep 17, 2020
6d9b11b
separate the lua filter to work with new rmarkdown support
cderv Sep 17, 2020
89944d2
prepend lua filter in html_document2
cderv Sep 17, 2020
833435c
Correct message
cderv Sep 17, 2020
99c1ce8
add a wrapper and add in several formats
cderv Sep 17, 2020
9bda2da
also in html_chapters()
cderv Sep 17, 2020
53130ac
remove undesired changes
cderv Sep 17, 2020
566bc7a
add test for other helper
cderv Sep 17, 2020
7b6ac0a
Fix the debugging printing
cderv Sep 18, 2020
3d11832
ignore explictly divs created by eng_theorem too
cderv Sep 18, 2020
739f4e5
improve debugging messages to help testing and checking
cderv Sep 18, 2020
409b571
unused variable
cderv Sep 18, 2020
d790a83
Add to first content of the Div, not first in the Div
cderv Sep 18, 2020
eaf349c
but there is a space missing
cderv Sep 18, 2020
351b1f9
forgot to remove one parenthesis
cderv Sep 18, 2020
8f3411d
bookdown use = not <-
cderv Sep 21, 2020
3d31abf
Use name as the current engines
cderv Sep 21, 2020
ffa2069
add a function to convert theorems and proofs engine to pandoc fenced…
cderv Sep 21, 2020
3440515
use also label regex
cderv Sep 21, 2020
3876efe
Add a test for convert function
cderv Sep 21, 2020
24295ef
Do not rewrite the file if no chunk to modify
cderv Sep 21, 2020
bc3bb62
change message
cderv Sep 21, 2020
e4dfb59
it is the sum of logical to get the number
cderv Sep 21, 2020
228711d
generate an id if none is provided
cderv Sep 21, 2020
21d63be
add the square after the last p tag
cderv Sep 22, 2020
f68c2c3
refactor and simplify the lua filter
cderv Sep 22, 2020
812f4b6
add a commment to better explain
cderv Sep 22, 2020
5012125
clean the lua filter
cderv Sep 22, 2020
6c19437
Import rmarkdown dev version
cderv Sep 22, 2020
8812e1c
all_math_env is what we want here
cderv Sep 23, 2020
e7976ea
add some test for Theorems and Proofs with fenced divs
cderv Sep 23, 2020
9d54de0
load config is not exported
cderv Sep 23, 2020
802e1e9
use inherits() from base instead of is() from methods
cderv Sep 23, 2020
7a49610
it does not work currently as with testthat and won't load the all pa…
cderv Sep 23, 2020
30d14e6
test _bookdown.yml is in rmd dir
cderv Sep 23, 2020
683df63
separate the tests
cderv Sep 23, 2020
4e7e764
Use pandoc.Attr constructor instead of the more convenient attribut s…
cderv Sep 23, 2020
da703ff
review help page
cderv Sep 23, 2020
fe318f7
typo
cderv Sep 23, 2020
8f47d00
Simplify the nested if structure
cderv Sep 24, 2020
db731db
use local functions instead of global
cderv Sep 24, 2020
5a90518
Defines get_name function locally but outside of Div function and sim…
cderv Sep 24, 2020
4e97a1e
rmarkdown 2.4 is on CRAN now
yihui Oct 12, 2020
04fbf45
cosmetic
yihui Oct 12, 2020
5e51337
tweak convert_to_fenced_div() and rename it to fence_theorems()
yihui Oct 12, 2020
6215f07
add a `text` argument to make this function easier to test (no need t…
yihui Oct 12, 2020
cf36699
move the lua folder from resources/ to rmarkdown/ so we can use rmark…
yihui Oct 12, 2020
13fac33
only add tests that are directly relevant to bookdown, and not tests …
yihui Oct 12, 2020
4d7a123
return() early to make the code more compact (less indent and fewer n…
yihui Oct 12, 2020
6ef5c31
make the config and the temp path arguments of the function, so the f…
yihui Oct 12, 2020
8b598b4
the actual filename shouldn't matter
yihui Oct 12, 2020
bc7f09e
rename bookdown_metadata_file_arg() to bookdown_yml_arg()
yihui Oct 12, 2020
754ee46
this test won't work with rmarkdown 2.4 because of https://github.com…
yihui Oct 12, 2020
5233ec6
simplify the tests for bookdown_yml_arg() after faa25ee27edd88d683411…
yihui Oct 12, 2020
7c5fbb8
add the custom-environment.lua filter in common_format_config() inste…
yihui Oct 12, 2020
f613761
add a news item, and we can close #924 now
yihui Oct 12, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,10 @@ License: GPL-3
Imports:
htmltools (>= 0.3.6),
knitr (>= 1.22),
rmarkdown (>= 2.3.5),
rmarkdown (>= 2.4),
xfun (>= 0.13),
tinytex (>= 0.12)
tinytex (>= 0.12),
yaml (>= 2.1.19)
Suggests:
htmlwidgets,
rstudioapi,
Expand All @@ -80,4 +81,3 @@ SystemRequirements: Pandoc (>= 1.17.2)
LazyData: TRUE
RoxygenNote: 7.1.1
Encoding: UTF-8
Remotes: rstudio/rmarkdown
1 change: 1 addition & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export(calibre)
export(clean_book)
export(context_document2)
export(epub_book)
export(fence_theorems)
export(gitbook)
export(github_document2)
export(html_book)
Expand Down
2 changes: 2 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

- Add the `number_sections` argument to `markdown_document2()` and its family. This allows to have now figure references numbered by chapters in these formats, like `word_document2()` or `odt_document2()` for example. This argument default to `TRUE` like `html_document2()` and `pdf_document2()`. Set it to `number_sections = FALSE` to get the same output as previous version without numbered chapters (thanks, @atusy, #756).

- Provided an alternative way to create theorem and proof environments using Pandoc's fenced Divs. Previously, **bookdown** supports theorems and proofs in special code chunks like ```` ```{theorem}````. Now you can use the new syntax ```::: {.theorem}```. You may use the helper function `bookdown::fence_theorems()` to convert the former syntax to the latter. The main benefit of using fenced Divs is that you can write arbitrary content in a theorem environment (here "theorem" includes other environments such as lemma, corollary, and definition, etc.), such as R code chunks and inline R code, which was not possible previously. Note that this feature is only supported for LaTeX and HTML output formats at the moment. To learn more about the fenced Divs in general, you may read this section in _R Markdown Cookbook_: https://bookdown.org/yihui/rmarkdown-cookbook/custom-blocks.html (thanks, @tchevri #924, @cderv #940).

## BUG FIXES

- Correctly encode the document title when creating the Twitter sharing link from a bookdown chapter (thanks, @maelle, #934).
Expand Down
4 changes: 3 additions & 1 deletion R/latex.R
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,9 @@ add_toc_bib = function(x) {
restore_block2 = function(x, global = FALSE) {
i = grep('^\\\\begin\\{document\\}', x)[1]
if (is.na(i)) return(x)
if (length(grep(sprintf('^\\\\BeginKnitrBlock\\{(%s)\\}', paste(all_math_env, collapse = '|')), x)) &&
# add the necessary definition in the preamble when block2 engine (\BeginKnitrBlock) or pandoc
# 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_defs = sprintf(
'%s\\newtheorem{%s}{%s}%s', theorem_style(names(theorem_abbr)), names(theorem_abbr),
Expand Down
72 changes: 72 additions & 0 deletions R/utils.R
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,13 @@ common_format_config = function(
# provide file_scope if requested
if (file_scope) config$file_scope = md_chapter_splitter

# prepend the custom-environment filter
config$pandoc$lua_filters = c(
lua_filter("custom-environment.lua"), config$pandoc$lua_filters
)
# and add bookdown metadata file for the filter to work
config$pandoc$args = c(bookdown_yml_arg(), config$pandoc$args)

# set output format
config$bookdown_output_format = format

Expand Down Expand Up @@ -559,3 +566,68 @@ strip_latex_body = function(x, alt = '\nThe content was intentionally removed.\n
}
c(x1, x2[sort(i)], '\\end{document}')
}

# bookdown Lua filters paths
lua_filter = function (filters = NULL) {
rmarkdown::pkg_file_lua(filters, package = 'bookdown')
}

# pass _bookdown.yml to Pandoc's Lua filters
bookdown_yml_arg = function(config = load_config(), path = tempfile()) {
# this is supported for Pandoc >= 2.0 only
if (!pandoc2.0() || length(config) == 0) return()
yaml::write_yaml(list(bookdown = config), path)
c("--metadata-file", rmarkdown::pandoc_path_arg(path))
}

#' Convert the syntax of theorem and proof environments from code blocks to
#' fenced Divs
#'
#' This function converts the syntax \samp{```{theorem, label, ...}} to
#' \samp{::: {.theorem #label ...}} (Pandoc's fenced Div) for theorem
#' environments.
#' @param input Path to an Rmd file that contains theorem environments written
#' in the syntax of code blocks.
#' @param text A character vector of the Rmd source. When \code{text} is
#' provided, the \code{input} argument will be ignored.
#' @param output The output file to write the converted input content. You can
#' specify \code{output} to be identical to \code{input}, which means the
#' input file will be overwritten. If you want to overwrite the input file,
#' you are strongly recommended to put the file under version control or make
#' a backup copy in advance.
#' @references Learn more about
#' \href{https://bookdown.org/yihui/bookdown/markdown-extensions-by-bookdown.html#theorems}{theorems
#' and proofs} and
#' \href{https://bookdown.org/yihui/rmarkdown-cookbook/custom-blocks.html}{custom
#' blocks} in the \pkg{bookdown} book.
#' @return If \code{output = NULL}, the converted text is returned, otherwise
#' the text is written to the output file.
#' @export
fence_theorems = function(input, text = xfun::read_utf8(input), output = NULL) {
# identify blocks
md_pattern = knitr::all_patterns$md
block_start = grep(md_pattern$chunk.begin, text)
# extract params
params = gsub(md_pattern$chunk.begin, "\\1", text[block_start])
# find block with custom environment engine
reg = sprintf("^(%s).*", paste(all_math_env, collapse = "|"))
to_convert = grepl(reg, params)
# only modify those blocks
params = params[to_convert]
block_start = block_start[to_convert]
block_end = grep(md_pattern$chunk.end, text)
block_end = vapply(block_start, function(x) block_end[block_end > x][1], integer(1))
# add a . to engine name
params = sprintf(".%s", params)
# change label to id
params = gsub(",\\s*([-/[:alnum:]]+)(,|\\s*$)", " #\\1", params)
params = gsub("label\\s*=\\s*\"([-/[:alnum:]]+)\"", "#\\1", params)
# clean , and space
params = gsub(",\\s*", " ", params)
params = gsub("\\s*=\\s*", "=", params)
# modify the blocks
text[block_start] = sprintf("::: {%s}", params)
text[block_end] = ":::"
# return the text or write to output file
if (is.null(output)) xfun::raw_string(text) else xfun::write_utf8(text, input)
}
2 changes: 1 addition & 1 deletion inst/resources/gitbook/css/plugin-bookdown.css
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ div.theorem, div.lemma, div.corollary, div.proposition, div.conjecture {
span.theorem, span.lemma, span.corollary, span.proposition, span.conjecture {
font-style: normal;
}
div.proof:after {
div.proof>p:last-child:after {
Copy link
Contributor

Choose a reason for hiding this comment

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

This has been addressed by c59e3dc.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I kind of remember having tried that, and that was not ok in the output... I may have wrongly test it maybe. 🤷‍♂️

content: "\25a2";
float: right;
}
Expand Down
188 changes: 188 additions & 0 deletions inst/rmarkdown/lua/custom-environment.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
--[[
A Pandoc 2 lua filter to deal with custom environment in bookdown
--]]

-- theorem types available to be used
local theorem_abbr = {
theorem = 'thm',
lemma = 'lem',
corollary = 'cor',
proposition = 'prp',
conjecture = 'cnj',
definition = 'def',
example = 'exm',
exercise = 'exr'
}

-- other special proof envs
local proof_label = {
proof = 'Proof',
remark = 'Remark',
solution = 'Solution'
}

-- for debuging purpose
local debug_mode = os.getenv("DEBUG_PANDOC_LUA") == "TRUE"
local function print_debug(label,obj,iter)
obj = obj or nil
iter = iter or pairs
label = label or ""
label = "DEBUG (from custom-environment.lua): "..label
if (debug_mode) then
if not obj then
print(label)
elseif (type(obj) == "string") then
print(label.." "..obj)
elseif type(obj) == "table" then
for k,v in iter(obj) do
print(label.."id:"..k.. " val:"..v)
end
end
end
return nil
end

-- create a unique id for a div with none provided
local counter = 0
local function unlabeled_div()
counter = counter + 1
return "unlabeled-div-"..(counter)
end

-- return [name] for latex, and (name) for html
local function get_name(format, options)
local name = options["name"]
if not name then return "" end
local template = {latex = "[%s]", html = " (%s)"}
name = string.format(template[format], name)
print_debug("name -> ", name)
-- remove data-name from option
options["name"] = nil
return name
end

-- Get metadata specific to bookdown for this filter
Meta = function(m)
bookdownmeta = m.bookdown
if (bookdownmeta and bookdownmeta.language and bookdownmeta.language.label) then
-- For internationalization feature of bookdown
for k,v in pairs(bookdownmeta.language.label) do
if (type(v) == 'table' and v.t == 'MetaInlines' and proof_label[k] ~= nil) then
-- remove any undesired space (3 or less)
proof_label[k] = pandoc.utils.stringify(v):gsub("%.?%s?%s?%s?$", "")
print_debug("Translation-> "..k..":", proof_label[k])
end
end
end
end

-- Modify Pandoc AST for supported custom environment
Div = function (div)
local classes = div.classes
-- we do nothing if no classes
if (#classes == 0) then
print_debug("No classes in the Div.")
return div
end
print_debug("Div classes -> " , classes)

-- checking if the class is one of the supported custom environment
local env_type = {type = nil, env = nil}
for i,v in ipairs(classes) do
if (theorem_abbr[v] ~= nil) then
env_type.type = "theorem"
env_type.env = v
break
elseif (proof_label[v] ~= nil) then
env_type.type = "proof"
env_type.env = v
break
end
end
-- classes is not a supported one, we return as is
if not env_type.env then
print_debug("Not a bookdown supported custom class")
return div
end
print_debug("Found types -> ", env_type)

-- get the id if it exists - it will we use to build label for reference
local id = div.identifier
-- if no id, one is generated so that bookdown labelling mechanism works
if #id == 0 then id = unlabeled_div() end
print_debug("id -> ", id)
-- remove unwanted identifier on the div, as it will be on the span
div.identifier = ""

-- get the attributes
local options = div.attributes
if (options["data-latex"] ~= nil) then
-- so that latex-divs.lua in rmarkdown does not activate
print("[WARNING] data-latex attribute can't be used with one of bookdown custom environment. It has been removed.")
options["data-latex"] = nil
end

-- create the custom environment
local label
-- Create a label for referencing - only for theorem like env
if (env_type.type == "theorem") then
label = string.format("%s:%s", theorem_abbr[env_type.env], id)
end
print_debug("label for reference -> ", label)

-- TODO: should we support beamer also ?
if (FORMAT:match 'latex') then
local label_part
if label then
label_part = string.format( "\n\\protect\\hypertarget{%s}{}\\label{%s}", label, label)
end
local name = get_name('latex', options)
table.insert(
div.content, 1,
pandoc.RawBlock('tex', string.format('\\begin{%s}%s%s', env_type.env, name, label_part or ""))
)
table.insert(
div.content,
pandoc.RawBlock('tex', string.format('\\end{%s}', env_type.env))
)
elseif (FORMAT:match 'html') then
local name = get_name('html', options)

-- if div is already processed by eng_theorem, it would also modify it.
-- we can ignore knowing how eng_theorem modifies options$html.before2
-- It can be Plain or Para depending if a name was used or not.
-- MAYBE NOT VERY RELIABLE THOUGH
if (div.content[1].t == "Plain" or div.content[1].t == "Para") then
for i,el in pairs(div.content[1].content) do
if (el.t == "Span" and el.classes[1] == env_type.env) then
print_debug("Already processed by knitr engine.")
return div
end
end
end

-- inserted the correct span depending on the environment type
local span
if (env_type.type == "theorem") then
span = pandoc.Span(
pandoc.Strong(string.format("(#%s)%s ", label, name)),
pandoc.Attr(label, {env_type.env})
)
elseif (env_type.type == "proof") then
span = pandoc.Span({
pandoc.Emph(pandoc.Str(proof_label[env_type.env])),
pandoc.Str(name),
pandoc.Str("."),
pandoc.Space()
},
pandoc.Attr(id, {env_type.env})
)
end
-- add to the first block of the div, and not as first block
table.insert(div.content[1].content, 1, span)
end

return div
end

return {{Meta = Meta}, {Div = Div}}
38 changes: 38 additions & 0 deletions man/fence_theorems.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions tests/rmd/_bookdown.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
language:
label:
solution: 'SOLUTION'
Loading