Created
July 7, 2021 17:57
-
-
Save Pangoraw/1be19ac127a56f3cdfc6b9b6b384a613 to your computer and use it in GitHub Desktop.
Katex hyper fun
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
### A Pluto.jl notebook ### | |
# v0.15.0 | |
using Markdown | |
using InteractiveUtils | |
# This Pluto notebook uses @bind for interactivity. When running this notebook outside of Pluto, the following 'mock version' of @bind gives bound variables a default value (instead of an error). | |
macro bind(def, element) | |
quote | |
local el = $(esc(element)) | |
global $(esc(def)) = Core.applicable(Base.get, el) ? Base.get(el) : missing | |
el | |
end | |
end | |
# ╔═╡ c7fd078d-862f-42a2-a515-221ed411b4fb | |
using PlutoUI | |
# ╔═╡ 899f5d16-7018-4297-9939-0a4b5eb30620 | |
using HypertextLiteral | |
# ╔═╡ 1a6943bf-ede7-4690-80de-e9ec23cc66da | |
katex_base_code = @htl(""" | |
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/katex.min.css" integrity="sha384-Um5gpz1odJg5Z4HAmzPtgZKdTBHZdw8S29IecapCSB31ligYPhHQZMIlWLYQGVoc" crossorigin="anonymous"> | |
<style> | |
.katex .base, | |
.katex .strut { | |
/*display: inline-flex !important;*/ | |
pointer-events: none; | |
} | |
.SlottedLaTeX { | |
font-size: .75em; | |
} | |
.SlottedLaTeX .slot { | |
pointer-events: initial; | |
} | |
</style> | |
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/katex.min.js" integrity="sha384-YNHdsYkH6gMx9y3mRkmcJ2mFUjTd0qNQQvY9VYZgQd7DcN7env35GzlmFaZ23JGp" crossorigin="anonymous"></script> | |
""") | |
# ╔═╡ b0ce1300-df3c-11eb-30ca-e7fee566afae | |
begin | |
Base.@kwdef struct SlottedLaTeX | |
parts::Vector{String} | |
slots::Vector{Any} | |
defined_commands::Vector=[] | |
used_commands::Vector=[] | |
used_commands_defs::Vector=[] | |
end | |
function Base.show(io::IO, m::MIME"text/html", sl::SlottedLaTeX) | |
defined_command_names = map(c -> c[1], sl.defined_commands) | |
commands_defs = join(map(comm -> "\\newcommand\\$(comm[1])$(comm[2])\n", | |
filter(x -> x[1] ∉ defined_command_names, collect(zip(sl.used_commands, sl.used_commands_defs))) | |
)) | |
parts = map(part -> "$commands_defs $part", sl.parts) | |
h = @htl(""" | |
$katex_base_code | |
<span class="SlottedLaTeX-slots" style="display: none;"> | |
$( | |
map(sl.slots) do s | |
@htl("<span class='slot'>$(s)</span>") | |
end | |
) | |
</span> | |
<script> | |
// https://unicode-table.com/en/#2800 | |
const braille_start = 10240 | |
// https://unicode-table.com/en/#03B1 | |
const greek_start = 945 | |
const placeholder = (i) => String.fromCodePoint(braille_start + i) | |
const placeholder_index = (s) => s.codePointAt(0) - braille_start | |
const k = (segments, ...slots) => { | |
const mock = [...slots.flatMap((_, i) => [segments[i], placeholder(i)]), segments[segments.length-1]].join("") | |
const el = html`<span class='SlottedLaTeX'></span>` | |
katex.render(mock, el, { | |
displayMode: currentScript.closest("p") == null, | |
}) | |
Array.from(el.querySelectorAll("span")).forEach(span => { | |
const t = span.innerText | |
if(t.length === 1) { | |
const i = placeholder_index(t) | |
if(0 <= i && i < slots.length) { | |
span.replaceWith(slots[i]) | |
} | |
} | |
}) | |
return el | |
} | |
const parts = $(parts) | |
console.log(parts) | |
const slots = Array.from(currentScript.previousElementSibling.children) | |
console.log(slots) | |
return k(parts, ...slots) | |
</script> | |
""") | |
Base.show(io, m, h) | |
end | |
end | |
# ╔═╡ aab42b5e-3d81-454f-97c7-e39bbb0ba425 | |
begin | |
latex_name(s) = Symbol("__LATEX_$s") | |
latex_name(vec::Vector) = latex_name(vec[1]) | |
end | |
# ╔═╡ 3edb1f40-3cb0-4ef9-a917-047fbdfc7c33 | |
begin | |
tex(s::String) = tex(Expr(:string, s)) #SlottedLaTeX(parts=[s], slots=[]) | |
function tex(ex::Expr) | |
@assert ex.head === :string "$(ex.head)" | |
if ex.args[1] isa String | |
parts = String[ex.args[1]] | |
slots = Any[] | |
else | |
parts = ["\\hspace{0pt}"] | |
slots = [ex.args[1]] | |
end | |
for x in ex.args[2:end] | |
if x isa String | |
all(==(' '), x) ? push!(parts, "\\hspace{0pt}") : push!(parts, x) | |
else | |
length(parts) != length(slots) + 1 && push!(parts, "\\hspace{0pt}") | |
push!(slots, x) | |
end | |
end | |
if length(slots) == length(parts) | |
push!(parts, "\\hspace{0pt}") | |
end | |
used_commands = collect(Iterators.flatten(map(parts) do part | |
matches = eachmatch(r"\\([a-zA-Z]+)", part) | |
map(match -> match.captures, matches) | |
end)) | |
defined_commands = collect(Iterators.flatten(map(parts) do part | |
matches = eachmatch(r"\\newcommand\\([a-zA-Z]+)(.+)\n", part) | |
map(match -> match.captures, matches) | |
end)) | |
defined_exprs = map(defined_commands) do command | |
Expr(:(=), esc(latex_name(command[1])), command[2]) | |
end | |
usage_name_symbols = filter(name -> isdefined(@__MODULE__, name[2]), map(x -> (x[1], latex_name(x[1])), used_commands)) | |
symbols = map(x -> x[2], usage_name_symbols) | |
names = map(x -> x[1], usage_name_symbols) | |
quote | |
$(defined_exprs...) | |
commands = [$(symbols...)] | |
SlottedLaTeX( | |
parts = $parts, | |
slots = [$(esc.(slots)...)], | |
defined_commands=$defined_commands, | |
used_commands=$names, | |
used_commands_defs=commands, | |
) | |
end | |
end | |
end | |
# ╔═╡ 4d007aa4-831f-4933-a96d-38e9ed3e8d46 | |
macro tex_str(s) | |
ex = Meta.parse("\"" * s * "\"") | |
tex(ex) | |
end | |
# ╔═╡ 3bbc1d6d-86c6-4ad8-b8a8-e2154bd11352 | |
macro tex(exp) | |
:(tex($exp)) | |
end | |
# ╔═╡ bc8a2c67-f944-4999-87c9-1cc17467cbe8 | |
jjj = tex""" | |
\\newcommand\\Par[1]{{ \\left( {#1} \\right] }} | |
\\frac {xxx}{\\Par{N} + $(@bind x Scrubbable(10))} | |
""" | |
# ╔═╡ c7581a61-f600-40f1-960f-127e9de2c2e8 | |
jjj | |
# ╔═╡ acaa17f6-9e56-4ed1-8545-ea2f0653f3b7 | |
tex""" | |
\\frac 1{\\Par{N}} | |
""" | |
# ╔═╡ 4c06d6b3-8802-4e48-b926-0512203e345e | |
__LATEX_Par | |
# ╔═╡ 00000000-0000-0000-0000-000000000001 | |
PLUTO_PROJECT_TOML_CONTENTS = """ | |
[compat] | |
HypertextLiteral = "~0.8.0" | |
PlutoUI = "~0.7.9" | |
[deps] | |
HypertextLiteral = "ac1192a8-f4b3-4bfe-ba22-af5b92cd3ab2" | |
PlutoUI = "7f904dfe-b85e-4ff6-b463-dae2292396a8" | |
""" | |
# ╔═╡ 00000000-0000-0000-0000-000000000002 | |
PLUTO_MANIFEST_TOML_CONTENTS = """ | |
# This file is machine-generated - editing it directly is not advised | |
[[Base64]] | |
uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" | |
[[Dates]] | |
deps = ["Printf"] | |
uuid = "ade2ca70-3891-5945-98fb-dc099432e06a" | |
[[HypertextLiteral]] | |
git-tree-sha1 = "1e3ccdc7a6f7b577623028e0095479f4727d8ec1" | |
uuid = "ac1192a8-f4b3-4bfe-ba22-af5b92cd3ab2" | |
version = "0.8.0" | |
[[InteractiveUtils]] | |
deps = ["Markdown"] | |
uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" | |
[[JSON]] | |
deps = ["Dates", "Mmap", "Parsers", "Unicode"] | |
git-tree-sha1 = "81690084b6198a2e1da36fcfda16eeca9f9f24e4" | |
uuid = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" | |
version = "0.21.1" | |
[[Logging]] | |
uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" | |
[[Markdown]] | |
deps = ["Base64"] | |
uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" | |
[[Mmap]] | |
uuid = "a63ad114-7e13-5084-954f-fe012c677804" | |
[[Parsers]] | |
deps = ["Dates"] | |
git-tree-sha1 = "c8abc88faa3f7a3950832ac5d6e690881590d6dc" | |
uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0" | |
version = "1.1.0" | |
[[PlutoUI]] | |
deps = ["Base64", "Dates", "InteractiveUtils", "JSON", "Logging", "Markdown", "Random", "Reexport", "Suppressor"] | |
git-tree-sha1 = "44e225d5837e2a2345e69a1d1e01ac2443ff9fcb" | |
uuid = "7f904dfe-b85e-4ff6-b463-dae2292396a8" | |
version = "0.7.9" | |
[[Printf]] | |
deps = ["Unicode"] | |
uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" | |
[[Random]] | |
deps = ["Serialization"] | |
uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" | |
[[Reexport]] | |
git-tree-sha1 = "5f6c21241f0f655da3952fd60aa18477cf96c220" | |
uuid = "189a3867-3050-52da-a836-e630ba90ab69" | |
version = "1.1.0" | |
[[Serialization]] | |
uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" | |
[[Suppressor]] | |
git-tree-sha1 = "a819d77f31f83e5792a76081eee1ea6342ab8787" | |
uuid = "fd094767-a336-5f1f-9728-57cf17d0bbfb" | |
version = "0.2.0" | |
[[Unicode]] | |
uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" | |
""" | |
# ╔═╡ Cell order: | |
# ╠═1a6943bf-ede7-4690-80de-e9ec23cc66da | |
# ╠═b0ce1300-df3c-11eb-30ca-e7fee566afae | |
# ╠═aab42b5e-3d81-454f-97c7-e39bbb0ba425 | |
# ╠═3edb1f40-3cb0-4ef9-a917-047fbdfc7c33 | |
# ╠═4d007aa4-831f-4933-a96d-38e9ed3e8d46 | |
# ╠═3bbc1d6d-86c6-4ad8-b8a8-e2154bd11352 | |
# ╠═bc8a2c67-f944-4999-87c9-1cc17467cbe8 | |
# ╠═c7581a61-f600-40f1-960f-127e9de2c2e8 | |
# ╠═acaa17f6-9e56-4ed1-8545-ea2f0653f3b7 | |
# ╠═4c06d6b3-8802-4e48-b926-0512203e345e | |
# ╠═c7fd078d-862f-42a2-a515-221ed411b4fb | |
# ╠═899f5d16-7018-4297-9939-0a4b5eb30620 | |
# ╟─00000000-0000-0000-0000-000000000001 | |
# ╟─00000000-0000-0000-0000-000000000002 |
This is awesome stuff!
May I ask what the justification is for having double backslashes everywhere within a tex_str
block? Is it not possible to parse escapes/tex commands with single backslashes?
This is caused by ex = Meta.parse("\"" * s * "\"")
to support interpolation of Julia objects.
Using the following should not require double backslashes:
macro tex_str(s)
tex(s)
end
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
use with pluto#1032
original source: https://github.com/fonsp/disorganised-mess/blob/main/katex%20fun.jl