Merge branch 'source_code_highlighting'
This commit is contained in:
commit
9f14534c10
160
Cargo.lock
generated
160
Cargo.lock
generated
@ -17,6 +17,15 @@ version = "2.0.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
|
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aho-corasick"
|
||||||
|
version = "1.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anstream"
|
name = "anstream"
|
||||||
version = "0.6.15"
|
version = "0.6.15"
|
||||||
@ -123,6 +132,15 @@ version = "1.7.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3"
|
checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cc"
|
||||||
|
version = "1.2.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c736e259eea577f443d5c86c304f9f4ae0295c43f3ba05c21f1d66b5f06001af"
|
||||||
|
dependencies = [
|
||||||
|
"shlex",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
@ -445,6 +463,10 @@ dependencies = [
|
|||||||
"serde_json",
|
"serde_json",
|
||||||
"tokio",
|
"tokio",
|
||||||
"toml",
|
"toml",
|
||||||
|
"tree-sitter-bash",
|
||||||
|
"tree-sitter-highlight",
|
||||||
|
"tree-sitter-nix",
|
||||||
|
"tree-sitter-python",
|
||||||
"url",
|
"url",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -517,9 +539,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.88"
|
version = "1.0.93"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7c3a7fc5db1e57d5a779a352c8cdb57b29aa4c40cc69c3a68a7fedc815fbf2f9"
|
checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
@ -539,6 +561,35 @@ version = "0.5.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "941ba9d78d8e2f7ce474c015eea4d9c6d25b6a3327f9832ee29a4de27f91bbb8"
|
checksum = "941ba9d78d8e2f7ce474c015eea4d9c6d25b6a3327f9832ee29a4de27f91bbb8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex"
|
||||||
|
version = "1.11.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-automata",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-automata"
|
||||||
|
version = "0.4.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-syntax"
|
||||||
|
version = "0.8.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc-demangle"
|
name = "rustc-demangle"
|
||||||
version = "0.1.24"
|
version = "0.1.24"
|
||||||
@ -582,10 +633,11 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.130"
|
version = "1.0.139"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "610f75ff4a8e3cb29b85da56eabdd1bff5b06739059a4b8e2967fef32e5d9944"
|
checksum = "44f86c3acccc9c65b153fe1b85a3be07fe5515274ec9f0653b4a0875731c72a6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"indexmap",
|
||||||
"itoa",
|
"itoa",
|
||||||
"memchr",
|
"memchr",
|
||||||
"ryu",
|
"ryu",
|
||||||
@ -601,6 +653,12 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "shlex"
|
||||||
|
version = "1.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "slab"
|
name = "slab"
|
||||||
version = "0.4.9"
|
version = "0.4.9"
|
||||||
@ -617,10 +675,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "streaming-iterator"
|
||||||
version = "2.0.79"
|
version = "0.1.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590"
|
checksum = "2b2231b7c3057d5e4ad0156fb3dc807d900806020c5ffa3ee6ff2c8c76fb8520"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.98"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -633,6 +697,26 @@ version = "1.0.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
|
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror"
|
||||||
|
version = "2.0.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror-impl",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror-impl"
|
||||||
|
version = "2.0.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tinyvec"
|
name = "tinyvec"
|
||||||
version = "1.8.0"
|
version = "1.8.0"
|
||||||
@ -693,6 +777,68 @@ dependencies = [
|
|||||||
"winnow",
|
"winnow",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tree-sitter"
|
||||||
|
version = "0.25.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5168a515fe492af54c5cc8800ff8c840be09fa5168de45838afaecd3e008bce4"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"regex",
|
||||||
|
"regex-syntax",
|
||||||
|
"serde_json",
|
||||||
|
"streaming-iterator",
|
||||||
|
"tree-sitter-language",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tree-sitter-bash"
|
||||||
|
version = "0.23.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "329a4d48623ac337d42b1df84e81a1c9dbb2946907c102ca72db158c1964a52e"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"tree-sitter-language",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tree-sitter-highlight"
|
||||||
|
version = "0.25.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "457164f56e8dbbd0dc620c239bd7e2eb6025b76e4d1593a690bd4d9ed37bf168"
|
||||||
|
dependencies = [
|
||||||
|
"regex",
|
||||||
|
"streaming-iterator",
|
||||||
|
"thiserror",
|
||||||
|
"tree-sitter",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tree-sitter-language"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c4013970217383f67b18aef68f6fb2e8d409bc5755227092d32efb0422ba24b8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tree-sitter-nix"
|
||||||
|
version = "0.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3a8d4f8705d377d63242a075331d2d8c1dcc9828fd74aa13d7145185b3d9c004"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"tree-sitter-language",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tree-sitter-python"
|
||||||
|
version = "0.23.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3d065aaa27f3aaceaf60c1f0e0ac09e1cb9eb8ed28e7bcdaa52129cffc7f4b04"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"tree-sitter-language",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-bidi"
|
name = "unicode-bidi"
|
||||||
version = "0.3.17"
|
version = "0.3.17"
|
||||||
|
@ -31,6 +31,10 @@ serde = { version = "1.0.189", default-features = false, features = ["std", "der
|
|||||||
serde_json = "1.0.107"
|
serde_json = "1.0.107"
|
||||||
tokio = { version = "1.30.0", default-features = false, features = ["rt", "rt-multi-thread", "fs", "io-util"] }
|
tokio = { version = "1.30.0", default-features = false, features = ["rt", "rt-multi-thread", "fs", "io-util"] }
|
||||||
toml = "0.8.2"
|
toml = "0.8.2"
|
||||||
|
tree-sitter-bash = "0.23.3"
|
||||||
|
tree-sitter-highlight = "0.25.2"
|
||||||
|
tree-sitter-nix = "0.0.2"
|
||||||
|
tree-sitter-python = "0.23.6"
|
||||||
url = "2.5.0"
|
url = "2.5.0"
|
||||||
|
|
||||||
# Optimized build for any sort of release.
|
# Optimized build for any sort of release.
|
||||||
|
18
TODO.org
Normal file
18
TODO.org
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
* Things to do [4/14]
|
||||||
|
** DONE If the paragraph only contains an image, text-align center
|
||||||
|
** DONE Syntax highlighting for code blocks
|
||||||
|
** TODO Render gnuplot
|
||||||
|
** TODO Pretty-print the timestamps
|
||||||
|
** TODO Support Table of Contents
|
||||||
|
** TODO Support line numbers in code blocks
|
||||||
|
** TODO Support references to code block lines
|
||||||
|
** TODO Only include text up to first heading on homepage and include a "read more" link
|
||||||
|
** DONE Make loading language-specific CSS files conditional on the presence of src blocks using those languages
|
||||||
|
** TODO Set up tracing so I can use warning and such
|
||||||
|
** TODO Make copying of language-specific CSS files conditional on the presence of src blocks using those languages
|
||||||
|
** TODO Switch to an entirely lazily-evaluated output tree
|
||||||
|
** TODO Add highlighting for languages [1/2]
|
||||||
|
*** DONE bash
|
||||||
|
*** TODO gnuplot
|
||||||
|
https://github.com/dpezto/tree-sitter-gnuplot is not on crates.io so I'd have to add a git dependency to use it. This would prevent publishing this crate to crates.io.
|
||||||
|
** DONE Bug: carry over highlight starts when breaking lines
|
39
default_environment/stylesheet/language_bash.css
Normal file
39
default_environment/stylesheet/language_bash.css
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
:root {
|
||||||
|
--srclg-bash-srchl-comment-color: #048a81;
|
||||||
|
--srclg-bash-srchl-function-color: #e95a62;
|
||||||
|
--srclg-bash-srchl-keyword-color: #1a936f;
|
||||||
|
--srclg-bash-srchl-property-color: inherit;
|
||||||
|
--srclg-bash-srchl-string-color: #ecc30b;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: light) {
|
||||||
|
:root {
|
||||||
|
--srclg-bash-srchl-comment-color: #fb757e;
|
||||||
|
--srclg-bash-srchl-function-color: #16a59d;
|
||||||
|
--srclg-bash-srchl-keyword-color: #e56c90;
|
||||||
|
--srclg-bash-srchl-property-color: inherit;
|
||||||
|
--srclg-bash-srchl-string-color: #133cf4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.main_content {
|
||||||
|
.src_block {
|
||||||
|
&.srclg_bash {
|
||||||
|
.srchl_comment {
|
||||||
|
color: var(--srclg-bash-srchl-comment-color);
|
||||||
|
}
|
||||||
|
.srchl_function {
|
||||||
|
color: var(--srclg-bash-srchl-function-color);
|
||||||
|
}
|
||||||
|
.srchl_keyword {
|
||||||
|
color: var(--srclg-bash-srchl-keyword-color);
|
||||||
|
}
|
||||||
|
.srchl_property {
|
||||||
|
color: var(--srclg-bash-srchl-property-color);
|
||||||
|
}
|
||||||
|
.srchl_string {
|
||||||
|
color: var(--srclg-bash-srchl-string-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
39
default_environment/stylesheet/language_nix.css
Normal file
39
default_environment/stylesheet/language_nix.css
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
:root {
|
||||||
|
--srclg-nix-srchl-keyword-color: #1a936f;
|
||||||
|
--srclg-nix-srchl-comment-color: #048a81;
|
||||||
|
--srclg-nix-srchl-property-color: #bfbccb;
|
||||||
|
--srclg-nix-srchl-string-color: #ecc30b;
|
||||||
|
--srclg-nix-srchl-string-special-path-color: #067bc2;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: light) {
|
||||||
|
:root {
|
||||||
|
--srclg-nix-srchl-keyword-color: #e56c90;
|
||||||
|
--srclg-nix-srchl-comment-color: #fb757e;
|
||||||
|
--srclg-nix-srchl-property-color: #404334;
|
||||||
|
--srclg-nix-srchl-string-color: #133cf4;
|
||||||
|
--srclg-nix-srchl-string-special-path-color: #f9843d;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.main_content {
|
||||||
|
.src_block {
|
||||||
|
&.srclg_nix {
|
||||||
|
.srchl_keyword {
|
||||||
|
color: var(--srclg-nix-srchl-keyword-color);
|
||||||
|
}
|
||||||
|
.srchl_comment {
|
||||||
|
color: var(--srclg-nix-srchl-comment-color);
|
||||||
|
}
|
||||||
|
.srchl_property {
|
||||||
|
color: var(--srclg-nix-srchl-property-color);
|
||||||
|
}
|
||||||
|
.srchl_string {
|
||||||
|
color: var(--srclg-nix-srchl-string-color);
|
||||||
|
}
|
||||||
|
.srchl_string_special_path {
|
||||||
|
color: var(--srclg-nix-srchl-string-special-path-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
51
default_environment/stylesheet/language_python.css
Normal file
51
default_environment/stylesheet/language_python.css
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
/* ea912c */
|
||||||
|
/* e95a62 */
|
||||||
|
:root {
|
||||||
|
--srclg-python-srchl-comment-color: #048a81;
|
||||||
|
--srclg-python-srchl-function-builtin-color: #e95a62;
|
||||||
|
--srclg-python-srchl-keyword-color: #1a936f;
|
||||||
|
--srclg-python-srchl-property-color: inherit;
|
||||||
|
--srclg-python-srchl-string-color: #ecc30b;
|
||||||
|
--srclg-python-srchl-type-color: #067bc2;
|
||||||
|
--srclg-python-srchl-variable-color: #ea912c;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: light) {
|
||||||
|
:root {
|
||||||
|
--srclg-python-srchl-comment-color: #fb757e;
|
||||||
|
--srclg-python-srchl-function-builtin-color: #16a59d;
|
||||||
|
--srclg-python-srchl-keyword-color: #e56c90;
|
||||||
|
--srclg-python-srchl-property-color: inherit;
|
||||||
|
--srclg-python-srchl-string-color: #133cf4;
|
||||||
|
--srclg-python-srchl-type-color: #f9843d;
|
||||||
|
--srclg-python-srchl-variable-color: #156ed3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.main_content {
|
||||||
|
.src_block {
|
||||||
|
&.srclg_python {
|
||||||
|
.srchl_comment {
|
||||||
|
color: var(--srclg-python-srchl-comment-color);
|
||||||
|
}
|
||||||
|
.srchl_function_builtin {
|
||||||
|
color: var(--srclg-python-srchl-function-builtin-color);
|
||||||
|
}
|
||||||
|
.srchl_keyword {
|
||||||
|
color: var(--srclg-python-srchl-keyword-color);
|
||||||
|
}
|
||||||
|
.srchl_property {
|
||||||
|
color: var(--srclg-python-srchl-property-color);
|
||||||
|
}
|
||||||
|
.srchl_string {
|
||||||
|
color: var(--srclg-python-srchl-string-color);
|
||||||
|
}
|
||||||
|
.srchl_type {
|
||||||
|
color: var(--srclg-python-srchl-type-color);
|
||||||
|
}
|
||||||
|
.srchl_variable {
|
||||||
|
color: var(--srclg-python-srchl-variable-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -9,9 +9,8 @@
|
|||||||
|
|
||||||
--blog-post-background-color: #0a0a0a;
|
--blog-post-background-color: #0a0a0a;
|
||||||
|
|
||||||
--src-font-family:
|
--src-font-family: ui-monospace, "Cascadia Code", "Source Code Pro", Menlo,
|
||||||
ui-monospace, "Cascadia Code", "Source Code Pro", Menlo, Consolas,
|
Consolas, "DejaVu Sans Mono", monospace;
|
||||||
"DejaVu Sans Mono", monospace;
|
|
||||||
|
|
||||||
--src-block-background-color: #141414;
|
--src-block-background-color: #141414;
|
||||||
--src-block-border-color: #84828f;
|
--src-block-border-color: #84828f;
|
||||||
@ -56,9 +55,8 @@
|
|||||||
body {
|
body {
|
||||||
color: var(--site-text-color);
|
color: var(--site-text-color);
|
||||||
background-color: var(--site-background-color);
|
background-color: var(--site-background-color);
|
||||||
font-family:
|
font-family: source-sans-pro, Seravek, "Gill Sans Nova", Ubuntu, Calibri,
|
||||||
source-sans-pro, Seravek, "Gill Sans Nova", Ubuntu, Calibri, "DejaVu Sans",
|
"DejaVu Sans", sans-serif;
|
||||||
sans-serif;
|
|
||||||
|
|
||||||
a:link,
|
a:link,
|
||||||
a:visited {
|
a:visited {
|
||||||
@ -161,6 +159,7 @@ body {
|
|||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
font-family: var(--src-font-family);
|
font-family: var(--src-font-family);
|
||||||
margin: 1rem 0;
|
margin: 1rem 0;
|
||||||
|
font-variant-ligatures: none;
|
||||||
|
|
||||||
.src_language {
|
.src_language {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
@ -185,11 +184,13 @@ body {
|
|||||||
.inline_source_block {
|
.inline_source_block {
|
||||||
font-family: var(--src-font-family);
|
font-family: var(--src-font-family);
|
||||||
font-size: 1.2rem;
|
font-size: 1.2rem;
|
||||||
|
font-variant-ligatures: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.code,
|
.code,
|
||||||
.verbatim {
|
.verbatim {
|
||||||
font-family: var(--src-font-family);
|
font-family: var(--src-font-family);
|
||||||
|
font-variant-ligatures: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.quote_block {
|
.quote_block {
|
||||||
|
@ -1,10 +1,15 @@
|
|||||||
<div class="src_block">
|
<div class="src_block{?.language} srclg_{.language}{/.language}">
|
||||||
{?.language}<div class="src_language">{.language}</div>{/.language}
|
{?.language}<div class="src_language">{.language}</div>{/.language}
|
||||||
<table class="src_body">
|
<table class="src_body">
|
||||||
<tbody>
|
<tbody>
|
||||||
{#.lines}
|
{#.lines}
|
||||||
<tr>
|
<tr>
|
||||||
<td><code class="src_line">{.}</code></td>
|
<td>{#.children}{@select key=.type}
|
||||||
|
{@eq value="raw_text"}<code class="src_line">{.content}</code>{/eq}
|
||||||
|
{@eq value="highlight_start"}<span class="srchl_{.name}">{/eq}
|
||||||
|
{@eq value="highlight_end"}</span>{/eq}
|
||||||
|
{@none}{!TODO: make this panic!}ERROR: Unrecognized type {.type}.{/none}
|
||||||
|
{/select}{/.children}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{/.lines}
|
{/.lines}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
use super::render_context::RenderContext;
|
use super::render_context::RenderContext;
|
||||||
@ -51,28 +53,6 @@ render!(
|
|||||||
render_context,
|
render_context,
|
||||||
{
|
{
|
||||||
push_file!(render_context, &original.page.src, {
|
push_file!(render_context, &original.page.src, {
|
||||||
let css_files = vec![
|
|
||||||
get_web_path(
|
|
||||||
render_context.config,
|
|
||||||
render_context.output_root_directory,
|
|
||||||
render_context.output_file,
|
|
||||||
"stylesheet/reset.css",
|
|
||||||
)?,
|
|
||||||
get_web_path(
|
|
||||||
render_context.config,
|
|
||||||
render_context.output_root_directory,
|
|
||||||
render_context.output_file,
|
|
||||||
"stylesheet/main.css",
|
|
||||||
)?,
|
|
||||||
];
|
|
||||||
let js_files = vec![get_web_path(
|
|
||||||
render_context.config,
|
|
||||||
render_context.output_root_directory,
|
|
||||||
render_context.output_file,
|
|
||||||
"blog_post.js",
|
|
||||||
)?];
|
|
||||||
let global_settings =
|
|
||||||
GlobalSettings::new(original.page.title.clone(), css_files, js_files);
|
|
||||||
let page_header = PageHeader::new(
|
let page_header = PageHeader::new(
|
||||||
render_context.config.get_site_title().map(str::to_string),
|
render_context.config.get_site_title().map(str::to_string),
|
||||||
Some(get_web_path(
|
Some(get_web_path(
|
||||||
@ -114,6 +94,46 @@ render!(
|
|||||||
ret
|
ret
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mut css_files = vec![
|
||||||
|
get_web_path(
|
||||||
|
render_context.config,
|
||||||
|
render_context.output_root_directory,
|
||||||
|
render_context.output_file,
|
||||||
|
"stylesheet/reset.css",
|
||||||
|
)?,
|
||||||
|
get_web_path(
|
||||||
|
render_context.config,
|
||||||
|
render_context.output_root_directory,
|
||||||
|
render_context.output_file,
|
||||||
|
"stylesheet/main.css",
|
||||||
|
)?,
|
||||||
|
];
|
||||||
|
let additional_css_files = render_context
|
||||||
|
.dependency_manager
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.list_css()?
|
||||||
|
.map(|css_name| {
|
||||||
|
get_web_path(
|
||||||
|
render_context.config,
|
||||||
|
render_context.output_root_directory,
|
||||||
|
render_context.output_file,
|
||||||
|
format!("stylesheet/{}", css_name),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect::<Result<HashSet<_>, _>>()?;
|
||||||
|
css_files.extend(additional_css_files.into_iter());
|
||||||
|
|
||||||
|
let js_files = vec![get_web_path(
|
||||||
|
render_context.config,
|
||||||
|
render_context.output_root_directory,
|
||||||
|
render_context.output_file,
|
||||||
|
"blog_post.js",
|
||||||
|
)?];
|
||||||
|
|
||||||
|
let global_settings =
|
||||||
|
GlobalSettings::new(original.page.title.clone(), css_files, js_files);
|
||||||
|
|
||||||
let ret = RenderBlogPostPage {
|
let ret = RenderBlogPostPage {
|
||||||
global_settings,
|
global_settings,
|
||||||
page_header: Some(page_header),
|
page_header: Some(page_header),
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
use super::macros::render;
|
use super::macros::render;
|
||||||
@ -49,31 +51,6 @@ render!(
|
|||||||
original,
|
original,
|
||||||
render_context,
|
render_context,
|
||||||
{
|
{
|
||||||
let css_files = vec![
|
|
||||||
get_web_path(
|
|
||||||
render_context.config,
|
|
||||||
render_context.output_root_directory,
|
|
||||||
render_context.output_file,
|
|
||||||
"stylesheet/reset.css",
|
|
||||||
)?,
|
|
||||||
get_web_path(
|
|
||||||
render_context.config,
|
|
||||||
render_context.output_root_directory,
|
|
||||||
render_context.output_file,
|
|
||||||
"stylesheet/main.css",
|
|
||||||
)?,
|
|
||||||
];
|
|
||||||
let js_files = vec![get_web_path(
|
|
||||||
render_context.config,
|
|
||||||
render_context.output_root_directory,
|
|
||||||
render_context.output_file,
|
|
||||||
"blog_post.js",
|
|
||||||
)?];
|
|
||||||
let global_settings = GlobalSettings::new(
|
|
||||||
render_context.config.get_site_title().map(str::to_string),
|
|
||||||
css_files,
|
|
||||||
js_files,
|
|
||||||
);
|
|
||||||
let page_header = PageHeader::new(
|
let page_header = PageHeader::new(
|
||||||
render_context.config.get_site_title().map(str::to_string),
|
render_context.config.get_site_title().map(str::to_string),
|
||||||
Some(get_web_path(
|
Some(get_web_path(
|
||||||
@ -105,6 +82,49 @@ render!(
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mut css_files = vec![
|
||||||
|
get_web_path(
|
||||||
|
render_context.config,
|
||||||
|
render_context.output_root_directory,
|
||||||
|
render_context.output_file,
|
||||||
|
"stylesheet/reset.css",
|
||||||
|
)?,
|
||||||
|
get_web_path(
|
||||||
|
render_context.config,
|
||||||
|
render_context.output_root_directory,
|
||||||
|
render_context.output_file,
|
||||||
|
"stylesheet/main.css",
|
||||||
|
)?,
|
||||||
|
];
|
||||||
|
let additional_css_files = render_context
|
||||||
|
.dependency_manager
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.list_css()?
|
||||||
|
.map(|css_name| {
|
||||||
|
get_web_path(
|
||||||
|
render_context.config,
|
||||||
|
render_context.output_root_directory,
|
||||||
|
render_context.output_file,
|
||||||
|
format!("stylesheet/{}", css_name),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect::<Result<HashSet<_>, _>>()?;
|
||||||
|
css_files.extend(additional_css_files.into_iter());
|
||||||
|
|
||||||
|
let js_files = vec![get_web_path(
|
||||||
|
render_context.config,
|
||||||
|
render_context.output_root_directory,
|
||||||
|
render_context.output_file,
|
||||||
|
"blog_post.js",
|
||||||
|
)?];
|
||||||
|
|
||||||
|
let global_settings = GlobalSettings::new(
|
||||||
|
render_context.config.get_site_title().map(str::to_string),
|
||||||
|
css_files,
|
||||||
|
js_files,
|
||||||
|
);
|
||||||
|
|
||||||
Ok(RenderBlogStream {
|
Ok(RenderBlogStream {
|
||||||
global_settings,
|
global_settings,
|
||||||
page_header: Some(page_header),
|
page_header: Some(page_header),
|
||||||
|
@ -7,6 +7,7 @@ use super::RenderContext;
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) enum Dependency {
|
pub(crate) enum Dependency {
|
||||||
StaticFile { absolute_path: PathBuf },
|
StaticFile { absolute_path: PathBuf },
|
||||||
|
CssFile { name: String },
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Dependency {
|
impl Dependency {
|
||||||
@ -40,6 +41,11 @@ impl Dependency {
|
|||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
Dependency::CssFile { name: _ } => {
|
||||||
|
// We don't do anything because the CSS files are already copied into the output from natter's default environment.
|
||||||
|
// TODO: When we add support for CSS outside the default environment, we should add support for dynamically picking which ones to copy here.
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -65,4 +65,20 @@ impl DependencyManager {
|
|||||||
std::mem::swap(&mut self.dependencies, &mut dependencies);
|
std::mem::swap(&mut self.dependencies, &mut dependencies);
|
||||||
dependencies
|
dependencies
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn include_css<N>(&mut self, name: N) -> Result<(), CustomError>
|
||||||
|
where
|
||||||
|
std::string::String: From<N>,
|
||||||
|
{
|
||||||
|
self.dependencies
|
||||||
|
.push(Dependency::CssFile { name: name.into() });
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn list_css(&self) -> Result<impl Iterator<Item = &String>, CustomError> {
|
||||||
|
Ok(self.dependencies.iter().filter_map(|dep| match dep {
|
||||||
|
Dependency::CssFile { name } => Some(name),
|
||||||
|
_ => None,
|
||||||
|
}))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
use super::footnote_definition::RenderRealFootnoteDefinition;
|
use super::footnote_definition::RenderRealFootnoteDefinition;
|
||||||
use super::macros::render;
|
use super::macros::render;
|
||||||
use super::render_context::RenderContext;
|
use super::render_context::RenderContext;
|
||||||
@ -30,27 +32,6 @@ pub(crate) struct RenderPage {
|
|||||||
|
|
||||||
render!(RenderPage, IPage, original, render_context, {
|
render!(RenderPage, IPage, original, render_context, {
|
||||||
push_file!(render_context, &original.src, {
|
push_file!(render_context, &original.src, {
|
||||||
let css_files = vec![
|
|
||||||
get_web_path(
|
|
||||||
render_context.config,
|
|
||||||
render_context.output_root_directory,
|
|
||||||
render_context.output_file,
|
|
||||||
"stylesheet/reset.css",
|
|
||||||
)?,
|
|
||||||
get_web_path(
|
|
||||||
render_context.config,
|
|
||||||
render_context.output_root_directory,
|
|
||||||
render_context.output_file,
|
|
||||||
"stylesheet/main.css",
|
|
||||||
)?,
|
|
||||||
];
|
|
||||||
let js_files = vec![get_web_path(
|
|
||||||
render_context.config,
|
|
||||||
render_context.output_root_directory,
|
|
||||||
render_context.output_file,
|
|
||||||
"blog_post.js",
|
|
||||||
)?];
|
|
||||||
let global_settings = GlobalSettings::new(original.title.clone(), css_files, js_files);
|
|
||||||
let page_header = PageHeader::new(
|
let page_header = PageHeader::new(
|
||||||
render_context.config.get_site_title().map(str::to_string),
|
render_context.config.get_site_title().map(str::to_string),
|
||||||
Some(get_web_path(
|
Some(get_web_path(
|
||||||
@ -92,6 +73,45 @@ render!(RenderPage, IPage, original, render_context, {
|
|||||||
ret
|
ret
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mut css_files = vec![
|
||||||
|
get_web_path(
|
||||||
|
render_context.config,
|
||||||
|
render_context.output_root_directory,
|
||||||
|
render_context.output_file,
|
||||||
|
"stylesheet/reset.css",
|
||||||
|
)?,
|
||||||
|
get_web_path(
|
||||||
|
render_context.config,
|
||||||
|
render_context.output_root_directory,
|
||||||
|
render_context.output_file,
|
||||||
|
"stylesheet/main.css",
|
||||||
|
)?,
|
||||||
|
];
|
||||||
|
let additional_css_files = render_context
|
||||||
|
.dependency_manager
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.list_css()?
|
||||||
|
.map(|css_name| {
|
||||||
|
get_web_path(
|
||||||
|
render_context.config,
|
||||||
|
render_context.output_root_directory,
|
||||||
|
render_context.output_file,
|
||||||
|
format!("stylesheet/{}", css_name),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect::<Result<HashSet<_>, _>>()?;
|
||||||
|
css_files.extend(additional_css_files.into_iter());
|
||||||
|
|
||||||
|
let js_files = vec![get_web_path(
|
||||||
|
render_context.config,
|
||||||
|
render_context.output_root_directory,
|
||||||
|
render_context.output_file,
|
||||||
|
"blog_post.js",
|
||||||
|
)?];
|
||||||
|
|
||||||
|
let global_settings = GlobalSettings::new(original.title.clone(), css_files, js_files);
|
||||||
|
|
||||||
let ret = RenderPage {
|
let ret = RenderPage {
|
||||||
global_settings,
|
global_settings,
|
||||||
page_header: Some(page_header),
|
page_header: Some(page_header),
|
||||||
|
@ -3,6 +3,7 @@ use serde::Serialize;
|
|||||||
use super::render_context::RenderContext;
|
use super::render_context::RenderContext;
|
||||||
use crate::error::CustomError;
|
use crate::error::CustomError;
|
||||||
use crate::intermediate::ISrcBlock;
|
use crate::intermediate::ISrcBlock;
|
||||||
|
use crate::intermediate::ISrcSegment;
|
||||||
|
|
||||||
use super::macros::render;
|
use super::macros::render;
|
||||||
|
|
||||||
@ -10,15 +11,85 @@ use super::macros::render;
|
|||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
#[serde(rename = "src_block")]
|
#[serde(rename = "src_block")]
|
||||||
pub(crate) struct RenderSrcBlock {
|
pub(crate) struct RenderSrcBlock {
|
||||||
lines: Vec<String>,
|
lines: Vec<RenderSrcLine>,
|
||||||
language: Option<String>,
|
language: Option<String>,
|
||||||
post_blank: organic::types::PostBlank,
|
post_blank: organic::types::PostBlank,
|
||||||
}
|
}
|
||||||
|
|
||||||
render!(RenderSrcBlock, ISrcBlock, original, _render_context, {
|
#[derive(Debug, Serialize)]
|
||||||
|
pub(crate) struct RenderSrcLine {
|
||||||
|
children: Vec<RenderSrcSegment>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
#[serde(tag = "type")]
|
||||||
|
pub(crate) enum RenderSrcSegment {
|
||||||
|
#[serde(rename = "raw_text")]
|
||||||
|
RawText { content: String },
|
||||||
|
|
||||||
|
#[serde(rename = "highlight_start")]
|
||||||
|
HighlightStart { name: String },
|
||||||
|
|
||||||
|
#[serde(rename = "highlight_end")]
|
||||||
|
HighlightEnd,
|
||||||
|
}
|
||||||
|
|
||||||
|
render!(RenderSrcBlock, ISrcBlock, original, render_context, {
|
||||||
|
let lines = original
|
||||||
|
.lines
|
||||||
|
.iter()
|
||||||
|
.map(|original_line| {
|
||||||
|
let children = original_line
|
||||||
|
.children
|
||||||
|
.iter()
|
||||||
|
.map(|original_segment| match original_segment {
|
||||||
|
ISrcSegment::RawText(body) => RenderSrcSegment::RawText {
|
||||||
|
content: body.to_owned(),
|
||||||
|
},
|
||||||
|
ISrcSegment::HighlightStart { name } => RenderSrcSegment::HighlightStart {
|
||||||
|
name: css_safe_name(name),
|
||||||
|
},
|
||||||
|
ISrcSegment::HighlightEnd => RenderSrcSegment::HighlightEnd,
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
RenderSrcLine { children }
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
match original.language.as_deref() {
|
||||||
|
Some("bash") => {
|
||||||
|
render_context
|
||||||
|
.dependency_manager
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.include_css("language_bash.css")?;
|
||||||
|
}
|
||||||
|
Some("nix") => {
|
||||||
|
render_context
|
||||||
|
.dependency_manager
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.include_css("language_nix.css")?;
|
||||||
|
}
|
||||||
|
Some("python") => {
|
||||||
|
render_context
|
||||||
|
.dependency_manager
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.include_css("language_python.css")?;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
};
|
||||||
Ok(RenderSrcBlock {
|
Ok(RenderSrcBlock {
|
||||||
lines: original.lines.clone(),
|
lines,
|
||||||
language: original.language.clone(),
|
language: original.language.clone(),
|
||||||
post_blank: original.post_blank,
|
post_blank: original.post_blank,
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
fn css_safe_name<S>(inp: S) -> String
|
||||||
|
where
|
||||||
|
std::string::String: From<S>,
|
||||||
|
{
|
||||||
|
let inp: String = inp.into();
|
||||||
|
inp.replace(".", "_")
|
||||||
|
}
|
||||||
|
@ -32,6 +32,7 @@ pub(crate) struct IRealFootnoteDefinition {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl IRealFootnoteDefinition {
|
impl IRealFootnoteDefinition {
|
||||||
|
#[allow(clippy::needless_lifetimes)]
|
||||||
pub(crate) async fn new<'orig, 'parse>(
|
pub(crate) async fn new<'orig, 'parse>(
|
||||||
_intermediate_context: IntermediateContext<'orig, 'parse>,
|
_intermediate_context: IntermediateContext<'orig, 'parse>,
|
||||||
footnote_id: usize,
|
footnote_id: usize,
|
||||||
|
@ -9,6 +9,7 @@ macro_rules! inoop {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl $istruct {
|
impl $istruct {
|
||||||
|
#[allow(clippy::extra_unused_lifetimes)]
|
||||||
pub(crate) async fn new<'reg, 'orig, 'parse>(
|
pub(crate) async fn new<'reg, 'orig, 'parse>(
|
||||||
_intermediate_context: crate::intermediate::IntermediateContext<'orig, 'parse>,
|
_intermediate_context: crate::intermediate::IntermediateContext<'orig, 'parse>,
|
||||||
original: &'orig organic::types::$pstruct<'parse>,
|
original: &'orig organic::types::$pstruct<'parse>,
|
||||||
|
@ -127,6 +127,7 @@ pub(crate) use regular_link::LinkTarget;
|
|||||||
pub(crate) use section::ISection;
|
pub(crate) use section::ISection;
|
||||||
pub(crate) use special_block::ISpecialBlock;
|
pub(crate) use special_block::ISpecialBlock;
|
||||||
pub(crate) use src_block::ISrcBlock;
|
pub(crate) use src_block::ISrcBlock;
|
||||||
|
pub(crate) use src_block::ISrcSegment;
|
||||||
pub(crate) use statistics_cookie::IStatisticsCookie;
|
pub(crate) use statistics_cookie::IStatisticsCookie;
|
||||||
pub(crate) use strike_through::IStrikeThrough;
|
pub(crate) use strike_through::IStrikeThrough;
|
||||||
pub(crate) use subscript::ISubscript;
|
pub(crate) use subscript::ISubscript;
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
use std::any::Any;
|
|
||||||
|
|
||||||
use super::macros::intermediate;
|
use super::macros::intermediate;
|
||||||
use super::IObject;
|
use super::IObject;
|
||||||
use crate::error::CustomError;
|
use crate::error::CustomError;
|
||||||
@ -33,6 +31,7 @@ intermediate!(
|
|||||||
);
|
);
|
||||||
|
|
||||||
impl IParagraph {
|
impl IParagraph {
|
||||||
|
#[allow(clippy::needless_lifetimes)]
|
||||||
pub(crate) async fn artificial<'orig, 'parse>(
|
pub(crate) async fn artificial<'orig, 'parse>(
|
||||||
_intermediate_context: crate::intermediate::IntermediateContext<'orig, 'parse>,
|
_intermediate_context: crate::intermediate::IntermediateContext<'orig, 'parse>,
|
||||||
children: Vec<IObject>,
|
children: Vec<IObject>,
|
||||||
@ -52,19 +51,19 @@ impl IParagraph {
|
|||||||
.children
|
.children
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|c| match c {
|
.filter(|c| match c {
|
||||||
IObject::RegularLink(iregular_link) => match &iregular_link.target {
|
IObject::RegularLink(iregular_link) => matches!(
|
||||||
super::LinkTarget::Image { src, alt } => true,
|
&iregular_link.target,
|
||||||
_ => false,
|
super::LinkTarget::Image { src: _, alt: _ }
|
||||||
},
|
),
|
||||||
_ => false,
|
_ => false,
|
||||||
})
|
})
|
||||||
.count();
|
.count();
|
||||||
num_images == 1
|
num_images == 1
|
||||||
&& self.children.iter().all(|c| match c {
|
&& self.children.iter().all(|c| match c {
|
||||||
IObject::RegularLink(iregular_link) => match &iregular_link.target {
|
IObject::RegularLink(iregular_link) => matches!(
|
||||||
super::LinkTarget::Image { src, alt } => true,
|
&iregular_link.target,
|
||||||
_ => false,
|
super::LinkTarget::Image { src: _, alt: _ }
|
||||||
},
|
),
|
||||||
IObject::PlainText(iplain_text) => {
|
IObject::PlainText(iplain_text) => {
|
||||||
iplain_text.source.chars().all(|c| c.is_ascii_whitespace())
|
iplain_text.source.chars().all(|c| c.is_ascii_whitespace())
|
||||||
}
|
}
|
||||||
|
@ -180,6 +180,7 @@ async fn convert_definition_contents<'orig, 'parse>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Take a footnote definition that has not yet received a reference and move it into the active footnotes.
|
/// Take a footnote definition that has not yet received a reference and move it into the active footnotes.
|
||||||
|
#[allow(clippy::needless_lifetimes)]
|
||||||
pub(crate) async fn promote_footnote_definition<'orig, 'parse>(
|
pub(crate) async fn promote_footnote_definition<'orig, 'parse>(
|
||||||
intermediate_context: IntermediateContext<'orig, 'parse>,
|
intermediate_context: IntermediateContext<'orig, 'parse>,
|
||||||
label: &'parse str,
|
label: &'parse str,
|
||||||
|
@ -186,7 +186,7 @@ impl LinkTarget {
|
|||||||
if path.is_absolute() {
|
if path.is_absolute() {
|
||||||
return Ok(format!("file://{}", input));
|
return Ok(format!("file://{}", input));
|
||||||
}
|
}
|
||||||
return Ok(input.into_owned());
|
Ok(input.into_owned())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get file name from the last segment of an image path.
|
/// Get file name from the last segment of an image path.
|
||||||
@ -199,7 +199,7 @@ impl LinkTarget {
|
|||||||
let path = Path::new(input.as_ref());
|
let path = Path::new(input.as_ref());
|
||||||
match path
|
match path
|
||||||
.components()
|
.components()
|
||||||
.last()
|
.next_back()
|
||||||
.ok_or("Images should have at least one component in their path.")?
|
.ok_or("Images should have at least one component in their path.")?
|
||||||
{
|
{
|
||||||
std::path::Component::Prefix(_) => {
|
std::path::Component::Prefix(_) => {
|
||||||
@ -209,9 +209,7 @@ impl LinkTarget {
|
|||||||
std::path::Component::RootDir
|
std::path::Component::RootDir
|
||||||
| std::path::Component::CurDir
|
| std::path::Component::CurDir
|
||||||
| std::path::Component::ParentDir => {
|
| std::path::Component::ParentDir => {
|
||||||
return Err(
|
Err("Final component of an image path should be a normal component.".into())
|
||||||
"Final component of an image path should be a normal component.".into(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
std::path::Component::Normal(file_name) => Ok(file_name
|
std::path::Component::Normal(file_name) => Ok(file_name
|
||||||
.to_str()
|
.to_str()
|
||||||
@ -236,6 +234,7 @@ mod tests {
|
|||||||
let registry = Registry::new();
|
let registry = Registry::new();
|
||||||
let registry = Arc::new(Mutex::new(registry));
|
let registry = Arc::new(Mutex::new(registry));
|
||||||
let intermediate_context = IntermediateContext::new(registry)?;
|
let intermediate_context = IntermediateContext::new(registry)?;
|
||||||
|
#[allow(clippy::single_element_loop)]
|
||||||
for (inp, typ) in [(
|
for (inp, typ) in [(
|
||||||
"https://test.example/foo",
|
"https://test.example/foo",
|
||||||
LinkType::Protocol(Cow::from("https")),
|
LinkType::Protocol(Cow::from("https")),
|
||||||
|
@ -1,14 +1,31 @@
|
|||||||
|
use std::borrow::Borrow;
|
||||||
|
|
||||||
use super::macros::intermediate;
|
use super::macros::intermediate;
|
||||||
use crate::error::CustomError;
|
use crate::error::CustomError;
|
||||||
use organic::types::StandardProperties;
|
use organic::types::StandardProperties;
|
||||||
|
use tree_sitter_highlight::HighlightConfiguration;
|
||||||
|
use tree_sitter_highlight::HighlightEvent;
|
||||||
|
use tree_sitter_highlight::Highlighter;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub(crate) struct ISrcBlock {
|
pub(crate) struct ISrcBlock {
|
||||||
pub(crate) lines: Vec<String>,
|
pub(crate) lines: Vec<ISrcLine>,
|
||||||
pub(crate) language: Option<String>,
|
pub(crate) language: Option<String>,
|
||||||
pub(crate) post_blank: organic::types::PostBlank,
|
pub(crate) post_blank: organic::types::PostBlank,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub(crate) struct ISrcLine {
|
||||||
|
pub(crate) children: Vec<ISrcSegment>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub(crate) enum ISrcSegment {
|
||||||
|
RawText(String),
|
||||||
|
HighlightStart { name: String },
|
||||||
|
HighlightEnd,
|
||||||
|
}
|
||||||
|
|
||||||
intermediate!(
|
intermediate!(
|
||||||
ISrcBlock,
|
ISrcBlock,
|
||||||
&'orig organic::types::SrcBlock<'parse>,
|
&'orig organic::types::SrcBlock<'parse>,
|
||||||
@ -59,14 +76,66 @@ intermediate!(
|
|||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
let language = original.language.map(str::to_owned);
|
let language = original.language.map(str::to_owned);
|
||||||
|
|
||||||
|
match language.as_deref() {
|
||||||
|
Some(lang @ "bash") => {
|
||||||
|
let highlighted = highlight_bash(&lines);
|
||||||
|
if let Ok(highlighted) = highlighted {
|
||||||
|
return Ok(ISrcBlock {
|
||||||
|
lines: highlighted,
|
||||||
|
language,
|
||||||
|
post_blank: original.get_post_blank(),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
println!("Warning: Failed to highlight {} source.", lang);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(lang @ "nix") => {
|
||||||
|
let highlighted = highlight_nix(&lines);
|
||||||
|
if let Ok(highlighted) = highlighted {
|
||||||
|
return Ok(ISrcBlock {
|
||||||
|
lines: highlighted,
|
||||||
|
language,
|
||||||
|
post_blank: original.get_post_blank(),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
println!("Warning: Failed to highlight {} source.", lang);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(lang @ "python") => {
|
||||||
|
let highlighted = highlight_python(&lines);
|
||||||
|
if let Ok(highlighted) = highlighted {
|
||||||
|
return Ok(ISrcBlock {
|
||||||
|
lines: highlighted,
|
||||||
|
language,
|
||||||
|
post_blank: original.get_post_blank(),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
println!("Warning: Failed to highlight {} source.", lang);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(lang) => {
|
||||||
|
println!("Warning: No highlighting for language: {}", lang);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
};
|
||||||
|
let highlighted = highlight_plain(&lines)?;
|
||||||
Ok(ISrcBlock {
|
Ok(ISrcBlock {
|
||||||
lines,
|
lines: highlighted,
|
||||||
language,
|
language,
|
||||||
post_blank: original.get_post_blank(),
|
post_blank: original.get_post_blank(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
impl ISrcLine {
|
||||||
|
pub(crate) fn new() -> ISrcLine {
|
||||||
|
ISrcLine {
|
||||||
|
children: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn ascii_whitespace_value(c: char) -> usize {
|
fn ascii_whitespace_value(c: char) -> usize {
|
||||||
match c {
|
match c {
|
||||||
' ' => 1,
|
' ' => 1,
|
||||||
@ -76,3 +145,161 @@ fn ascii_whitespace_value(c: char) -> usize {
|
|||||||
_ => unreachable!("Only ascii whitespace can reach this code."),
|
_ => unreachable!("Only ascii whitespace can reach this code."),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn highlight_plain<L>(lines: &[L]) -> Result<Vec<ISrcLine>, CustomError>
|
||||||
|
where
|
||||||
|
std::string::String: for<'a> From<&'a L>,
|
||||||
|
{
|
||||||
|
Ok(lines
|
||||||
|
.iter()
|
||||||
|
.map(|l| {
|
||||||
|
let mut line = ISrcLine::new();
|
||||||
|
line.children.push(ISrcSegment::RawText(l.into()));
|
||||||
|
line
|
||||||
|
})
|
||||||
|
.collect())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn highlight_tree_sitter<L>(
|
||||||
|
config: HighlightConfiguration,
|
||||||
|
highlight_names: &[&str],
|
||||||
|
lines: &[L],
|
||||||
|
) -> Result<Vec<ISrcLine>, CustomError>
|
||||||
|
where
|
||||||
|
L: Borrow<str>,
|
||||||
|
{
|
||||||
|
let combined_text = lines.join("");
|
||||||
|
|
||||||
|
// Need 1 highlighter per thread
|
||||||
|
let mut highlighter = Highlighter::new();
|
||||||
|
|
||||||
|
let highlights = highlighter
|
||||||
|
.highlight(&config, combined_text.as_bytes(), None, |_| None)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let mut highlighted_text: Vec<ISrcLine> = Vec::with_capacity(lines.len());
|
||||||
|
let mut current_line = ISrcLine::new();
|
||||||
|
let mut highlight_stack: Vec<&str> = Vec::new();
|
||||||
|
for event in highlights {
|
||||||
|
match event.unwrap() {
|
||||||
|
HighlightEvent::Source { start, end } => {
|
||||||
|
let mut span = &combined_text[start..end];
|
||||||
|
while let Some(line_break_index) = span.find('\n') {
|
||||||
|
let first_line = &span[..(line_break_index + 1)];
|
||||||
|
current_line
|
||||||
|
.children
|
||||||
|
.push(ISrcSegment::RawText(first_line.to_owned()));
|
||||||
|
current_line.children.extend(
|
||||||
|
highlight_stack
|
||||||
|
.iter()
|
||||||
|
.map(|_name| ISrcSegment::HighlightEnd),
|
||||||
|
);
|
||||||
|
highlighted_text.push(current_line);
|
||||||
|
current_line = ISrcLine::new();
|
||||||
|
current_line
|
||||||
|
.children
|
||||||
|
.extend(
|
||||||
|
highlight_stack
|
||||||
|
.iter()
|
||||||
|
.map(|name| ISrcSegment::HighlightStart {
|
||||||
|
name: (*name).into(),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
span = &span[(line_break_index + 1)..];
|
||||||
|
}
|
||||||
|
if !span.is_empty() {
|
||||||
|
current_line
|
||||||
|
.children
|
||||||
|
.push(ISrcSegment::RawText(span.to_owned()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
HighlightEvent::HighlightStart(s) => {
|
||||||
|
highlight_stack.push(highlight_names[s.0]);
|
||||||
|
current_line.children.push(ISrcSegment::HighlightStart {
|
||||||
|
name: highlight_names[s.0].into(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
HighlightEvent::HighlightEnd => {
|
||||||
|
highlight_stack.pop();
|
||||||
|
current_line.children.push(ISrcSegment::HighlightEnd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
debug_assert!(highlight_stack.is_empty());
|
||||||
|
|
||||||
|
Ok(highlighted_text)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn highlight_bash<L>(lines: &[L]) -> Result<Vec<ISrcLine>, CustomError>
|
||||||
|
where
|
||||||
|
L: Borrow<str>,
|
||||||
|
{
|
||||||
|
let highlight_names = ["comment", "function", "keyword", "property", "string"];
|
||||||
|
let language = tree_sitter_bash::LANGUAGE.into();
|
||||||
|
let mut config =
|
||||||
|
HighlightConfiguration::new(language, "bash", tree_sitter_bash::HIGHLIGHT_QUERY, "", "")
|
||||||
|
.unwrap();
|
||||||
|
config.configure(&highlight_names);
|
||||||
|
highlight_tree_sitter(config, &highlight_names, lines)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn highlight_nix<L>(lines: &[L]) -> Result<Vec<ISrcLine>, CustomError>
|
||||||
|
where
|
||||||
|
L: Borrow<str>,
|
||||||
|
{
|
||||||
|
let highlight_names = [
|
||||||
|
"comment",
|
||||||
|
"keyword",
|
||||||
|
"property",
|
||||||
|
"string",
|
||||||
|
"string.special.path",
|
||||||
|
// "string.special.uri",
|
||||||
|
];
|
||||||
|
let language = tree_sitter_nix::LANGUAGE.into();
|
||||||
|
let mut config =
|
||||||
|
HighlightConfiguration::new(language, "nix", tree_sitter_nix::HIGHLIGHTS_QUERY, "", "")
|
||||||
|
.unwrap();
|
||||||
|
config.configure(&highlight_names);
|
||||||
|
highlight_tree_sitter(config, &highlight_names, lines)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn highlight_python<L>(lines: &[L]) -> Result<Vec<ISrcLine>, CustomError>
|
||||||
|
where
|
||||||
|
L: Borrow<str>,
|
||||||
|
{
|
||||||
|
let highlight_names = [
|
||||||
|
"comment",
|
||||||
|
"function.builtin",
|
||||||
|
"keyword",
|
||||||
|
"property",
|
||||||
|
"string",
|
||||||
|
"type",
|
||||||
|
"variable",
|
||||||
|
];
|
||||||
|
let language = tree_sitter_python::LANGUAGE.into();
|
||||||
|
let mut config = HighlightConfiguration::new(
|
||||||
|
language,
|
||||||
|
"python",
|
||||||
|
tree_sitter_python::HIGHLIGHTS_QUERY,
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
config.configure(&highlight_names);
|
||||||
|
highlight_tree_sitter(config, &highlight_names, lines)
|
||||||
|
}
|
||||||
|
|
||||||
|
// use tree_sitter::Parser;
|
||||||
|
// fn dump_nix<B>(body: B) -> Result<(), CustomError>
|
||||||
|
// where
|
||||||
|
// B: AsRef<str>,
|
||||||
|
// {
|
||||||
|
// let mut parser = Parser::new();
|
||||||
|
// parser
|
||||||
|
// .set_language(&tree_sitter_nix::LANGUAGE.into())
|
||||||
|
// .expect("Error loading Nix grammar");
|
||||||
|
// let mut tree = parser.parse(body.as_ref(), None).unwrap();
|
||||||
|
// println!("{}", tree.root_node());
|
||||||
|
// Ok(())
|
||||||
|
// }
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
use super::macros::inoop;
|
|
||||||
use super::macros::intermediate;
|
use super::macros::intermediate;
|
||||||
use super::util::coalesce_whitespace;
|
use super::util::coalesce_whitespace;
|
||||||
use crate::error::CustomError;
|
use crate::error::CustomError;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user