commit
4bbf7a62a1
|
@ -1,5 +1,12 @@
|
|||
# Changelog
|
||||
|
||||
## 0.12.1 (2020-09-27)
|
||||
|
||||
- Add line highlighting in code blocks
|
||||
- Fix the new `zola serve` being broken on Windows
|
||||
- Fix slugified taxonomies not being rendered at the right path
|
||||
- Fix issues with shortcodes with newlines and read more
|
||||
|
||||
## 0.12.0 (2020-09-04)
|
||||
|
||||
### Breaking
|
||||
|
|
183
Cargo.lock
generated
183
Cargo.lock
generated
|
@ -157,9 +157,9 @@ checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7"
|
|||
|
||||
[[package]]
|
||||
name = "bytemuck"
|
||||
version = "1.4.0"
|
||||
version = "1.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92046dbb6f9332943252123f53623e0a6d513651af14967e2991c371ec20201c"
|
||||
checksum = "41aa2ec95ca3b5c54cf73c91acf06d24f4495d5f1b1c12506ae3483d646177ac"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
|
@ -185,9 +185,9 @@ checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38"
|
|||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.59"
|
||||
version = "1.0.60"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "66120af515773fb005778dc07c261bd201ec8ce50bd6e7144c927753fe013381"
|
||||
checksum = "ef611cc68ff783f18535d77ddd080185275713d852c4f5cbb6122c462a7a825c"
|
||||
|
||||
[[package]]
|
||||
name = "cedarwood"
|
||||
|
@ -206,14 +206,16 @@ checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
|||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.15"
|
||||
version = "0.4.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "942f72db697d8767c22d46a598e01f2d3b475501ea43d0db4f16d90259182d0b"
|
||||
checksum = "d021fddb7bd3e734370acfa4a83f34095571d8570c039f1420d77540f68d5772"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
"serde",
|
||||
"time",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -290,12 +292,12 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "crossbeam-channel"
|
||||
version = "0.4.3"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09ee0cc8804d5393478d743b035099520087a5186f3b93fa58cec08fa62407b6"
|
||||
checksum = "b153fe7cbef478c567df0f972e02e6d736db11affe43dfc9c56a9374d1adfb87"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crossbeam-utils",
|
||||
"maybe-uninit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -421,9 +423,9 @@ checksum = "134951f4028bdadb9b84baf4232681efbf277da25144b9b0ad65df75946c422b"
|
|||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.6.0"
|
||||
version = "1.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd56b59865bce947ac5958779cfa508f6c3b9497cc762b7e24a12d11ccde2c4f"
|
||||
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
|
||||
|
||||
[[package]]
|
||||
name = "elasticlunr-rs"
|
||||
|
@ -565,7 +567,7 @@ dependencies = [
|
|||
"cfg-if",
|
||||
"crc32fast",
|
||||
"libc",
|
||||
"miniz_oxide 0.4.1",
|
||||
"miniz_oxide 0.4.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -721,9 +723,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.1.14"
|
||||
version = "0.1.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb"
|
||||
checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
|
@ -732,12 +734,12 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gif"
|
||||
version = "0.10.3"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "471d90201b3b223f3451cd4ad53e34295f16a1df17b1edf3736d47761c3981af"
|
||||
checksum = "02efba560f227847cb41463a7395c514d127d4f74fff12ef0137fff1b84b96c4"
|
||||
dependencies = [
|
||||
"color_quant",
|
||||
"lzw",
|
||||
"weezl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -799,6 +801,12 @@ dependencies = [
|
|||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "00d63df3d41950fb462ed38308eea019113ad1508da725bbedcd0fa5a85ef5f7"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.3.1"
|
||||
|
@ -810,9 +818,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.15"
|
||||
version = "0.1.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3deed196b6e7f9e44a2ae8d94225d80302d81208b1bb673fd21fe634645c85a9"
|
||||
checksum = "4c30f6d0bc6b00693347368a67d41b58f2fb851215ff1da49e90fe2c5c667151"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
@ -858,6 +866,12 @@ version = "1.3.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9"
|
||||
|
||||
[[package]]
|
||||
name = "httpdate"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "494b4d60369511e7dea41cf646832512a94e542f68bb9c49e54518e0f468eb47"
|
||||
|
||||
[[package]]
|
||||
name = "humansize"
|
||||
version = "1.1.0"
|
||||
|
@ -866,9 +880,9 @@ checksum = "b6cab2627acfc432780848602f3f558f7e9dd427352224b0d9324025796d2a5e"
|
|||
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "0.13.7"
|
||||
version = "0.13.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e68a8dd9716185d9e64ea473ea6ef63529252e3e27623295a0378a19665d5eb"
|
||||
checksum = "2f3afcfae8af5ad0576a31e768415edb627824129e8e5a29b8bfccb2f234e835"
|
||||
dependencies = [
|
||||
"bytes 0.5.6",
|
||||
"futures-channel",
|
||||
|
@ -878,10 +892,10 @@ dependencies = [
|
|||
"http",
|
||||
"http-body",
|
||||
"httparse",
|
||||
"httpdate",
|
||||
"itoa",
|
||||
"pin-project",
|
||||
"socket2",
|
||||
"time",
|
||||
"tokio",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
|
@ -952,9 +966,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "image"
|
||||
version = "0.23.9"
|
||||
version = "0.23.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "974e194911d1f7efe3cd8a8f9db3b767e43536327e899e8bc9a12ef5711b74d2"
|
||||
checksum = "985fc06b1304d19c28d5c562ed78ef5316183f2b0053b46763a0b94862373c34"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"byteorder",
|
||||
|
@ -983,12 +997,12 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.5.2"
|
||||
version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4e47a3566dd4fd4eec714ae6ceabdee0caec795be835c223d92c2d40f1e8cf1c"
|
||||
checksum = "55e2e4c765aa53a0424761bf9f41aa7a6ac1efa87238f59560640e27fca028f2"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"hashbrown",
|
||||
"hashbrown 0.9.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1039,7 +1053,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "2ca2de723e93727460917d9542f7ae35a74d03d93923f03380a0238d860d137c"
|
||||
dependencies = [
|
||||
"cedarwood",
|
||||
"hashbrown",
|
||||
"hashbrown 0.8.2",
|
||||
"lazy_static",
|
||||
"phf",
|
||||
"phf_codegen",
|
||||
|
@ -1058,9 +1072,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.44"
|
||||
version = "0.3.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85a7e2c92a4804dd459b86c339278d0fe87cf93757fae222c3fa3ae75458bc73"
|
||||
checksum = "ca059e81d9486668f12d455a4ea6daa600bd408134cd17e3d3fb5a32d1f016f8"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
@ -1095,9 +1109,9 @@ checksum = "73a004f877f468548d8d0ac4977456a249d8fabbdb8416c36db163dfc8f2e8ca"
|
|||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.76"
|
||||
version = "0.2.77"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "755456fae044e6fa1ebbbd1b3e902ae19e73097ed4ed87bb79934a867c007bc3"
|
||||
checksum = "f2f96b10ec2560088a8e76961b00d47107b3a625fecb76dedb29ee7ccbf98235"
|
||||
|
||||
[[package]]
|
||||
name = "library"
|
||||
|
@ -1287,9 +1301,9 @@ checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400"
|
|||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
version = "0.5.5"
|
||||
version = "0.5.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c198b026e1bbf08a937e94c6c60f9ec4a2267f5b0d2eec9c1b21b061ce2be55f"
|
||||
checksum = "043175f069eda7b85febe4a74abbaeff828d9f8b448515d3151a14a3542811aa"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
@ -1332,11 +1346,12 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.4.1"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4d7559a8a40d0f97e1edea3220f698f78b1c5ab67532e49f68fde3910323b722"
|
||||
checksum = "c60c0dfe32c10b43a144bad8fc83538c52f58302c92300ea7ec7bf7b38d5a7b9"
|
||||
dependencies = [
|
||||
"adler",
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1402,9 +1417,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "net2"
|
||||
version = "0.2.34"
|
||||
version = "0.2.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2ba7c918ac76704fb42afcbbb43891e72731f3dcca3bef2a19786297baf14af7"
|
||||
checksum = "3ebc3ec692ed7c9a255596c67808dee269f64655d8baf7b4f0638e51ba1d6853"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
|
@ -1507,9 +1522,9 @@ checksum = "260e51e7efe62b592207e9e13a68e43692a7a279171d6ba57abd208bf23645ad"
|
|||
|
||||
[[package]]
|
||||
name = "onig"
|
||||
version = "6.0.0"
|
||||
version = "6.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd91ccd8a02fce2f7e8a86655aec67bc6c171e6f8e704118a0e8c4b866a05a8a"
|
||||
checksum = "8a155d13862da85473665694f4c05d77fb96598bdceeaf696433c84ea9567e20"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"lazy_static",
|
||||
|
@ -1519,9 +1534,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "onig_sys"
|
||||
version = "69.5.0"
|
||||
version = "69.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3814583fad89f3c60ae0701d80e87e1fd3028741723deda72d0d4a0ecf0cb0db"
|
||||
checksum = "9bff06597a6b17855040955cae613af000fc0bfc8ad49ea68b9479a74e59292d"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"pkg-config",
|
||||
|
@ -1646,18 +1661,18 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "pin-project"
|
||||
version = "0.4.23"
|
||||
version = "0.4.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca4433fff2ae79342e497d9f8ee990d174071408f28f726d6d83af93e58e48aa"
|
||||
checksum = "f48fad7cfbff853437be7cf54d7b993af21f53be7f0988cbfe4a51535aa77205"
|
||||
dependencies = [
|
||||
"pin-project-internal",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-internal"
|
||||
version = "0.4.23"
|
||||
version = "0.4.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2c0e815c3ee9a031fdf5af21c10aa17c573c9c6a566328d99e3936c34e36461f"
|
||||
checksum = "24c6d293bdd3ca5a1697997854c6cf7855e43fb6a0ba1c47af57a5bcafd158ae"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -1666,9 +1681,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.1.7"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "282adbf10f2698a7a77f8e983a74b2d18176c19a7fd32a45446139ae7b02b715"
|
||||
checksum = "71f349a4f0e70676ffb2dbafe16d0c992382d02f0a952e3ddf584fc289dac6b3"
|
||||
|
||||
[[package]]
|
||||
name = "pin-utils"
|
||||
|
@ -1758,9 +1773,9 @@ checksum = "eba180dafb9038b050a4c280019bbedf9f2467b61e5d892dcad585bb57aadc5a"
|
|||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.20"
|
||||
version = "1.0.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "175c513d55719db99da20232b06cda8bab6b83ec2d04e3283edf0213c37c1a29"
|
||||
checksum = "51ef7cd2518ead700af67bf9d1a658d90b6037d77110fd9c0445429d0ba1c6c9"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
@ -1850,9 +1865,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rayon-core"
|
||||
version = "1.8.0"
|
||||
version = "1.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91739a34c4355b5434ce54c9086c5895604a9c278586d1f1aa95e04f66b525a0"
|
||||
checksum = "e8c4fec834fb6e6d2dd5eece3c7b432a52f0ba887cf40e595190c4107edc08bf"
|
||||
dependencies = [
|
||||
"crossbeam-channel",
|
||||
"crossbeam-deque",
|
||||
|
@ -1900,6 +1915,12 @@ version = "0.6.18"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26412eb97c6b088a6997e05f69403a802a92d520de2f8e63c2b65f9e0f47c4e8"
|
||||
|
||||
[[package]]
|
||||
name = "relative-path"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "65aff7c83039e88c1c0b4bedf8dfa93d6ec84d5fc2945b37c1fa4186f46c5f94"
|
||||
|
||||
[[package]]
|
||||
name = "remove_dir_all"
|
||||
version = "0.5.3"
|
||||
|
@ -2092,18 +2113,18 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.115"
|
||||
version = "1.0.116"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e54c9a88f2da7238af84b5101443f0c0d0a3bbdc455e34a5c9497b1903ed55d5"
|
||||
checksum = "96fe57af81d28386a513cbc6858332abc6117cfdb5999647c6444b8f43a370a5"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.115"
|
||||
version = "1.0.116"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "609feed1d0a73cc36a0182a840a9b37b4a82f0b1150369f0536a9e3f2a31dc48"
|
||||
checksum = "f630a6370fd8e457873b4bd2ffdae75408bc291ba72be773772a4c2a065d9ae8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -2179,6 +2200,7 @@ dependencies = [
|
|||
"link_checker",
|
||||
"minify-html",
|
||||
"rayon",
|
||||
"relative-path",
|
||||
"sass-rs",
|
||||
"search",
|
||||
"serde",
|
||||
|
@ -2218,9 +2240,9 @@ checksum = "fbee7696b84bbf3d89a1c2eccff0850e3047ed46bfcd2e92c29a2d074d57e252"
|
|||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.3.12"
|
||||
version = "0.3.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "03088793f677dce356f3ccc2edb1b314ad191ab702a5de3faf49304f7e104918"
|
||||
checksum = "b1fa70dc5c8104ec096f4fe7ede7a221d35ae13dcd19ba1ad9a81d2cab9a1c44"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
|
@ -2297,9 +2319,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.39"
|
||||
version = "1.0.42"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "891d8d6567fe7c7f8835a3a98af4208f3846fba258c1bc3c31d6e506239f11f9"
|
||||
checksum = "9c51d92969d209b54a98397e1b91c8ae82d8c87a7bb87df0b29aa2ad81454228"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -2538,9 +2560,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "tracing-core"
|
||||
version = "0.1.15"
|
||||
version = "0.1.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4f0e00789804e99b20f12bc7003ca416309d28a6f495d6af58d1e2c2842461b5"
|
||||
checksum = "5bcf46c1f1f06aeea2d6b81f3c863d0930a596c86ad1920d4e5bad6dd1d7119a"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
]
|
||||
|
@ -2757,9 +2779,9 @@ checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.67"
|
||||
version = "0.2.68"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0563a9a4b071746dd5aedbc3a28c6fe9be4586fb3fbadb67c400d4f53c6b16c"
|
||||
checksum = "1ac64ead5ea5f05873d7c12b545865ca2b8d28adfc50a49b84770a3a97265d42"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"serde",
|
||||
|
@ -2769,9 +2791,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.67"
|
||||
version = "0.2.68"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc71e4c5efa60fb9e74160e89b93353bc24059999c0ae0fb03affc39770310b0"
|
||||
checksum = "f22b422e2a757c35a73774860af8e112bff612ce6cb604224e8e47641a9e4f68"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"lazy_static",
|
||||
|
@ -2784,9 +2806,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-futures"
|
||||
version = "0.4.17"
|
||||
version = "0.4.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95f8d235a77f880bcef268d379810ea6c0af2eacfa90b1ad5af731776e0c4699"
|
||||
checksum = "b7866cab0aa01de1edf8b5d7936938a7e397ee50ce24119aef3e1eaa3b6171da"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
|
@ -2796,9 +2818,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.67"
|
||||
version = "0.2.68"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97c57cefa5fa80e2ba15641578b44d36e7a64279bc5ed43c6dbaf329457a2ed2"
|
||||
checksum = "6b13312a745c08c469f0b292dd2fcd6411dba5f7160f593da6ef69b64e407038"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
|
@ -2806,9 +2828,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.67"
|
||||
version = "0.2.68"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "841a6d1c35c6f596ccea1f82504a192a60378f64b3bb0261904ad8f2f5657556"
|
||||
checksum = "f249f06ef7ee334cc3b8ff031bfc11ec99d00f34d86da7498396dc1e3b1498fe"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -2819,15 +2841,15 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.67"
|
||||
version = "0.2.68"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93b162580e34310e5931c4b792560108b10fd14d64915d7fff8ff00180e70092"
|
||||
checksum = "1d649a3145108d7d3fbcde896a468d1bd636791823c9921135218ad89be08307"
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.44"
|
||||
version = "0.3.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dda38f4e5ca63eda02c059d243aa25b5f35ab98451e518c51612cd0f1bd19a47"
|
||||
checksum = "4bf6ef87ad7ae8008e15a355ce696bed26012b7caa21605188cfd8214ab51e2d"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
|
@ -2852,6 +2874,12 @@ dependencies = [
|
|||
"webpki",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "weezl"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a3d2f24b6c3aa92fb33279566dbebf1cbe66b03a73f09aa69cf8cf14d2f9feb9"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.2.8"
|
||||
|
@ -2967,7 +2995,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "zola"
|
||||
version = "0.12.0"
|
||||
version = "0.12.1"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"chrono",
|
||||
|
@ -2981,6 +3009,7 @@ dependencies = [
|
|||
"lazy_static",
|
||||
"notify",
|
||||
"open",
|
||||
"relative-path",
|
||||
"site",
|
||||
"termcolor",
|
||||
"tokio",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "zola"
|
||||
version = "0.12.0"
|
||||
version = "0.12.1"
|
||||
authors = ["Vincent Prouillet <hello@vincentprouillet.com>"]
|
||||
edition = "2018"
|
||||
license = "MIT"
|
||||
|
@ -35,6 +35,7 @@ ws = "0.9"
|
|||
ctrlc = "3"
|
||||
open = "1.2"
|
||||
globset = "0.4"
|
||||
relative-path = "1"
|
||||
|
||||
site = { path = "components/site" }
|
||||
errors = { path = "components/errors" }
|
||||
|
|
|
@ -17,11 +17,11 @@ lazy_static! {
|
|||
}
|
||||
|
||||
/// Returns the highlighter and whether it was found in the extra or not
|
||||
pub fn get_highlighter<'a>(info: &str, config: &Config) -> (HighlightLines<'a>, bool) {
|
||||
pub fn get_highlighter(language: Option<&str>, config: &Config) -> (HighlightLines<'static>, bool) {
|
||||
let theme = &THEME_SET.themes[&config.highlight_theme];
|
||||
let mut in_extra = false;
|
||||
|
||||
if let Some(ref lang) = info.split(' ').next() {
|
||||
if let Some(ref lang) = language {
|
||||
let syntax = SYNTAX_SET
|
||||
.find_syntax_by_token(lang)
|
||||
.or_else(|| {
|
||||
|
|
|
@ -103,7 +103,7 @@ impl<'a> Paginator<'a> {
|
|||
paginate_reversed: false,
|
||||
root: PaginationRoot::Taxonomy(taxonomy, item),
|
||||
permalink: item.permalink.clone(),
|
||||
path: format!("/{}/{}/", taxonomy.kind.name, item.slug),
|
||||
path: format!("/{}/{}/", taxonomy.slug, item.slug),
|
||||
paginate_path: taxonomy
|
||||
.kind
|
||||
.paginate_path
|
||||
|
@ -129,7 +129,7 @@ impl<'a> Paginator<'a> {
|
|||
}
|
||||
|
||||
for key in self.all_pages.to_mut().iter_mut() {
|
||||
let page = library.get_page_by_key(key.clone());
|
||||
let page = library.get_page_by_key(*key);
|
||||
current_page.push(page.to_serialized_basic(library));
|
||||
|
||||
if current_page.len() == self.paginate_by {
|
||||
|
@ -416,7 +416,11 @@ mod tests {
|
|||
permalink: "https://vincent.is/tags/something/".to_string(),
|
||||
pages: library.pages().keys().collect(),
|
||||
};
|
||||
let taxonomy = Taxonomy { kind: taxonomy_def, items: vec![taxonomy_item.clone()] };
|
||||
let taxonomy = Taxonomy {
|
||||
kind: taxonomy_def,
|
||||
slug: "tags".to_string(),
|
||||
items: vec![taxonomy_item.clone()],
|
||||
};
|
||||
let paginator = Paginator::from_taxonomy(&taxonomy, &taxonomy_item, &library);
|
||||
assert_eq!(paginator.pagers.len(), 2);
|
||||
|
||||
|
@ -431,6 +435,39 @@ mod tests {
|
|||
assert_eq!(paginator.pagers[1].path, "/tags/something/page/2/");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_can_create_paginator_for_slugified_taxonomy() {
|
||||
let (_, library) = create_library(false, 3, false);
|
||||
let taxonomy_def = TaxonomyConfig {
|
||||
name: "some tags".to_string(),
|
||||
paginate_by: Some(2),
|
||||
..TaxonomyConfig::default()
|
||||
};
|
||||
let taxonomy_item = TaxonomyItem {
|
||||
name: "Something".to_string(),
|
||||
slug: "something".to_string(),
|
||||
permalink: "https://vincent.is/some-tags/something/".to_string(),
|
||||
pages: library.pages().keys().collect(),
|
||||
};
|
||||
let taxonomy = Taxonomy {
|
||||
kind: taxonomy_def,
|
||||
slug: "some-tags".to_string(),
|
||||
items: vec![taxonomy_item.clone()],
|
||||
};
|
||||
let paginator = Paginator::from_taxonomy(&taxonomy, &taxonomy_item, &library);
|
||||
assert_eq!(paginator.pagers.len(), 2);
|
||||
|
||||
assert_eq!(paginator.pagers[0].index, 1);
|
||||
assert_eq!(paginator.pagers[0].pages.len(), 2);
|
||||
assert_eq!(paginator.pagers[0].permalink, "https://vincent.is/some-tags/something/");
|
||||
assert_eq!(paginator.pagers[0].path, "/some-tags/something/");
|
||||
|
||||
assert_eq!(paginator.pagers[1].index, 2);
|
||||
assert_eq!(paginator.pagers[1].pages.len(), 2);
|
||||
assert_eq!(paginator.pagers[1].permalink, "https://vincent.is/some-tags/something/page/2/");
|
||||
assert_eq!(paginator.pagers[1].path, "/some-tags/something/page/2/");
|
||||
}
|
||||
|
||||
// https://github.com/getzola/zola/issues/866
|
||||
#[test]
|
||||
fn works_with_empty_paginate_path() {
|
||||
|
|
|
@ -53,6 +53,7 @@ impl TaxonomyItem {
|
|||
pub fn new(
|
||||
name: &str,
|
||||
taxonomy: &TaxonomyConfig,
|
||||
taxo_slug: &str,
|
||||
config: &Config,
|
||||
keys: Vec<DefaultKey>,
|
||||
library: &Library,
|
||||
|
@ -72,7 +73,6 @@ impl TaxonomyItem {
|
|||
.collect();
|
||||
let (mut pages, ignored_pages) = sort_pages_by_date(data);
|
||||
let item_slug = slugify_paths(name, config.slugify.taxonomies);
|
||||
let taxo_slug = slugify_paths(&taxonomy.name, config.slugify.taxonomies);
|
||||
let permalink = if taxonomy.lang != config.default_language {
|
||||
config.make_permalink(&format!("/{}/{}/{}", taxonomy.lang, taxo_slug, item_slug))
|
||||
} else {
|
||||
|
@ -118,6 +118,7 @@ impl<'a> SerializedTaxonomy<'a> {
|
|||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Taxonomy {
|
||||
pub kind: TaxonomyConfig,
|
||||
pub slug: String,
|
||||
// this vec is sorted by the count of item
|
||||
pub items: Vec<TaxonomyItem>,
|
||||
}
|
||||
|
@ -130,8 +131,9 @@ impl Taxonomy {
|
|||
library: &Library,
|
||||
) -> Taxonomy {
|
||||
let mut sorted_items = vec![];
|
||||
let slug = slugify_paths(&kind.name, config.slugify.taxonomies);
|
||||
for (name, pages) in items {
|
||||
sorted_items.push(TaxonomyItem::new(&name, &kind, config, pages, library));
|
||||
sorted_items.push(TaxonomyItem::new(&name, &kind, &slug, config, pages, library));
|
||||
}
|
||||
//sorted_items.sort_by(|a, b| a.name.cmp(&b.name));
|
||||
sorted_items.sort_by(|a, b| match a.slug.cmp(&b.slug) {
|
||||
|
@ -150,7 +152,7 @@ impl Taxonomy {
|
|||
false
|
||||
}
|
||||
});
|
||||
Taxonomy { kind, items: sorted_items }
|
||||
Taxonomy { kind, slug, items: sorted_items }
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
|
|
|
@ -14,8 +14,7 @@ pub fn render_content(content: &str, context: &RenderContext) -> Result<markdown
|
|||
// Don't do shortcodes if there is nothing like a shortcode in the content
|
||||
if content.contains("{{") || content.contains("{%") {
|
||||
let rendered = render_shortcodes(content, context)?;
|
||||
let mut html = markdown_to_html(&rendered, context)?;
|
||||
html.body = html.body.replace("<!--\\n-->", "\n");
|
||||
let html = markdown_to_html(&rendered, context)?;
|
||||
return Ok(html);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,14 +1,11 @@
|
|||
use lazy_static::lazy_static;
|
||||
use pulldown_cmark as cmark;
|
||||
use regex::Regex;
|
||||
use syntect::easy::HighlightLines;
|
||||
use syntect::html::{
|
||||
start_highlighted_html_snippet, styled_line_to_highlighted_html, IncludeBackground,
|
||||
};
|
||||
use syntect::html::{start_highlighted_html_snippet, IncludeBackground};
|
||||
|
||||
use crate::context::RenderContext;
|
||||
use crate::table_of_contents::{make_table_of_contents, Heading};
|
||||
use config::highlighting::{get_highlighter, SYNTAX_SET, THEME_SET};
|
||||
use config::highlighting::THEME_SET;
|
||||
use errors::{Error, Result};
|
||||
use front_matter::InsertAnchor;
|
||||
use utils::site::resolve_internal_link;
|
||||
|
@ -18,6 +15,10 @@ use utils::vec::InsertMany;
|
|||
use self::cmark::{Event, LinkType, Options, Parser, Tag};
|
||||
use pulldown_cmark::CodeBlockKind;
|
||||
|
||||
mod codeblock;
|
||||
mod fence;
|
||||
use self::codeblock::CodeBlock;
|
||||
|
||||
const CONTINUE_READING: &str = "<span id=\"continue-reading\"></span>";
|
||||
const ANCHOR_LINK_TEMPLATE: &str = "anchor-link.html";
|
||||
|
||||
|
@ -172,8 +173,7 @@ pub fn markdown_to_html(content: &str, context: &RenderContext) -> Result<Render
|
|||
// Set while parsing
|
||||
let mut error = None;
|
||||
|
||||
let mut background = IncludeBackground::Yes;
|
||||
let mut highlighter: Option<(HighlightLines, bool)> = None;
|
||||
let mut highlighter: Option<CodeBlock> = None;
|
||||
|
||||
let mut inserted_anchors: Vec<String> = vec![];
|
||||
let mut headings: Vec<Heading> = vec![];
|
||||
|
@ -182,6 +182,7 @@ pub fn markdown_to_html(content: &str, context: &RenderContext) -> Result<Render
|
|||
|
||||
let mut opts = Options::empty();
|
||||
let mut has_summary = false;
|
||||
let mut in_html_block = false;
|
||||
opts.insert(Options::ENABLE_TABLES);
|
||||
opts.insert(Options::ENABLE_FOOTNOTES);
|
||||
opts.insert(Options::ENABLE_STRIKETHROUGH);
|
||||
|
@ -192,26 +193,14 @@ pub fn markdown_to_html(content: &str, context: &RenderContext) -> Result<Render
|
|||
.map(|event| {
|
||||
match event {
|
||||
Event::Text(text) => {
|
||||
// if we are in the middle of a code block
|
||||
if let Some((ref mut highlighter, in_extra)) = highlighter {
|
||||
let highlighted = if in_extra {
|
||||
if let Some(ref extra) = context.config.extra_syntax_set {
|
||||
highlighter.highlight(&text, &extra)
|
||||
} else {
|
||||
unreachable!(
|
||||
"Got a highlighter from extra syntaxes but no extra?"
|
||||
);
|
||||
}
|
||||
} else {
|
||||
highlighter.highlight(&text, &SYNTAX_SET)
|
||||
};
|
||||
//let highlighted = &highlighter.highlight(&text, ss);
|
||||
let html = styled_line_to_highlighted_html(&highlighted, background);
|
||||
return Event::Html(html.into());
|
||||
// if we are in the middle of a highlighted code block
|
||||
if let Some(ref mut code_block) = highlighter {
|
||||
let html = code_block.highlight(&text);
|
||||
Event::Html(html.into())
|
||||
} else {
|
||||
// Business as usual
|
||||
Event::Text(text)
|
||||
}
|
||||
|
||||
// Business as usual
|
||||
Event::Text(text)
|
||||
}
|
||||
Event::Start(Tag::CodeBlock(ref kind)) => {
|
||||
if !context.config.highlight_code {
|
||||
|
@ -221,16 +210,21 @@ pub fn markdown_to_html(content: &str, context: &RenderContext) -> Result<Render
|
|||
let theme = &THEME_SET.themes[&context.config.highlight_theme];
|
||||
match kind {
|
||||
CodeBlockKind::Indented => (),
|
||||
CodeBlockKind::Fenced(info) => {
|
||||
highlighter = Some(get_highlighter(info, &context.config));
|
||||
CodeBlockKind::Fenced(fence_info) => {
|
||||
// This selects the background color the same way that
|
||||
// start_coloured_html_snippet does
|
||||
let color = theme
|
||||
.settings
|
||||
.background
|
||||
.unwrap_or(::syntect::highlighting::Color::WHITE);
|
||||
|
||||
highlighter = Some(CodeBlock::new(
|
||||
fence_info,
|
||||
&context.config,
|
||||
IncludeBackground::IfDifferent(color),
|
||||
));
|
||||
}
|
||||
};
|
||||
// This selects the background color the same way that start_coloured_html_snippet does
|
||||
let color = theme
|
||||
.settings
|
||||
.background
|
||||
.unwrap_or(::syntect::highlighting::Color::WHITE);
|
||||
background = IncludeBackground::IfDifferent(color);
|
||||
let snippet = start_highlighted_html_snippet(theme);
|
||||
let mut html = snippet.0;
|
||||
html.push_str("<code>");
|
||||
|
@ -273,9 +267,27 @@ pub fn markdown_to_html(content: &str, context: &RenderContext) -> Result<Render
|
|||
|
||||
Event::Start(Tag::Link(link_type, fixed_link.into(), title))
|
||||
}
|
||||
Event::Html(ref markup) if markup.contains("<!-- more -->") => {
|
||||
has_summary = true;
|
||||
Event::Html(CONTINUE_READING.into())
|
||||
Event::Html(ref markup) => {
|
||||
if markup.contains("<!-- more -->") {
|
||||
has_summary = true;
|
||||
Event::Html(CONTINUE_READING.into())
|
||||
} else {
|
||||
if in_html_block && markup.contains("</pre>") {
|
||||
in_html_block = false;
|
||||
Event::Html(markup.replacen("</pre>", "", 1).into())
|
||||
} else if markup.contains("pre data-shortcode") {
|
||||
in_html_block = true;
|
||||
let m = markup.replacen("<pre data-shortcode>", "", 1);
|
||||
if m.contains("</pre>") {
|
||||
in_html_block = false;
|
||||
Event::Html(m.replacen("</pre>", "", 1).into())
|
||||
} else {
|
||||
Event::Html(m.into())
|
||||
}
|
||||
} else {
|
||||
event
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => event,
|
||||
}
|
||||
|
|
182
components/rendering/src/markdown/codeblock.rs
Normal file
182
components/rendering/src/markdown/codeblock.rs
Normal file
|
@ -0,0 +1,182 @@
|
|||
use config::highlighting::{get_highlighter, SYNTAX_SET, THEME_SET};
|
||||
use config::Config;
|
||||
use std::cmp::min;
|
||||
use std::collections::HashSet;
|
||||
use syntect::easy::HighlightLines;
|
||||
use syntect::highlighting::{Color, Style, Theme};
|
||||
use syntect::html::{styled_line_to_highlighted_html, IncludeBackground};
|
||||
use syntect::parsing::SyntaxSet;
|
||||
|
||||
use super::fence::{FenceSettings, Range};
|
||||
|
||||
pub struct CodeBlock<'config> {
|
||||
highlighter: HighlightLines<'static>,
|
||||
extra_syntax_set: Option<&'config SyntaxSet>,
|
||||
background: IncludeBackground,
|
||||
theme: &'static Theme,
|
||||
|
||||
/// List of ranges of lines to highlight.
|
||||
highlight_lines: Vec<Range>,
|
||||
/// The number of lines in the code block being processed.
|
||||
num_lines: usize,
|
||||
}
|
||||
|
||||
impl<'config> CodeBlock<'config> {
|
||||
pub fn new(fence_info: &str, config: &'config Config, background: IncludeBackground) -> Self {
|
||||
let fence_info = FenceSettings::new(fence_info);
|
||||
let theme = &THEME_SET.themes[&config.highlight_theme];
|
||||
let (highlighter, in_extra) = get_highlighter(fence_info.language, config);
|
||||
Self {
|
||||
highlighter,
|
||||
extra_syntax_set: match in_extra {
|
||||
true => config.extra_syntax_set.as_ref(),
|
||||
false => None,
|
||||
},
|
||||
background,
|
||||
theme,
|
||||
|
||||
highlight_lines: fence_info.highlight_lines,
|
||||
num_lines: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn highlight(&mut self, text: &str) -> String {
|
||||
let highlighted =
|
||||
self.highlighter.highlight(text, self.extra_syntax_set.unwrap_or(&SYNTAX_SET));
|
||||
let line_boundaries = self.find_line_boundaries(&highlighted);
|
||||
|
||||
// First we make sure that `highlighted` is split at every line
|
||||
// boundary. The `styled_line_to_highlighted_html` function will
|
||||
// merge split items with identical styles, so this is not a
|
||||
// problem.
|
||||
//
|
||||
// Note that this invalidates the values in `line_boundaries`.
|
||||
// The `perform_split` function takes it by value to ensure that
|
||||
// we don't use it later.
|
||||
let mut highlighted = perform_split(&highlighted, line_boundaries);
|
||||
|
||||
let hl_background =
|
||||
self.theme.settings.line_highlight.unwrap_or(Color { r: 255, g: 255, b: 0, a: 0 });
|
||||
|
||||
let hl_lines = self.get_highlighted_lines();
|
||||
color_highlighted_lines(&mut highlighted, &hl_lines, hl_background);
|
||||
|
||||
styled_line_to_highlighted_html(&highlighted, self.background)
|
||||
}
|
||||
|
||||
fn find_line_boundaries(&mut self, styled: &[(Style, &str)]) -> Vec<StyledIdx> {
|
||||
let mut boundaries = Vec::new();
|
||||
for (vec_idx, (_style, s)) in styled.iter().enumerate() {
|
||||
for (str_idx, character) in s.char_indices() {
|
||||
if character == '\n' {
|
||||
boundaries.push(StyledIdx { vec_idx, str_idx });
|
||||
}
|
||||
}
|
||||
}
|
||||
self.num_lines = boundaries.len() + 1;
|
||||
boundaries
|
||||
}
|
||||
|
||||
fn get_highlighted_lines(&self) -> HashSet<usize> {
|
||||
let mut lines = HashSet::new();
|
||||
for range in &self.highlight_lines {
|
||||
for line in range.from..=min(range.to, self.num_lines) {
|
||||
// Ranges are one-indexed
|
||||
lines.insert(line.saturating_sub(1));
|
||||
}
|
||||
}
|
||||
lines
|
||||
}
|
||||
}
|
||||
|
||||
/// This is an index of a character in a `&[(Style, &'b str)]`. The `vec_idx` is the
|
||||
/// index in the slice, and `str_idx` is the byte index of the character in the
|
||||
/// corresponding string slice.
|
||||
///
|
||||
/// The `Ord` impl on this type sorts lexiographically on `vec_idx`, and then `str_idx`.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
struct StyledIdx {
|
||||
vec_idx: usize,
|
||||
str_idx: usize,
|
||||
}
|
||||
|
||||
/// This is a utility used by `perform_split`. If the `vec_idx` in the `StyledIdx` is
|
||||
/// equal to the provided value, return the `str_idx`, otherwise return `None`.
|
||||
fn get_str_idx_if_vec_idx_is(idx: Option<&StyledIdx>, vec_idx: usize) -> Option<usize> {
|
||||
match idx {
|
||||
Some(idx) if idx.vec_idx == vec_idx => Some(idx.str_idx),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// This function assumes that `line_boundaries` is sorted according to the `Ord` impl on
|
||||
/// the `StyledIdx` type.
|
||||
fn perform_split<'b>(
|
||||
split: &[(Style, &'b str)],
|
||||
line_boundaries: Vec<StyledIdx>,
|
||||
) -> Vec<(Style, &'b str)> {
|
||||
let mut result = Vec::new();
|
||||
|
||||
let mut idxs_iter = line_boundaries.into_iter().peekable();
|
||||
|
||||
for (split_idx, item) in split.iter().enumerate() {
|
||||
let mut last_split = 0;
|
||||
|
||||
// Since `line_boundaries` is sorted, we know that any remaining indexes in
|
||||
// `idxs_iter` have `vec_idx >= split_idx`, and that if there are any with
|
||||
// `vec_idx == split_idx`, they will be first.
|
||||
//
|
||||
// Using the `get_str_idx_if_vec_idx_is` utility, this loop will keep consuming
|
||||
// indexes from `idxs_iter` as long as `vec_idx == split_idx` holds. Once
|
||||
// `vec_idx` becomes larger than `split_idx`, the loop will finish without
|
||||
// consuming that index.
|
||||
//
|
||||
// If `idxs_iter` is empty, or there are no indexes with `vec_idx == split_idx`,
|
||||
// the loop does nothing.
|
||||
while let Some(str_idx) = get_str_idx_if_vec_idx_is(idxs_iter.peek(), split_idx) {
|
||||
// Consume the value we just peeked.
|
||||
idxs_iter.next();
|
||||
|
||||
// This consumes the index to split at. We add one to include the newline
|
||||
// together with its own line, rather than as the first character in the next
|
||||
// line.
|
||||
let split_at = min(str_idx + 1, item.1.len());
|
||||
|
||||
// This will fail if `line_boundaries` is not sorted.
|
||||
debug_assert!(split_at >= last_split);
|
||||
|
||||
// Skip splitting if the string slice would be empty.
|
||||
if last_split != split_at {
|
||||
result.push((item.0, &item.1[last_split..split_at]));
|
||||
last_split = split_at;
|
||||
}
|
||||
}
|
||||
|
||||
// Now append the remainder. If the current item was not split, this will
|
||||
// append the entire item.
|
||||
if last_split != item.1.len() {
|
||||
result.push((item.0, &item.1[last_split..]));
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
fn color_highlighted_lines(data: &mut [(Style, &str)], lines: &HashSet<usize>, background: Color) {
|
||||
if lines.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut current_line = 0;
|
||||
|
||||
for item in data {
|
||||
if lines.contains(¤t_line) {
|
||||
item.0.background = background;
|
||||
}
|
||||
|
||||
// We split the lines such that every newline is at the end of an item.
|
||||
if item.1.ends_with('\n') {
|
||||
current_line += 1;
|
||||
}
|
||||
}
|
||||
}
|
90
components/rendering/src/markdown/fence.rs
Normal file
90
components/rendering/src/markdown/fence.rs
Normal file
|
@ -0,0 +1,90 @@
|
|||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct Range {
|
||||
pub from: usize,
|
||||
pub to: usize,
|
||||
}
|
||||
|
||||
impl Range {
|
||||
fn parse(s: &str) -> Option<Range> {
|
||||
match s.find('-') {
|
||||
Some(dash) => {
|
||||
let mut from = s[..dash].parse().ok()?;
|
||||
let mut to = s[dash + 1..].parse().ok()?;
|
||||
if to < from {
|
||||
std::mem::swap(&mut from, &mut to);
|
||||
}
|
||||
Some(Range { from, to })
|
||||
}
|
||||
None => {
|
||||
let val = s.parse().ok()?;
|
||||
Some(Range { from: val, to: val })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct FenceSettings<'a> {
|
||||
pub language: Option<&'a str>,
|
||||
pub line_numbers: bool,
|
||||
pub highlight_lines: Vec<Range>,
|
||||
}
|
||||
impl<'a> FenceSettings<'a> {
|
||||
pub fn new(fence_info: &'a str) -> Self {
|
||||
let mut me = Self { language: None, line_numbers: false, highlight_lines: Vec::new() };
|
||||
|
||||
for token in FenceIter::new(fence_info) {
|
||||
match token {
|
||||
FenceToken::Language(lang) => me.language = Some(lang),
|
||||
FenceToken::EnableLineNumbers => me.line_numbers = true,
|
||||
FenceToken::HighlightLines(lines) => me.highlight_lines.extend(lines),
|
||||
}
|
||||
}
|
||||
|
||||
me
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum FenceToken<'a> {
|
||||
Language(&'a str),
|
||||
EnableLineNumbers,
|
||||
HighlightLines(Vec<Range>),
|
||||
}
|
||||
|
||||
struct FenceIter<'a> {
|
||||
split: std::str::Split<'a, char>,
|
||||
}
|
||||
impl<'a> FenceIter<'a> {
|
||||
fn new(fence_info: &'a str) -> Self {
|
||||
Self { split: fence_info.split(',') }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for FenceIter<'a> {
|
||||
type Item = FenceToken<'a>;
|
||||
|
||||
fn next(&mut self) -> Option<FenceToken<'a>> {
|
||||
loop {
|
||||
let tok = self.split.next()?.trim();
|
||||
|
||||
let mut tok_split = tok.split('=');
|
||||
match tok_split.next().unwrap_or("").trim() {
|
||||
"" => continue,
|
||||
"linenos" => return Some(FenceToken::EnableLineNumbers),
|
||||
"hl_lines" => {
|
||||
let mut ranges = Vec::new();
|
||||
for range in tok_split.next().unwrap_or("").split(' ') {
|
||||
if let Some(range) = Range::parse(range) {
|
||||
ranges.push(range);
|
||||
}
|
||||
}
|
||||
return Some(FenceToken::HighlightLines(ranges));
|
||||
}
|
||||
lang => {
|
||||
return Some(FenceToken::Language(lang));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -131,7 +131,7 @@ fn render_shortcode(
|
|||
// someone wants to include that comment in their content. This behaviour is unwanted in when
|
||||
// rendering markdown shortcodes.
|
||||
if template_name.ends_with(".html") {
|
||||
Ok(res.replace('\n', "<!--\\n-->").to_string())
|
||||
Ok(format!("<pre data-shortcode>{}</pre>", res))
|
||||
} else {
|
||||
Ok(res.to_string())
|
||||
}
|
||||
|
@ -404,7 +404,7 @@ Some body {{ hello() }}{%/* end */%}"#,
|
|||
let mut tera = Tera::default();
|
||||
tera.add_raw_template("shortcodes/youtube.html", "Hello {{id}}").unwrap();
|
||||
let res = render_shortcodes("Inline {{ youtube(id=1) }}.", &tera);
|
||||
assert_eq!(res, "Inline Hello 1.");
|
||||
assert_eq!(res, "Inline <pre data-shortcode>Hello 1</pre>.");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -412,7 +412,7 @@ Some body {{ hello() }}{%/* end */%}"#,
|
|||
let mut tera = Tera::default();
|
||||
tera.add_raw_template("shortcodes/youtube.html", "{{body}}").unwrap();
|
||||
let res = render_shortcodes("Body\n {% youtube() %}Hey!{% end %}", &tera);
|
||||
assert_eq!(res, "Body\n Hey!");
|
||||
assert_eq!(res, "Body\n <pre data-shortcode>Hey!</pre>");
|
||||
}
|
||||
|
||||
// https://github.com/Keats/gutenberg/issues/462
|
||||
|
@ -421,7 +421,7 @@ Some body {{ hello() }}{%/* end */%}"#,
|
|||
let mut tera = Tera::default();
|
||||
tera.add_raw_template("shortcodes/youtube.html", "{{body | safe}}").unwrap();
|
||||
let res = render_shortcodes("Body\n {% youtube() %}\nHello \n \n\n World{% end %}", &tera);
|
||||
assert_eq!(res, "Body\n Hello <!--\\n--> <!--\\n--><!--\\n--> World");
|
||||
assert_eq!(res, "Body\n <pre data-shortcode>Hello \n \n\n World</pre>");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -429,7 +429,7 @@ Some body {{ hello() }}{%/* end */%}"#,
|
|||
let mut tera = Tera::default();
|
||||
tera.add_raw_template("shortcodes/youtube.html", " \n {{body}} \n ").unwrap();
|
||||
let res = render_shortcodes("\n{% youtube() %} \n content \n {% end %}\n", &tera);
|
||||
assert_eq!(res, "\n content \n");
|
||||
assert_eq!(res, "\n<pre data-shortcode> content </pre>\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -437,7 +437,7 @@ Some body {{ hello() }}{%/* end */%}"#,
|
|||
let mut tera = Tera::default();
|
||||
tera.add_raw_template("shortcodes/youtube.html", " \n Hello, Zola. \n ").unwrap();
|
||||
let res = render_shortcodes("\n{{ youtube() }}\n", &tera);
|
||||
assert_eq!(res, "\n Hello, Zola. \n");
|
||||
assert_eq!(res, "\n<pre data-shortcode> Hello, Zola. </pre>\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
400
components/rendering/tests/codeblock_hl_lines.rs
Normal file
400
components/rendering/tests/codeblock_hl_lines.rs
Normal file
|
@ -0,0 +1,400 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use tera::Tera;
|
||||
|
||||
use config::Config;
|
||||
use front_matter::InsertAnchor;
|
||||
use rendering::{render_content, RenderContext};
|
||||
|
||||
macro_rules! colored_html_line {
|
||||
( @no $s:expr ) => {{
|
||||
let mut result = "<span style=\"color:#c0c5ce;\">".to_string();
|
||||
result.push_str($s);
|
||||
result.push_str("\n</span>");
|
||||
result
|
||||
}};
|
||||
( @hl $s:expr ) => {{
|
||||
let mut result = "<span style=\"background-color:#65737e30;color:#c0c5ce;\">".to_string();
|
||||
result.push_str($s);
|
||||
result.push_str("\n</span>");
|
||||
result
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! colored_html {
|
||||
( $(@$kind:tt $s:expr),* $(,)* ) => {{
|
||||
let mut result = "<pre style=\"background-color:#2b303b;\">\n<code>".to_string();
|
||||
$(
|
||||
result.push_str(colored_html_line!(@$kind $s).as_str());
|
||||
)*
|
||||
result.push_str("</code></pre>");
|
||||
result
|
||||
}};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hl_lines_simple() {
|
||||
let tera_ctx = Tera::default();
|
||||
let permalinks_ctx = HashMap::new();
|
||||
let mut config = Config::default();
|
||||
config.highlight_code = true;
|
||||
let context = RenderContext::new(&tera_ctx, &config, "", &permalinks_ctx, InsertAnchor::None);
|
||||
let res = render_content(
|
||||
r#"
|
||||
```hl_lines=2
|
||||
foo
|
||||
bar
|
||||
bar
|
||||
baz
|
||||
```
|
||||
"#,
|
||||
&context,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
res.body,
|
||||
colored_html!(
|
||||
@no "foo",
|
||||
@hl "bar",
|
||||
@no "bar\nbaz",
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hl_lines_in_middle() {
|
||||
let tera_ctx = Tera::default();
|
||||
let permalinks_ctx = HashMap::new();
|
||||
let mut config = Config::default();
|
||||
config.highlight_code = true;
|
||||
let context = RenderContext::new(&tera_ctx, &config, "", &permalinks_ctx, InsertAnchor::None);
|
||||
let res = render_content(
|
||||
r#"
|
||||
```hl_lines=2-3
|
||||
foo
|
||||
bar
|
||||
bar
|
||||
baz
|
||||
```
|
||||
"#,
|
||||
&context,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
res.body,
|
||||
colored_html!(
|
||||
@no "foo",
|
||||
@hl "bar\nbar",
|
||||
@no "baz",
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hl_lines_all() {
|
||||
let tera_ctx = Tera::default();
|
||||
let permalinks_ctx = HashMap::new();
|
||||
let mut config = Config::default();
|
||||
config.highlight_code = true;
|
||||
let context = RenderContext::new(&tera_ctx, &config, "", &permalinks_ctx, InsertAnchor::None);
|
||||
let res = render_content(
|
||||
r#"
|
||||
```hl_lines=1-4
|
||||
foo
|
||||
bar
|
||||
bar
|
||||
baz
|
||||
```
|
||||
"#,
|
||||
&context,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
res.body,
|
||||
colored_html!(
|
||||
@hl "foo\nbar\nbar\nbaz",
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hl_lines_start_from_one() {
|
||||
let tera_ctx = Tera::default();
|
||||
let permalinks_ctx = HashMap::new();
|
||||
let mut config = Config::default();
|
||||
config.highlight_code = true;
|
||||
let context = RenderContext::new(&tera_ctx, &config, "", &permalinks_ctx, InsertAnchor::None);
|
||||
let res = render_content(
|
||||
r#"
|
||||
```hl_lines=1-3
|
||||
foo
|
||||
bar
|
||||
bar
|
||||
baz
|
||||
```
|
||||
"#,
|
||||
&context,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
res.body,
|
||||
colored_html!(
|
||||
@hl "foo\nbar\nbar",
|
||||
@no "baz",
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hl_lines_start_from_zero() {
|
||||
let tera_ctx = Tera::default();
|
||||
let permalinks_ctx = HashMap::new();
|
||||
let mut config = Config::default();
|
||||
config.highlight_code = true;
|
||||
let context = RenderContext::new(&tera_ctx, &config, "", &permalinks_ctx, InsertAnchor::None);
|
||||
let res = render_content(
|
||||
r#"
|
||||
```hl_lines=0-3
|
||||
foo
|
||||
bar
|
||||
bar
|
||||
baz
|
||||
```
|
||||
"#,
|
||||
&context,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
res.body,
|
||||
colored_html!(
|
||||
@hl "foo\nbar\nbar",
|
||||
@no "baz",
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hl_lines_end() {
|
||||
let tera_ctx = Tera::default();
|
||||
let permalinks_ctx = HashMap::new();
|
||||
let mut config = Config::default();
|
||||
config.highlight_code = true;
|
||||
let context = RenderContext::new(&tera_ctx, &config, "", &permalinks_ctx, InsertAnchor::None);
|
||||
let res = render_content(
|
||||
r#"
|
||||
```hl_lines=3-4
|
||||
foo
|
||||
bar
|
||||
bar
|
||||
baz
|
||||
```
|
||||
"#,
|
||||
&context,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
res.body,
|
||||
colored_html!(
|
||||
@no "foo\nbar",
|
||||
@hl "bar\nbaz",
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hl_lines_end_out_of_bounds() {
|
||||
let tera_ctx = Tera::default();
|
||||
let permalinks_ctx = HashMap::new();
|
||||
let mut config = Config::default();
|
||||
config.highlight_code = true;
|
||||
let context = RenderContext::new(&tera_ctx, &config, "", &permalinks_ctx, InsertAnchor::None);
|
||||
let res = render_content(
|
||||
r#"
|
||||
```hl_lines=3-4294967295
|
||||
foo
|
||||
bar
|
||||
bar
|
||||
baz
|
||||
```
|
||||
"#,
|
||||
&context,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
res.body,
|
||||
colored_html!(
|
||||
@no "foo\nbar",
|
||||
@hl "bar\nbaz",
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hl_lines_overlap() {
|
||||
let tera_ctx = Tera::default();
|
||||
let permalinks_ctx = HashMap::new();
|
||||
let mut config = Config::default();
|
||||
config.highlight_code = true;
|
||||
let context = RenderContext::new(&tera_ctx, &config, "", &permalinks_ctx, InsertAnchor::None);
|
||||
let res = render_content(
|
||||
r#"
|
||||
```hl_lines=2-3 1-2
|
||||
foo
|
||||
bar
|
||||
bar
|
||||
baz
|
||||
```
|
||||
"#,
|
||||
&context,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
res.body,
|
||||
colored_html!(
|
||||
@hl "foo\nbar\nbar",
|
||||
@no "baz",
|
||||
)
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn hl_lines_multiple() {
|
||||
let tera_ctx = Tera::default();
|
||||
let permalinks_ctx = HashMap::new();
|
||||
let mut config = Config::default();
|
||||
config.highlight_code = true;
|
||||
let context = RenderContext::new(&tera_ctx, &config, "", &permalinks_ctx, InsertAnchor::None);
|
||||
let res = render_content(
|
||||
r#"
|
||||
```hl_lines=2-3,hl_lines=1-2
|
||||
foo
|
||||
bar
|
||||
bar
|
||||
baz
|
||||
```
|
||||
"#,
|
||||
&context,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
res.body,
|
||||
colored_html!(
|
||||
@hl "foo\nbar\nbar",
|
||||
@no "baz",
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hl_lines_extra_spaces() {
|
||||
let tera_ctx = Tera::default();
|
||||
let permalinks_ctx = HashMap::new();
|
||||
let mut config = Config::default();
|
||||
config.highlight_code = true;
|
||||
let context = RenderContext::new(&tera_ctx, &config, "", &permalinks_ctx, InsertAnchor::None);
|
||||
let res = render_content(
|
||||
r#"
|
||||
``` hl_lines = 2 - 3 1 - 2
|
||||
foo
|
||||
bar
|
||||
bar
|
||||
baz
|
||||
```
|
||||
"#,
|
||||
&context,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
res.body,
|
||||
colored_html!(
|
||||
@hl "foo\nbar\nbar",
|
||||
@no "baz",
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hl_lines_int_and_range() {
|
||||
let tera_ctx = Tera::default();
|
||||
let permalinks_ctx = HashMap::new();
|
||||
let mut config = Config::default();
|
||||
config.highlight_code = true;
|
||||
let context = RenderContext::new(&tera_ctx, &config, "", &permalinks_ctx, InsertAnchor::None);
|
||||
let res = render_content(
|
||||
r#"
|
||||
```hl_lines=1 3-4
|
||||
foo
|
||||
bar
|
||||
bar
|
||||
baz
|
||||
```
|
||||
"#,
|
||||
&context,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
res.body,
|
||||
colored_html!(
|
||||
@hl "foo",
|
||||
@no "bar",
|
||||
@hl "bar\nbaz",
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hl_lines_single_line_range() {
|
||||
let tera_ctx = Tera::default();
|
||||
let permalinks_ctx = HashMap::new();
|
||||
let mut config = Config::default();
|
||||
config.highlight_code = true;
|
||||
let context = RenderContext::new(&tera_ctx, &config, "", &permalinks_ctx, InsertAnchor::None);
|
||||
let res = render_content(
|
||||
r#"
|
||||
```hl_lines=2-2
|
||||
foo
|
||||
bar
|
||||
bar
|
||||
baz
|
||||
```
|
||||
"#,
|
||||
&context,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
res.body,
|
||||
colored_html!(
|
||||
@no "foo",
|
||||
@hl "bar",
|
||||
@no "bar\nbaz",
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hl_lines_reverse_range() {
|
||||
let tera_ctx = Tera::default();
|
||||
let permalinks_ctx = HashMap::new();
|
||||
let mut config = Config::default();
|
||||
config.highlight_code = true;
|
||||
let context = RenderContext::new(&tera_ctx, &config, "", &permalinks_ctx, InsertAnchor::None);
|
||||
let res = render_content(
|
||||
r#"
|
||||
```hl_lines=3-2
|
||||
foo
|
||||
bar
|
||||
bar
|
||||
baz
|
||||
```
|
||||
"#,
|
||||
&context,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
res.body,
|
||||
colored_html!(
|
||||
@no "foo",
|
||||
@hl "bar\nbar",
|
||||
@no "baz",
|
||||
)
|
||||
);
|
||||
}
|
|
@ -142,7 +142,7 @@ fn can_render_body_shortcode_with_markdown_char_in_name() {
|
|||
let res =
|
||||
render_content(&format!("{{% {}(author=\"Bob\") %}}\nhey\n{{% end %}}", i), &context)
|
||||
.unwrap();
|
||||
println!("{:?}", res);
|
||||
|
||||
assert!(res.body.contains("<blockquote>hey - Bob</blockquote>"));
|
||||
}
|
||||
}
|
||||
|
@ -166,12 +166,12 @@ Here is another paragraph.
|
|||
<p>Here is another paragraph.</p>
|
||||
";
|
||||
|
||||
tera.add_raw_template(&format!("shortcodes/{}.html", "figure"), shortcode).unwrap();
|
||||
tera.add_raw_template("shortcodes/figure.html", shortcode).unwrap();
|
||||
let config = Config::default();
|
||||
let context = RenderContext::new(&tera, &config, "", &permalinks_ctx, InsertAnchor::None);
|
||||
|
||||
let res = render_content(markdown_string, &context).unwrap();
|
||||
println!("{:?}", res);
|
||||
|
||||
assert_eq!(res.body, expected);
|
||||
}
|
||||
|
||||
|
@ -199,12 +199,12 @@ Here is another paragraph.
|
|||
<p>Here is another paragraph.</p>
|
||||
";
|
||||
|
||||
tera.add_raw_template(&format!("shortcodes/{}.html", "figure"), shortcode).unwrap();
|
||||
tera.add_raw_template("shortcodes/figure.html", shortcode).unwrap();
|
||||
let config = Config::default();
|
||||
let context = RenderContext::new(&tera, &config, "", &permalinks_ctx, InsertAnchor::None);
|
||||
|
||||
let res = render_content(markdown_string, &context).unwrap();
|
||||
println!("{:?}", res);
|
||||
|
||||
assert_eq!(res.body, expected);
|
||||
}
|
||||
|
||||
|
@ -790,7 +790,7 @@ fn doesnt_try_to_highlight_content_from_shortcode() {
|
|||
|
||||
let expected = "<figure>\n \n <img src=\"/images/spherecluster.png\" alt=\"Some spheres.\" />\n \n\n <figcaption>Some spheres.</figcaption>\n</figure>";
|
||||
|
||||
tera.add_raw_template(&format!("shortcodes/{}.html", "figure"), shortcode).unwrap();
|
||||
tera.add_raw_template("shortcodes/figure.html", shortcode).unwrap();
|
||||
let config = Config::default();
|
||||
let context = RenderContext::new(&tera, &config, "", &permalinks_ctx, InsertAnchor::None);
|
||||
|
||||
|
@ -937,11 +937,102 @@ Bla bla"#;
|
|||
<p>Bla bla</p>
|
||||
"#;
|
||||
|
||||
tera.add_raw_template(&format!("shortcodes/{}.md", "quote"), shortcode).unwrap();
|
||||
tera.add_raw_template("shortcodes/quote.md", shortcode).unwrap();
|
||||
let config = Config::default();
|
||||
let context = RenderContext::new(&tera, &config, "", &permalinks_ctx, InsertAnchor::None);
|
||||
|
||||
let res = render_content(markdown_string, &context).unwrap();
|
||||
|
||||
assert_eq!(res.body, expected);
|
||||
}
|
||||
|
||||
// https://github.com/getzola/zola/issues/1172
|
||||
#[test]
|
||||
fn can_render_shortcode_body_with_no_invalid_escaping() {
|
||||
let permalinks_ctx = HashMap::new();
|
||||
let mut tera = Tera::default();
|
||||
tera.extend(&ZOLA_TERA).unwrap();
|
||||
|
||||
let shortcode = r#"
|
||||
<a class="resize-image" href="/tlera-corp-gnat/gnat-with-picoblade-cable.jpg">
|
||||
<img
|
||||
src="https://placekitten.com/200/300"
|
||||
alt="{{ alt }}">
|
||||
</img>
|
||||
<p>(click for full size)</p>
|
||||
</a>
|
||||
"#;
|
||||
|
||||
let markdown_string = r#"{{ resize_image(path="tlera-corp-gnat/gnat-with-picoblade-cable.jpg", width=600, alt="Some alt") }}"#;
|
||||
|
||||
let expected = "<a class=\"resize-image\" href=\"/tlera-corp-gnat/gnat-with-picoblade-cable.jpg\">\n <img\n src=\"https://placekitten.com/200/300\"\n alt=\"Some alt\">\n </img>\n <p>(click for full size)</p>\n</a>";
|
||||
|
||||
tera.add_raw_template("shortcodes/resize_image.html", shortcode).unwrap();
|
||||
let config = Config::default();
|
||||
let context = RenderContext::new(&tera, &config, "", &permalinks_ctx, InsertAnchor::None);
|
||||
|
||||
let res = render_content(markdown_string, &context).unwrap();
|
||||
assert_eq!(res.body, expected);
|
||||
}
|
||||
|
||||
// https://github.com/getzola/zola/issues/1172
|
||||
#[test]
|
||||
fn can_render_commented_out_shortcodes_fine() {
|
||||
let permalinks_ctx = HashMap::new();
|
||||
let mut tera = Tera::default();
|
||||
tera.extend(&ZOLA_TERA).unwrap();
|
||||
|
||||
let shortcode = r#"
|
||||
<a class="resize-image" href="/tlera-corp-gnat/gnat-with-picoblade-cable.jpg">
|
||||
<img
|
||||
src="https://placekitten.com/200/300"
|
||||
alt="{{ alt }}">
|
||||
</img>
|
||||
<p>(click for full size)</p>
|
||||
</a>
|
||||
"#;
|
||||
|
||||
let markdown_string = r#"<!--{{ resize_image(path="tlera-corp-gnat/gnat-with-picoblade-cable.jpg", width=600, alt="Some alt") }}-->"#;
|
||||
|
||||
let expected = "<!--<a class=\"resize-image\" href=\"/tlera-corp-gnat/gnat-with-picoblade-cable.jpg\">\n <img\n src=\"https://placekitten.com/200/300\"\n alt=\"Some alt\">\n </img>\n <p>(click for full size)</p>\n</a>-->";
|
||||
|
||||
tera.add_raw_template("shortcodes/resize_image.html", shortcode).unwrap();
|
||||
let config = Config::default();
|
||||
let context = RenderContext::new(&tera, &config, "", &permalinks_ctx, InsertAnchor::None);
|
||||
|
||||
let res = render_content(markdown_string, &context).unwrap();
|
||||
assert_eq!(res.body, expected);
|
||||
}
|
||||
|
||||
|
||||
// https://zola.discourse.group/t/zola-12-issue-with-continue-reading/590/7
|
||||
#[test]
|
||||
fn can_render_read_more_after_shortcode() {
|
||||
let permalinks_ctx = HashMap::new();
|
||||
let mut tera = Tera::default();
|
||||
tera.extend(&ZOLA_TERA).unwrap();
|
||||
|
||||
let shortcode = r#"<p>Quote: {{body}}</p>"#;
|
||||
let markdown_string = r#"
|
||||
# Title
|
||||
|
||||
Some text
|
||||
{{ quote(body="Nothing is impossible. The word itself says - I'm Possible" author="Audrey Hepburn")}}
|
||||
<!-- more -->
|
||||
|
||||
Again more text"#;
|
||||
|
||||
let expected = r#"<h1 id="title">Title</h1>
|
||||
<p>Some text</p>
|
||||
<p>Quote: Nothing is impossible. The word itself says - I'm Possible</p>
|
||||
<span id="continue-reading"></span>
|
||||
<p>Again more text</p>
|
||||
"#;
|
||||
|
||||
tera.add_raw_template("shortcodes/quote.md", shortcode).unwrap();
|
||||
let config = Config::default();
|
||||
let context = RenderContext::new(&tera, &config, "", &permalinks_ctx, InsertAnchor::None);
|
||||
|
||||
let res = render_content(markdown_string, &context).unwrap();
|
||||
println!("{:?}", res);
|
||||
assert_eq!(res.body, expected);
|
||||
}
|
||||
|
|
|
@ -8,12 +8,13 @@ include = ["src/**/*"]
|
|||
[dependencies]
|
||||
tera = "1"
|
||||
glob = "0.3"
|
||||
minify-html = "0.3.6"
|
||||
minify-html = "0.3.8"
|
||||
rayon = "1"
|
||||
serde = "1"
|
||||
serde_derive = "1"
|
||||
sass-rs = "0.2"
|
||||
lazy_static = "1.1"
|
||||
relative-path = "1"
|
||||
|
||||
errors = { path = "../errors" }
|
||||
config = { path = "../config" }
|
||||
|
|
|
@ -11,7 +11,7 @@ use std::sync::{Arc, Mutex, RwLock};
|
|||
|
||||
use glob::glob;
|
||||
use lazy_static::lazy_static;
|
||||
use minify_html::{truncate, Cfg};
|
||||
use minify_html::{with_friendly_error, Cfg};
|
||||
use rayon::prelude::*;
|
||||
use tera::{Context, Tera};
|
||||
|
||||
|
@ -19,6 +19,7 @@ use config::{get_config, Config};
|
|||
use errors::{bail, Error, Result};
|
||||
use front_matter::InsertAnchor;
|
||||
use library::{find_taxonomies, Library, Page, Paginator, Section, Taxonomy};
|
||||
use relative_path::RelativePathBuf;
|
||||
use templates::render_redirect_template;
|
||||
use utils::fs::{
|
||||
copy_directory, copy_file_if_needed, create_directory, create_file, ensure_directory_exists,
|
||||
|
@ -28,7 +29,7 @@ use utils::templates::render_template;
|
|||
|
||||
lazy_static! {
|
||||
/// The in-memory rendered map content
|
||||
pub static ref SITE_CONTENT: Arc<RwLock<HashMap<String, String>>> = Arc::new(RwLock::new(HashMap::new()));
|
||||
pub static ref SITE_CONTENT: Arc<RwLock<HashMap<RelativePathBuf, String>>> = Arc::new(RwLock::new(HashMap::new()));
|
||||
}
|
||||
|
||||
/// Where are we building the site
|
||||
|
@ -450,13 +451,18 @@ impl Site {
|
|||
fn minify(&self, html: String) -> Result<String> {
|
||||
let cfg = &Cfg { minify_js: false };
|
||||
let mut input_bytes = html.as_bytes().to_vec();
|
||||
match truncate(&mut input_bytes, cfg) {
|
||||
Ok(_len) => match std::str::from_utf8(&mut input_bytes) {
|
||||
match with_friendly_error(&mut input_bytes, cfg) {
|
||||
Ok(_len) => match std::str::from_utf8(&input_bytes) {
|
||||
Ok(result) => Ok(result.to_string()),
|
||||
Err(err) => bail!("Failed to convert bytes to string : {}", err),
|
||||
},
|
||||
Err(minify_error) => {
|
||||
bail!("Failed to truncate html at character {}:", minify_error.position);
|
||||
bail!(
|
||||
"Failed to truncate html at character {}: {} \n {}",
|
||||
minify_error.position,
|
||||
minify_error.message,
|
||||
minify_error.code_context
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -513,10 +519,12 @@ impl Site {
|
|||
let write_dirs = self.build_mode == BuildMode::Disk || create_dirs;
|
||||
ensure_directory_exists(&self.output_path)?;
|
||||
|
||||
let mut site_path = RelativePathBuf::new();
|
||||
let mut current_path = self.output_path.to_path_buf();
|
||||
|
||||
for component in components {
|
||||
current_path.push(component);
|
||||
site_path.push(component);
|
||||
|
||||
if !current_path.exists() && write_dirs {
|
||||
create_directory(¤t_path)?;
|
||||
|
@ -542,17 +550,10 @@ impl Site {
|
|||
create_file(&end_path, &final_content)?;
|
||||
}
|
||||
BuildMode::Memory => {
|
||||
let path = if filename != "index.html" {
|
||||
let p = current_path.join(filename);
|
||||
p.as_os_str().to_string_lossy().replace("public/", "/")
|
||||
} else {
|
||||
// TODO" remove unwrap
|
||||
let p = current_path.strip_prefix("public").unwrap();
|
||||
p.as_os_str().to_string_lossy().into_owned()
|
||||
}
|
||||
.trim_end_matches('/')
|
||||
.to_owned();
|
||||
&SITE_CONTENT.write().unwrap().insert(path, final_content);
|
||||
let site_path =
|
||||
if filename != "index.html" { site_path.join(filename) } else { site_path };
|
||||
|
||||
SITE_CONTENT.write().unwrap().insert(site_path, final_content);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -568,12 +569,8 @@ impl Site {
|
|||
let output = page.render_html(&self.tera, &self.config, &self.library.read().unwrap())?;
|
||||
let content = self.inject_livereload(output);
|
||||
let components: Vec<&str> = page.path.split('/').collect();
|
||||
let current_path = self.write_content(
|
||||
&components,
|
||||
"index.html",
|
||||
content,
|
||||
!page.assets.is_empty(),
|
||||
)?;
|
||||
let current_path =
|
||||
self.write_content(&components, "index.html", content, !page.assets.is_empty())?;
|
||||
|
||||
// Copy any asset we found previously into the same directory as the index.html
|
||||
for asset in &page.assets {
|
||||
|
@ -771,7 +768,7 @@ impl Site {
|
|||
components.push(taxonomy.kind.lang.as_ref());
|
||||
}
|
||||
|
||||
components.push(taxonomy.kind.name.as_ref());
|
||||
components.push(taxonomy.slug.as_ref());
|
||||
|
||||
let list_output =
|
||||
taxonomy.render_all_terms(&self.tera, &self.config, &self.library.read().unwrap())?;
|
||||
|
@ -801,7 +798,7 @@ impl Site {
|
|||
if taxonomy.kind.feed {
|
||||
self.render_feed(
|
||||
item.pages.iter().map(|p| library.get_page_by_key(*p)).collect(),
|
||||
Some(&PathBuf::from(format!("{}/{}", taxonomy.kind.name, item.slug))),
|
||||
Some(&PathBuf::from(format!("{}/{}", taxonomy.slug, item.slug))),
|
||||
if self.config.is_multilingual() && !taxonomy.kind.lang.is_empty() {
|
||||
&taxonomy.kind.lang
|
||||
} else {
|
||||
|
@ -932,11 +929,7 @@ impl Site {
|
|||
|
||||
if section.meta.generate_feed {
|
||||
let library = &self.library.read().unwrap();
|
||||
let pages = section
|
||||
.pages
|
||||
.iter()
|
||||
.map(|k| library.get_page_by_key(*k))
|
||||
.collect();
|
||||
let pages = section.pages.iter().map(|k| library.get_page_by_key(*k)).collect();
|
||||
self.render_feed(
|
||||
pages,
|
||||
Some(&PathBuf::from(§ion.path[1..])),
|
||||
|
|
|
@ -44,14 +44,7 @@ fn compile_sass_glob(
|
|||
extension: &str,
|
||||
options: &Options,
|
||||
) -> Result<Vec<(PathBuf, PathBuf)>> {
|
||||
let glob_string = format!("{}/**/*.{}", sass_path.display(), extension);
|
||||
let files = glob(&glob_string)
|
||||
.expect("Invalid glob for sass")
|
||||
.filter_map(|e| e.ok())
|
||||
.filter(|entry| {
|
||||
!entry.as_path().components().any(|c| c.as_os_str().to_string_lossy().starts_with('_'))
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let files = get_non_partial_scss(sass_path, extension);
|
||||
|
||||
let mut compiled_paths = Vec::new();
|
||||
for file in files {
|
||||
|
@ -71,3 +64,48 @@ fn compile_sass_glob(
|
|||
|
||||
Ok(compiled_paths)
|
||||
}
|
||||
|
||||
fn get_non_partial_scss(sass_path: &Path, extension: &str) -> Vec<PathBuf> {
|
||||
let glob_string = format!("{}/**/*.{}", sass_path.display(), extension);
|
||||
glob(&glob_string)
|
||||
.expect("Invalid glob for sass")
|
||||
.filter_map(|e| e.ok())
|
||||
.filter(|entry| {
|
||||
!entry
|
||||
.as_path()
|
||||
.iter()
|
||||
.last()
|
||||
.map(|c| c.to_string_lossy().starts_with('_'))
|
||||
.unwrap_or(true)
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_non_partial_scss() {
|
||||
use std::env;
|
||||
|
||||
let mut path = env::current_dir().unwrap().parent().unwrap().parent().unwrap().to_path_buf();
|
||||
path.push("test_site");
|
||||
path.push("sass");
|
||||
|
||||
let result = get_non_partial_scss(&path, "scss");
|
||||
|
||||
assert!(result.len() != 0);
|
||||
assert!(result.iter().filter_map(|path| path.file_name()).any(|file| file == "scss.scss"))
|
||||
}
|
||||
#[test]
|
||||
fn test_get_non_partial_scss_underscores() {
|
||||
use std::env;
|
||||
|
||||
let mut path = env::current_dir().unwrap().parent().unwrap().parent().unwrap().to_path_buf();
|
||||
path.push("test_site");
|
||||
path.push("_dir_with_underscores");
|
||||
path.push("..");
|
||||
path.push("sass");
|
||||
|
||||
let result = get_non_partial_scss(&path, "scss");
|
||||
|
||||
assert!(result.len() != 0);
|
||||
assert!(result.iter().filter_map(|path| path.file_name()).any(|file| file == "scss.scss"))
|
||||
}
|
||||
|
|
|
@ -154,6 +154,10 @@ fn can_build_site_without_live_reload() {
|
|||
assert_eq!(file_exists!(public, "categories/index.html"), true);
|
||||
assert_eq!(file_exists!(public, "categories/a-category/index.html"), true);
|
||||
assert_eq!(file_exists!(public, "categories/a-category/atom.xml"), true);
|
||||
// and podcast_authors (https://github.com/getzola/zola/issues/1177)
|
||||
assert_eq!(file_exists!(public, "podcast-authors/index.html"), true);
|
||||
assert_eq!(file_exists!(public, "podcast-authors/some-person/index.html"), true);
|
||||
assert_eq!(file_exists!(public, "podcast-authors/some-person/atom.xml"), true);
|
||||
// But no tags
|
||||
assert_eq!(file_exists!(public, "tags/index.html"), false);
|
||||
|
||||
|
|
|
@ -572,6 +572,7 @@ mod tests {
|
|||
let tag = TaxonomyItem::new(
|
||||
"Programming",
|
||||
&taxo_config,
|
||||
"tags",
|
||||
&config,
|
||||
vec![],
|
||||
&library.read().unwrap(),
|
||||
|
@ -579,12 +580,14 @@ mod tests {
|
|||
let tag_fr = TaxonomyItem::new(
|
||||
"Programmation",
|
||||
&taxo_config_fr,
|
||||
"tags",
|
||||
&config,
|
||||
vec![],
|
||||
&library.read().unwrap(),
|
||||
);
|
||||
let tags = Taxonomy { kind: taxo_config, items: vec![tag] };
|
||||
let tags_fr = Taxonomy { kind: taxo_config_fr, items: vec![tag_fr] };
|
||||
let tags = Taxonomy { kind: taxo_config, slug: "tags".to_string(), items: vec![tag] };
|
||||
let tags_fr =
|
||||
Taxonomy { kind: taxo_config_fr, slug: "tags".to_string(), items: vec![tag_fr] };
|
||||
|
||||
let taxonomies = vec![tags.clone(), tags_fr.clone()];
|
||||
let static_fn =
|
||||
|
@ -647,10 +650,12 @@ mod tests {
|
|||
..TaxonomyConfig::default()
|
||||
};
|
||||
let library = Library::new(0, 0, false);
|
||||
let tag = TaxonomyItem::new("Programming", &taxo_config, &config, vec![], &library);
|
||||
let tag_fr = TaxonomyItem::new("Programmation", &taxo_config_fr, &config, vec![], &library);
|
||||
let tags = Taxonomy { kind: taxo_config, items: vec![tag] };
|
||||
let tags_fr = Taxonomy { kind: taxo_config_fr, items: vec![tag_fr] };
|
||||
let tag = TaxonomyItem::new("Programming", &taxo_config, "tags", &config, vec![], &library);
|
||||
let tag_fr =
|
||||
TaxonomyItem::new("Programmation", &taxo_config_fr, "tags", &config, vec![], &library);
|
||||
let tags = Taxonomy { kind: taxo_config, slug: "tags".to_string(), items: vec![tag] };
|
||||
let tags_fr =
|
||||
Taxonomy { kind: taxo_config_fr, slug: "tags".to_string(), items: vec![tag_fr] };
|
||||
|
||||
let taxonomies = vec![tags.clone(), tags_fr.clone()];
|
||||
let static_fn =
|
||||
|
|
|
@ -82,7 +82,8 @@ taxonomies = []
|
|||
#
|
||||
languages = []
|
||||
|
||||
# When set to "true", the Sass files in the `sass` directory are compiled.
|
||||
# When set to "true", the Sass files in the `sass` directory in the site root are compiled.
|
||||
# Sass files in theme directories are always compiled.
|
||||
compile_sass = false
|
||||
|
||||
# A list of glob patterns specifying asset files to ignore when the content
|
||||
|
|
|
@ -26,6 +26,7 @@ use std::path::{Path, PathBuf};
|
|||
use std::sync::mpsc::channel;
|
||||
use std::thread;
|
||||
use std::time::{Duration, Instant};
|
||||
use std::net::{SocketAddrV4, TcpListener};
|
||||
|
||||
use hyper::header;
|
||||
use hyper::service::{make_service_fn, service_fn};
|
||||
|
@ -38,6 +39,7 @@ use ws::{Message, Sender, WebSocket};
|
|||
|
||||
use errors::{Error as ZolaError, Result};
|
||||
use globset::GlobSet;
|
||||
use relative_path::{RelativePath, RelativePathBuf};
|
||||
use site::sass::compile_sass;
|
||||
use site::{Site, SITE_CONTENT};
|
||||
use utils::fs::copy_file;
|
||||
|
@ -69,7 +71,12 @@ static NOT_FOUND_TEXT: &[u8] = b"Not Found";
|
|||
const LIVE_RELOAD: &str = include_str!("livereload.js");
|
||||
|
||||
async fn handle_request(req: Request<Body>, root: PathBuf) -> Result<Response<Body>> {
|
||||
let path = req.uri().path().trim_end_matches('/').trim_start_matches('/');
|
||||
let mut path = RelativePathBuf::new();
|
||||
|
||||
for c in req.uri().path().split('/') {
|
||||
path.push(c);
|
||||
}
|
||||
|
||||
// livereload.js is served using the LIVE_RELOAD str, not a file
|
||||
if path == "livereload.js" {
|
||||
if req.method() == Method::GET {
|
||||
|
@ -79,7 +86,7 @@ async fn handle_request(req: Request<Body>, root: PathBuf) -> Result<Response<Bo
|
|||
}
|
||||
}
|
||||
|
||||
if let Some(content) = SITE_CONTENT.read().unwrap().get(path) {
|
||||
if let Some(content) = SITE_CONTENT.read().unwrap().get(&path) {
|
||||
return Ok(in_memory_html(content));
|
||||
}
|
||||
|
||||
|
@ -87,7 +94,8 @@ async fn handle_request(req: Request<Body>, root: PathBuf) -> Result<Response<Bo
|
|||
match result {
|
||||
ResolveResult::MethodNotMatched => return Ok(method_not_allowed()),
|
||||
ResolveResult::NotFound | ResolveResult::UriNotMatched => {
|
||||
let content_404 = SITE_CONTENT.read().unwrap().get("404.html").map(|x| x.clone());
|
||||
let not_found_path = RelativePath::new("404.html");
|
||||
let content_404 = SITE_CONTENT.read().unwrap().get(not_found_path).cloned();
|
||||
return Ok(not_found(content_404));
|
||||
}
|
||||
_ => (),
|
||||
|
@ -175,6 +183,13 @@ fn create_new_site(
|
|||
|
||||
let base_address = format!("{}:{}", base_url, interface_port);
|
||||
let address = format!("{}:{}", interface, interface_port);
|
||||
|
||||
// Stop right there if we can't bind to the address
|
||||
let bind_address: SocketAddrV4 = address.parse().unwrap();
|
||||
if (TcpListener::bind(&bind_address)).is_err() {
|
||||
return Err(format!("Cannot start server on address {}.", address))?;
|
||||
}
|
||||
|
||||
let base_url = if site.config.base_url.ends_with('/') {
|
||||
format!("http://{}/", base_address)
|
||||
} else {
|
||||
|
@ -255,7 +270,7 @@ pub fn serve(
|
|||
if should_watch {
|
||||
watcher
|
||||
.watch(root_dir.join(entry), RecursiveMode::Recursive)
|
||||
.map_err(|e| ZolaError::chain(format!("Can't watch `{}` for changes in folder `{}`. Do you have correct permissions?", entry, root_dir.display()), e))?;
|
||||
.map_err(|e| ZolaError::chain(format!("Can't watch `{}` for changes in folder `{}`. Does it exist, and do you have correct permissions?", entry, root_dir.display()), e))?;
|
||||
watchers.push(entry.to_string());
|
||||
}
|
||||
}
|
||||
|
@ -319,10 +334,17 @@ pub fn serve(
|
|||
}
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let broadcaster = ws_server.broadcaster();
|
||||
|
||||
let ws_server = ws_server
|
||||
.bind(&*ws_address)
|
||||
.map_err(|_| format!("Cannot bind to address {} for the websocket server. Maybe the port is already in use?", &ws_address))?;
|
||||
|
||||
thread::spawn(move || {
|
||||
ws_server.listen(&*ws_address).unwrap();
|
||||
ws_server.run().unwrap();
|
||||
});
|
||||
|
||||
Some(broadcaster)
|
||||
} else {
|
||||
println!("Watching in watch only mode, no web server will be started");
|
||||
|
|
|
@ -16,7 +16,7 @@ fn main() {
|
|||
"." => env::current_dir().unwrap(),
|
||||
path => PathBuf::from(path)
|
||||
.canonicalize()
|
||||
.expect(&format!("Cannot find root directory: {}", path)),
|
||||
.unwrap_or_else(|_| panic!("Cannot find root directory: {}", path)),
|
||||
};
|
||||
let config_file = match matches.value_of("config") {
|
||||
Some(path) => PathBuf::from(path),
|
||||
|
|
0
test_site/_dir_with_underscores/.gitkeep
Normal file
0
test_site/_dir_with_underscores/.gitkeep
Normal file
|
@ -4,16 +4,21 @@ highlight_code = true
|
|||
compile_sass = true
|
||||
generate_feed = true
|
||||
theme = "sample"
|
||||
slugify_paths = true
|
||||
|
||||
taxonomies = [
|
||||
{name = "categories", feed = true},
|
||||
{name = "podcast_authors", feed = true},
|
||||
]
|
||||
|
||||
extra_syntaxes = ["syntaxes"]
|
||||
|
||||
ignored_content = ["*/ignored.md"]
|
||||
|
||||
[slugify]
|
||||
paths = "on"
|
||||
taxonomies = "on"
|
||||
anchors = "on"
|
||||
|
||||
[link_checker]
|
||||
skip_prefixes = [
|
||||
"http://[2001:db8::]/",
|
||||
|
|
|
@ -5,6 +5,7 @@ date = 2017-01-01
|
|||
|
||||
[taxonomies]
|
||||
categories = ["a-category"]
|
||||
podcast_authors = ["Some Person"]
|
||||
+++
|
||||
|
||||
# A title
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
</article>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<!-- Next line is meant to test inner html chars (see https://github.com/getzola/zola/issues/1152) -->
|
||||
<p> <<< </p>
|
||||
{% endblock content %}
|
||||
|
||||
{% block script %}
|
||||
|
|
4
test_site/templates/podcast_authors/list.html
Normal file
4
test_site/templates/podcast_authors/list.html
Normal file
|
@ -0,0 +1,4 @@
|
|||
{% for term in terms %}
|
||||
{{ term.name }} {{ term.slug }} {{ term.pages | length }}
|
||||
{% endfor %}
|
||||
Current path: {{ current_path }}
|
10
test_site/templates/podcast_authors/single.html
Normal file
10
test_site/templates/podcast_authors/single.html
Normal file
|
@ -0,0 +1,10 @@
|
|||
Category: {{ term.name }}
|
||||
|
||||
|
||||
{% for page in term.pages %}
|
||||
<article>
|
||||
<h3 class="post__title"><a href="{{ page.permalink }}">{{ page.title }}</a></h3>
|
||||
</article>
|
||||
{% endfor %}
|
||||
|
||||
Current path: {{ current_path }}
|
Loading…
Reference in a new issue