-
Notifications
You must be signed in to change notification settings - Fork 285
/
theorem.lua
206 lines (181 loc) · 5.47 KB
/
theorem.lua
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
-- theorem.lua
-- custom AST node for theorems, lemmata, etc.
--
-- Copyright (C) 2023 Posit Software, PBC
-- available theorem types
theorem_types = {
thm = {
env = "theorem",
style = "plain",
title = "Theorem"
},
lem = {
env = "lemma",
style = "plain",
title = "Lemma"
},
cor = {
env = "corollary",
style = "plain",
title = "Corollary",
},
prp = {
env = "proposition",
style = "plain",
title = "Proposition",
},
cnj = {
env = "conjecture",
style = "plain",
title = "Conjecture"
},
def = {
env = "definition",
style = "definition",
title = "Definition",
},
exm = {
env = "example",
style = "definition",
title = "Example",
},
exr = {
env = "exercise",
style = "definition",
title = "Exercise"
}
}
function has_theorem_ref(el)
local type = refType(el.attr.identifier)
return theorem_types[type] ~= nil
end
function is_theorem_div(div)
return is_regular_node(div, "Div") and has_theorem_ref(div)
end
_quarto.ast.add_handler({
-- empty table so this handler is only called programmatically
class_name = {},
-- the name of the ast node, used as a key in extended ast filter tables
ast_name = "Theorem",
-- generic names this custom AST node responds to
-- this is still unimplemented
interfaces = {"Crossref"},
-- Theorems are always blocks
kind = "Block",
parse = function(div)
-- luacov: disable
internal_error()
-- luacov: enable
end,
slots = { "div", "name" },
constructor = function(tbl)
return {
name = tbl.name,
div = tbl.div,
identifier = tbl.identifier
}
end
})
local included_typst_theorems = false
local letted_typst_theorem = {}
local function ensure_typst_theorems(reftype)
if not included_typst_theorems then
included_typst_theorems = true
quarto.doc.include_text("in-header", "#import \"@preview/ctheorems:1.1.0\": *")
quarto.doc.include_text("in-header", "#show: thmrules")
end
if not letted_typst_theorem[reftype] then
letted_typst_theorem[reftype] = true
local theorem_type = theorem_types[reftype]
quarto.doc.include_text("in-header", "#let " .. theorem_type.env .. " = thmbox(\"" ..
theorem_type.env .. "\", \"" .. theorem_type.title .. "\")")
end
end
_quarto.ast.add_renderer("Theorem", function()
return true
end, function(thm)
local el = thm.div
local pt = pandoc.utils.type(el)
if pt == "Blocks" or el.t ~= "Div" then
el = pandoc.Div(el)
end
el.identifier = thm.identifier -- restore identifier to render correctly
local label = thm.identifier
local type = refType(thm.identifier)
local name = quarto.utils.as_inlines(thm.name)
local theorem_type = theorem_types[refType(thm.identifier)]
local order = thm.order
-- add class for type
el.attr.classes:insert("theorem")
if theorem_type.env ~= "theorem" then
el.attr.classes:insert(theorem_type.env)
end
-- If this theorem has no content, then create a placeholder
if #el.content == 0 or el.content[1].t ~= "Para" then
tprepend(el.content, {pandoc.Para({pandoc.Str '\u{a0}'})})
end
if _quarto.format.isLatexOutput() then
local preamble = pandoc.Para(pandoc.RawInline("latex",
"\\begin{" .. theorem_type.env .. "}"))
preamble.content:insert(pandoc.RawInline("latex", "["))
if name then
tappend(preamble.content, name)
end
preamble.content:insert(pandoc.RawInline("latex", "]"))
preamble.content:insert(pandoc.RawInline("latex",
"\\protect\\hypertarget{" .. label .. "}{}\\label{" .. label .. "}")
)
el.content:insert(1, preamble)
el.content:insert(pandoc.Para(pandoc.RawInline("latex",
"\\end{" .. theorem_type.env .. "}"
)))
-- Remove id on those div to avoid Pandoc inserting \hypertaget #3776
el.attr.identifier = ""
elseif _quarto.format.isJatsOutput() then
-- JATS XML theorem
local lbl = captionPrefix({}, type, theorem_type, order)
el = jatsTheorem(el, lbl, name)
elseif _quarto.format.isTypstOutput() then
ensure_typst_theorems(type)
local preamble = pandoc.Plain({pandoc.RawInline("typst", "#" .. theorem_type.env .. "(")})
if name and #name > 0 then
preamble.content:insert(pandoc.RawInline("typst", '"'))
tappend(preamble.content, name)
preamble.content:insert(pandoc.RawInline("typst", '"'))
end
preamble.content:insert(pandoc.RawInline("typst", ")["))
local callthm = make_scaffold(pandoc.Div, preamble)
tappend(callthm.content, quarto.utils.as_blocks(el.content))
callthm.content:insert(pandoc.RawInline("typst", "] <" .. el.attr.identifier .. ">"))
return callthm
else
-- order might be nil in the case of an ipynb rendering in
-- manuscript mode
--
-- FIXME format == ipynb and enableCrossRef == false should be
-- its own rendering format
if order == nil then
return el
end
-- create caption prefix
local captionPrefix = captionPrefix(name, type, theorem_type, order)
local prefix = {
pandoc.Span(
pandoc.Strong(captionPrefix),
pandoc.Attr("", { "theorem-title" })
),
pandoc.Space()
}
-- prepend the prefix
local caption = el.content[1]
if caption.content == nil then
-- https://github.com/quarto-dev/quarto-cli/issues/2228
-- caption doesn't always have a content field; in that case,
-- use the parent?
tprepend(el.content, prefix)
else
tprepend(caption.content, prefix)
end
end
return el
end)