commit
ad03aba945
|
@ -4,3 +4,5 @@
|
||||||
- Add some colours in console
|
- Add some colours in console
|
||||||
- Allow using a file other than config.toml for config
|
- Allow using a file other than config.toml for config
|
||||||
- Add sections to the index page context
|
- Add sections to the index page context
|
||||||
|
- Fix page rendering not working when containing `+++`
|
||||||
|
- Add shortcodes (see README for details)
|
||||||
|
|
40
Cargo.lock
generated
40
Cargo.lock
generated
|
@ -7,13 +7,13 @@ dependencies = [
|
||||||
"error-chain 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"error-chain 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
"glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"iron 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"iron 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"lazy_static 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"lazy_static 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"mount 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"mount 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"notify 4.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"notify 4.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"pulldown-cmark 0.0.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
"pulldown-cmark 0.0.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"regex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"regex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde 0.9.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde 0.9.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde_derive 0.9.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde_derive 0.9.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"slug 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"slug 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"staticfile 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"staticfile 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"syntect 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"syntect 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -325,7 +325,7 @@ dependencies = [
|
||||||
"conduit-mime-types 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"conduit-mime-types 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"error 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
"error 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"hyper 0.10.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"hyper 0.10.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"lazy_static 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"lazy_static 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"modifier 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"modifier 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"num_cpus 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"num_cpus 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -355,7 +355,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lazy_static"
|
name = "lazy_static"
|
||||||
version = "0.2.4"
|
version = "0.2.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -555,7 +555,7 @@ version = "1.2.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"lazy_static 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"lazy_static 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"onig_sys 61.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"onig_sys 61.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
@ -588,7 +588,7 @@ dependencies = [
|
||||||
"byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"chrono 0.2.25 (registry+https://github.com/rust-lang/crates.io-index)",
|
"chrono 0.2.25 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rustc-serialize 0.3.23 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rustc-serialize 0.3.23 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde 0.9.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde 0.9.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"xml-rs 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"xml-rs 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -688,12 +688,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "0.9.11"
|
version = "0.9.12"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_codegen_internals"
|
name = "serde_codegen_internals"
|
||||||
version = "0.14.1"
|
version = "0.14.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"syn 0.11.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
"syn 0.11.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -701,11 +701,11 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "0.9.11"
|
version = "0.9.12"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
"quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde_codegen_internals 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde_codegen_internals 0.14.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"syn 0.11.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
"syn 0.11.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -717,7 +717,7 @@ dependencies = [
|
||||||
"dtoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"dtoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"itoa 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"itoa 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
|
"num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde 0.9.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde 0.9.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -786,7 +786,7 @@ dependencies = [
|
||||||
"bitflags 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bitflags 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"flate2 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)",
|
"flate2 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"lazy_static 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"lazy_static 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"onig 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"onig 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"plist 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"plist 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"regex-syntax 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"regex-syntax 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -812,10 +812,10 @@ dependencies = [
|
||||||
"error-chain 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"error-chain 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
"glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"humansize 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"humansize 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"lazy_static 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"lazy_static 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"pest 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"pest 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"regex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"regex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde 0.9.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde 0.9.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde_json 0.9.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde_json 0.9.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"slug 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"slug 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"url 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"url 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -882,7 +882,7 @@ name = "toml"
|
||||||
version = "0.3.1"
|
version = "0.3.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde 0.9.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde 0.9.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1093,7 +1093,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
"checksum itoa 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eb2f404fbc66fd9aac13e998248505e7ecb2ad8e44ab6388684c5fb11c6c251c"
|
"checksum itoa 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eb2f404fbc66fd9aac13e998248505e7ecb2ad8e44ab6388684c5fb11c6c251c"
|
||||||
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
|
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
|
||||||
"checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a"
|
"checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a"
|
||||||
"checksum lazy_static 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "7291b1dd97d331f752620b02dfdbc231df7fc01bf282a00769e1cdb963c460dc"
|
"checksum lazy_static 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "4732c563b9a21a406565c4747daa7b46742f082911ae4753f390dc9ec7ee1a97"
|
||||||
"checksum lazycell 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ce12306c4739d86ee97c23139f3a34ddf0387bbf181bc7929d287025a8c3ef6b"
|
"checksum lazycell 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ce12306c4739d86ee97c23139f3a34ddf0387bbf181bc7929d287025a8c3ef6b"
|
||||||
"checksum libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)" = "88ee81885f9f04bff991e306fea7c1c60a5f0f9e409e99f6b40e3311a3363135"
|
"checksum libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)" = "88ee81885f9f04bff991e306fea7c1c60a5f0f9e409e99f6b40e3311a3363135"
|
||||||
"checksum log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "5141eca02775a762cc6cd564d8d2c50f67c0ea3a372cbf1c51592b3e029e10ad"
|
"checksum log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "5141eca02775a762cc6cd564d8d2c50f67c0ea3a372cbf1c51592b3e029e10ad"
|
||||||
|
@ -1134,9 +1134,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
"checksum semver 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)" = "d4f410fedcf71af0345d7607d246e7ad15faaadd49d240ee3b24e5dc21a820ac"
|
"checksum semver 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)" = "d4f410fedcf71af0345d7607d246e7ad15faaadd49d240ee3b24e5dc21a820ac"
|
||||||
"checksum sequence_trie 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c915714ca833b1d4d6b8f6a9d72a3ff632fe45b40a8d184ef79c81bec6327eed"
|
"checksum sequence_trie 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c915714ca833b1d4d6b8f6a9d72a3ff632fe45b40a8d184ef79c81bec6327eed"
|
||||||
"checksum serde 0.8.23 (registry+https://github.com/rust-lang/crates.io-index)" = "9dad3f759919b92c3068c696c15c3d17238234498bbdcc80f2c469606f948ac8"
|
"checksum serde 0.8.23 (registry+https://github.com/rust-lang/crates.io-index)" = "9dad3f759919b92c3068c696c15c3d17238234498bbdcc80f2c469606f948ac8"
|
||||||
"checksum serde 0.9.11 (registry+https://github.com/rust-lang/crates.io-index)" = "a702319c807c016e51f672e5c77d6f0b46afddd744b5e437d6b8436b888b458f"
|
"checksum serde 0.9.12 (registry+https://github.com/rust-lang/crates.io-index)" = "f023838e7e1878c679322dc7f66c3648bd33763a215fad752f378a623856898d"
|
||||||
"checksum serde_codegen_internals 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4d52006899f910528a10631e5b727973fe668f3228109d1707ccf5bad5490b6e"
|
"checksum serde_codegen_internals 0.14.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bc888bd283bd2420b16ad0d860e35ad8acb21941180a83a189bb2046f9d00400"
|
||||||
"checksum serde_derive 0.9.11 (registry+https://github.com/rust-lang/crates.io-index)" = "f15ea24bd037b2d64646b4d934fa99c649be66e3f7b29fb595a5543b212b1452"
|
"checksum serde_derive 0.9.12 (registry+https://github.com/rust-lang/crates.io-index)" = "ebb753639f6d55ba1acbcd330ccaf4d9f5862353ac2851e43eac63c2a5343a11"
|
||||||
"checksum serde_json 0.9.9 (registry+https://github.com/rust-lang/crates.io-index)" = "dbc45439552eb8fb86907a2c41c1fd0ef97458efb87ff7f878db466eb581824e"
|
"checksum serde_json 0.9.9 (registry+https://github.com/rust-lang/crates.io-index)" = "dbc45439552eb8fb86907a2c41c1fd0ef97458efb87ff7f878db466eb581824e"
|
||||||
"checksum sha1 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cc30b1e1e8c40c121ca33b86c23308a090d19974ef001b4bf6e61fd1a0fb095c"
|
"checksum sha1 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cc30b1e1e8c40c121ca33b86c23308a090d19974ef001b4bf6e61fd1a0fb095c"
|
||||||
"checksum slab 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d807fd58c4181bbabed77cb3b891ba9748241a552bcc5be698faaebefc54f46e"
|
"checksum slab 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d807fd58c4181bbabed77cb3b891ba9748241a552bcc5be698faaebefc54f46e"
|
||||||
|
|
43
README.md
43
README.md
|
@ -101,6 +101,9 @@ Gutenberg supports that pattern out of the box: you can create a folder, put a f
|
||||||
along with it that are NOT markdown.
|
along with it that are NOT markdown.
|
||||||
Those assets will be copied in the same folder when building so you can just use a relative path to use them.
|
Those assets will be copied in the same folder when building so you can just use a relative path to use them.
|
||||||
|
|
||||||
|
A summary is only defined if you put `<!-- more -->` in the content. If present in a page, the summary will be from
|
||||||
|
the start up to that tag.s
|
||||||
|
|
||||||
### Sections
|
### Sections
|
||||||
Sections represent a group of pages, for example a `tutorials` section of your site.
|
Sections represent a group of pages, for example a `tutorials` section of your site.
|
||||||
Sections are only created in Gutenberg when a file named `_index.md` is found in the `content` directory.
|
Sections are only created in Gutenberg when a file named `_index.md` is found in the `content` directory.
|
||||||
|
@ -147,6 +150,46 @@ built-in:
|
||||||
A gallery containing lots of themes at https://tmtheme-editor.herokuapp.com/#!/editor/theme/Agola%20Dark.
|
A gallery containing lots of themes at https://tmtheme-editor.herokuapp.com/#!/editor/theme/Agola%20Dark.
|
||||||
More themes can be easily added to gutenberg, just make a PR with the wanted theme.
|
More themes can be easily added to gutenberg, just make a PR with the wanted theme.
|
||||||
|
|
||||||
|
### Shortcodes
|
||||||
|
Gutenberg uses markdown for content but sometimes you want to insert some HTML, for example for a YouTube video.
|
||||||
|
Rather than copy/pasting the HTML around, Gutenberg supports shortcodes, allowing you to define templates using Tera and call those templates inside markdown.
|
||||||
|
|
||||||
|
#### Using a shortcode
|
||||||
|
There are 2 kinds of shortcodes: simple ones and those that take some content as body. All shortcodes need to be preceded by a blank line or they
|
||||||
|
will be contained in a paragraph.
|
||||||
|
|
||||||
|
Simple shortcodes are called the following way:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
{{ youtube(id="my_youtube_id") }}
|
||||||
|
```
|
||||||
|
|
||||||
|
Shortcodes with a body are called like so:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
{% quote(author="Me", link="https://google.com") %}
|
||||||
|
My quote
|
||||||
|
{% end %}
|
||||||
|
```
|
||||||
|
|
||||||
|
The shortcodes names are taken from the files they are defined in, for example a shortcode with the name youtube will try to render
|
||||||
|
the template at `templates/shortcodes/youtube.html`.
|
||||||
|
|
||||||
|
#### Built-in shortcodes
|
||||||
|
Gutenberg comes with a few built-in shortcodes:
|
||||||
|
|
||||||
|
- YouTube: embeds a YouTube player for the given YouTube `id`. Also takes an optional `autoplay` argument that can be set to `true`
|
||||||
|
if wanted
|
||||||
|
- Vimeo: embeds a Vimeo player for the given Vimeo `id`
|
||||||
|
- Gist: embeds a Github gist from the `url` given. Also takes an optional `file` argument if you only want to show one of the files.
|
||||||
|
|
||||||
|
#### Defining a shortcode
|
||||||
|
All shortcodes need to be in the `templates/shortcodes` folder and their files to end with `.html`.
|
||||||
|
Shortcodes templates are simple Tera templates, with all the args being directly accessible in the template.
|
||||||
|
|
||||||
|
In case of shortcodes with a body, the body will be passed as the `body` variable.
|
||||||
|
|
||||||
|
|
||||||
## Example sites
|
## Example sites
|
||||||
|
|
||||||
- [vincent.is](https://vincent.is): https://gitlab.com/Keats/vincent.is
|
- [vincent.is](https://vincent.is): https://gitlab.com/Keats/vincent.is
|
||||||
|
|
|
@ -26,7 +26,7 @@ mod site;
|
||||||
mod markdown;
|
mod markdown;
|
||||||
mod section;
|
mod section;
|
||||||
|
|
||||||
pub use site::Site;
|
pub use site::{Site, GUTENBERG_TERA};
|
||||||
pub use config::{Config, get_config};
|
pub use config::{Config, get_config};
|
||||||
pub use front_matter::{FrontMatter, split_content};
|
pub use front_matter::{FrontMatter, split_content};
|
||||||
pub use page::{Page, populate_previous_and_next_pages};
|
pub use page::{Page, populate_previous_and_next_pages};
|
||||||
|
|
352
src/markdown.rs
352
src/markdown.rs
|
@ -1,13 +1,18 @@
|
||||||
use std::borrow::Cow::Owned;
|
use std::borrow::Cow::Owned;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use pulldown_cmark as cmark;
|
use pulldown_cmark as cmark;
|
||||||
use self::cmark::{Parser, Event, Tag};
|
use self::cmark::{Parser, Event, Tag};
|
||||||
|
use regex::Regex;
|
||||||
use syntect::dumps::from_binary;
|
use syntect::dumps::from_binary;
|
||||||
use syntect::easy::HighlightLines;
|
use syntect::easy::HighlightLines;
|
||||||
use syntect::parsing::SyntaxSet;
|
use syntect::parsing::SyntaxSet;
|
||||||
use syntect::highlighting::ThemeSet;
|
use syntect::highlighting::ThemeSet;
|
||||||
use syntect::html::{start_coloured_html_snippet, styles_to_coloured_html, IncludeBackground};
|
use syntect::html::{start_coloured_html_snippet, styles_to_coloured_html, IncludeBackground};
|
||||||
|
use tera::{Tera, Context};
|
||||||
|
|
||||||
|
use config::Config;
|
||||||
|
use errors::{Result, ResultExt};
|
||||||
|
|
||||||
|
|
||||||
// We need to put those in a struct to impl Send and sync
|
// We need to put those in a struct to impl Send and sync
|
||||||
|
@ -20,117 +25,266 @@ unsafe impl Send for Setup {}
|
||||||
unsafe impl Sync for Setup {}
|
unsafe impl Sync for Setup {}
|
||||||
|
|
||||||
lazy_static!{
|
lazy_static!{
|
||||||
|
static ref SHORTCODE_RE: Regex = Regex::new(r#"\{(?:%|\{)\s+([[:alnum:]]+?)\(([[:alnum:]]+?="?.+?"?)\)\s+(?:%|\})\}"#).unwrap();
|
||||||
pub static ref SETUP: Setup = Setup {
|
pub static ref SETUP: Setup = Setup {
|
||||||
syntax_set: SyntaxSet::load_defaults_newlines(),
|
syntax_set: SyntaxSet::load_defaults_newlines(),
|
||||||
theme_set: from_binary(include_bytes!("../sublime_themes/all.themedump"))
|
theme_set: from_binary(include_bytes!("../sublime_themes/all.themedump"))
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A ShortCode that has a body
|
||||||
struct CodeHighlightingParser<'a> {
|
/// Called by having some content like {% ... %} body {% end %}
|
||||||
// The block we're currently highlighting
|
/// We need the struct to hold the data while we're processing the markdown
|
||||||
highlighter: Option<HighlightLines<'a>>,
|
#[derive(Debug)]
|
||||||
parser: Parser<'a>,
|
struct ShortCode {
|
||||||
theme: &'a str,
|
name: String,
|
||||||
|
args: HashMap<String, String>,
|
||||||
|
body: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> CodeHighlightingParser<'a> {
|
impl ShortCode {
|
||||||
pub fn new(parser: Parser<'a>, theme: &'a str) -> CodeHighlightingParser<'a> {
|
pub fn new(name: &str, args: HashMap<String, String>) -> ShortCode {
|
||||||
CodeHighlightingParser {
|
ShortCode {
|
||||||
highlighter: None,
|
name: name.to_string(),
|
||||||
parser: parser,
|
args: args,
|
||||||
theme: theme,
|
body: String::new(),
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Iterator for CodeHighlightingParser<'a> {
|
pub fn append(&mut self, text: &str) {
|
||||||
type Item = Event<'a>;
|
self.body.push_str(text)
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Event<'a>> {
|
|
||||||
// Not using pattern matching to reduce indentation levels
|
|
||||||
let next_opt = self.parser.next();
|
|
||||||
if next_opt.is_none() {
|
|
||||||
return None;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let item = next_opt.unwrap();
|
pub fn render(&self, tera: &Tera) -> Result<String> {
|
||||||
// Below we just look for the start of a code block and highlight everything
|
let mut context = Context::new();
|
||||||
// until we see the end of a code block.
|
for (key, value) in self.args.iter() {
|
||||||
// Everything else happens as normal in pulldown_cmark
|
context.add(key, value);
|
||||||
match item {
|
|
||||||
Event::Text(text) => {
|
|
||||||
// if we are in the middle of a code block
|
|
||||||
if let Some(ref mut highlighter) = self.highlighter {
|
|
||||||
let highlighted = &highlighter.highlight(&text);
|
|
||||||
let html = styles_to_coloured_html(highlighted, IncludeBackground::Yes);
|
|
||||||
Some(Event::Html(Owned(html)))
|
|
||||||
} else {
|
|
||||||
Some(Event::Text(text))
|
|
||||||
}
|
}
|
||||||
},
|
context.add("body", &self.body);
|
||||||
Event::Start(Tag::CodeBlock(ref info)) => {
|
let tpl_name = format!("shortcodes/{}.html", self.name);
|
||||||
let theme = &SETUP.theme_set.themes[self.theme];
|
tera.render(&tpl_name, &context)
|
||||||
let syntax = info
|
.chain_err(|| format!("Failed to render {} shortcode", self.name))
|
||||||
.split(' ')
|
|
||||||
.next()
|
|
||||||
.and_then(|lang| SETUP.syntax_set.find_syntax_by_token(lang))
|
|
||||||
.unwrap_or_else(|| SETUP.syntax_set.find_syntax_plain_text());
|
|
||||||
self.highlighter = Some(
|
|
||||||
HighlightLines::new(syntax, theme)
|
|
||||||
);
|
|
||||||
let snippet = start_coloured_html_snippet(theme);
|
|
||||||
Some(Event::Html(Owned(snippet)))
|
|
||||||
},
|
|
||||||
Event::End(Tag::CodeBlock(_)) => {
|
|
||||||
// reset highlight and close the code block
|
|
||||||
self.highlighter = None;
|
|
||||||
Some(Event::Html(Owned("</pre>".to_owned())))
|
|
||||||
},
|
|
||||||
_ => Some(item)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn markdown_to_html(content: &str, highlight_code: bool, highlight_theme: &str) -> String {
|
/// Parse a shortcode without a body
|
||||||
|
fn parse_shortcode(input: &str) -> (String, HashMap<String, String>) {
|
||||||
|
let mut args = HashMap::new();
|
||||||
|
let caps = SHORTCODE_RE.captures(input).unwrap();
|
||||||
|
// caps[0] is the full match
|
||||||
|
let name = &caps[1];
|
||||||
|
let arg_list = &caps[2];
|
||||||
|
for arg in arg_list.split(',') {
|
||||||
|
let bits = arg.split('=').collect::<Vec<_>>();
|
||||||
|
args.insert(bits[0].trim().to_string(), bits[1].replace("\"", ""));
|
||||||
|
}
|
||||||
|
|
||||||
|
(name.to_string(), args)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Renders a shortcode or return an error
|
||||||
|
fn render_simple_shortcode(tera: &Tera, name: &str, args: &HashMap<String, String>) -> Result<String> {
|
||||||
|
let mut context = Context::new();
|
||||||
|
for (key, value) in args.iter() {
|
||||||
|
context.add(key, value);
|
||||||
|
}
|
||||||
|
let tpl_name = format!("shortcodes/{}.html", name);
|
||||||
|
|
||||||
|
tera.render(&tpl_name, &context).chain_err(|| format!("Failed to render {} shortcode", name))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn markdown_to_html(content: &str, permalinks: &HashMap<String, String>, tera: &Tera, config: &Config) -> Result<String> {
|
||||||
// We try to be smart about highlighting code as it can be time-consuming
|
// We try to be smart about highlighting code as it can be time-consuming
|
||||||
// If the global config disables it, then we do nothing. However,
|
// If the global config disables it, then we do nothing. However,
|
||||||
// if we see a code block in the content, we assume that this page needs
|
// if we see a code block in the content, we assume that this page needs
|
||||||
// to be highlighted. It could potentially have false positive if the content
|
// to be highlighted. It could potentially have false positive if the content
|
||||||
// has ``` in it but that seems kind of unlikely
|
// has ``` in it but that seems kind of unlikely
|
||||||
let should_highlight = if highlight_code {
|
let should_highlight = if config.highlight_code.unwrap() {
|
||||||
content.contains("```")
|
content.contains("```")
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
};
|
};
|
||||||
|
let highlight_theme = config.highlight_theme.clone().unwrap();
|
||||||
|
// Set while parsing
|
||||||
|
let mut error = None;
|
||||||
|
let mut highlighter: Option<HighlightLines> = None;
|
||||||
|
let mut shortcode_block = None;
|
||||||
|
// shortcodes live outside of paragraph so we need to ensure we don't close
|
||||||
|
// a paragraph that has already been closed
|
||||||
|
let mut added_shortcode = false;
|
||||||
|
// Don't transform things that look like shortcodes in code blocks
|
||||||
|
let mut in_code_block = false;
|
||||||
|
// the rendered html
|
||||||
let mut html = String::new();
|
let mut html = String::new();
|
||||||
if should_highlight {
|
|
||||||
let parser = CodeHighlightingParser::new(Parser::new(content), highlight_theme);
|
{
|
||||||
cmark::html::push_html(&mut html, parser);
|
let parser = Parser::new(content).map(|event| match event {
|
||||||
|
Event::Text(text) => {
|
||||||
|
// if we are in the middle of a code block
|
||||||
|
if let Some(ref mut highlighter) = highlighter {
|
||||||
|
let highlighted = &highlighter.highlight(&text);
|
||||||
|
let html = styles_to_coloured_html(highlighted, IncludeBackground::Yes);
|
||||||
|
return Event::Html(Owned(html));
|
||||||
|
}
|
||||||
|
|
||||||
|
if in_code_block {
|
||||||
|
return Event::Text(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shortcode without body
|
||||||
|
if shortcode_block.is_none() && text.starts_with("{{") && text.ends_with("}}") {
|
||||||
|
if SHORTCODE_RE.is_match(&text) {
|
||||||
|
let (name, args) = parse_shortcode(&text);
|
||||||
|
added_shortcode = true;
|
||||||
|
match render_simple_shortcode(tera, &name, &args) {
|
||||||
|
Ok(s) => return Event::Html(Owned(format!("</p>{}", s))),
|
||||||
|
Err(e) => {
|
||||||
|
error = Some(e);
|
||||||
|
return Event::Html(Owned("".to_string()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// non-matching will be returned normally below
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shortcode with a body
|
||||||
|
if shortcode_block.is_none() && text.starts_with("{%") && text.ends_with("%}") {
|
||||||
|
if SHORTCODE_RE.is_match(&text) {
|
||||||
|
let (name, args) = parse_shortcode(&text);
|
||||||
|
shortcode_block = Some(ShortCode::new(&name, args));
|
||||||
|
}
|
||||||
|
// Don't return anything
|
||||||
|
return Event::Text(Owned("".to_string()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we have some text while in a shortcode, it's either the body
|
||||||
|
// or the end tag
|
||||||
|
if shortcode_block.is_some() {
|
||||||
|
if let Some(ref mut shortcode) = shortcode_block {
|
||||||
|
if text.trim() == "{% end %}" {
|
||||||
|
added_shortcode = true;
|
||||||
|
match shortcode.render(tera) {
|
||||||
|
Ok(s) => return Event::Html(Owned(format!("</p>{}", s))),
|
||||||
|
Err(e) => {
|
||||||
|
error = Some(e);
|
||||||
|
return Event::Html(Owned("".to_string()));
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
let parser = Parser::new(content);
|
shortcode.append(&text);
|
||||||
|
return Event::Html(Owned("".to_string()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Business as usual
|
||||||
|
Event::Text(text)
|
||||||
|
},
|
||||||
|
Event::Start(Tag::CodeBlock(ref info)) => {
|
||||||
|
in_code_block = true;
|
||||||
|
if !should_highlight {
|
||||||
|
return Event::Html(Owned("<pre><code>".to_owned()));
|
||||||
|
}
|
||||||
|
let theme = &SETUP.theme_set.themes[&highlight_theme];
|
||||||
|
let syntax = info
|
||||||
|
.split(' ')
|
||||||
|
.next()
|
||||||
|
.and_then(|lang| SETUP.syntax_set.find_syntax_by_token(lang))
|
||||||
|
.unwrap_or_else(|| SETUP.syntax_set.find_syntax_plain_text());
|
||||||
|
highlighter = Some(HighlightLines::new(syntax, theme));
|
||||||
|
let snippet = start_coloured_html_snippet(theme);
|
||||||
|
Event::Html(Owned(snippet))
|
||||||
|
},
|
||||||
|
Event::End(Tag::CodeBlock(_)) => {
|
||||||
|
in_code_block = false;
|
||||||
|
if !should_highlight{
|
||||||
|
return Event::Html(Owned("</code></pre>\n".to_owned()))
|
||||||
|
}
|
||||||
|
// reset highlight and close the code block
|
||||||
|
highlighter = None;
|
||||||
|
Event::Html(Owned("</pre>".to_owned()))
|
||||||
|
},
|
||||||
|
Event::Start(Tag::Code) => {
|
||||||
|
in_code_block = true;
|
||||||
|
event
|
||||||
|
},
|
||||||
|
Event::End(Tag::Code) => {
|
||||||
|
in_code_block = false;
|
||||||
|
event
|
||||||
|
},
|
||||||
|
Event::End(Tag::Paragraph) => {
|
||||||
|
if added_shortcode {
|
||||||
|
added_shortcode = false;
|
||||||
|
return Event::Html(Owned("".to_owned()));
|
||||||
|
}
|
||||||
|
event
|
||||||
|
},
|
||||||
|
Event::SoftBreak => {
|
||||||
|
if shortcode_block.is_some() {
|
||||||
|
return Event::Html(Owned("".to_owned()));
|
||||||
|
}
|
||||||
|
event
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
// println!("event = {:?}", event);
|
||||||
|
event
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
cmark::html::push_html(&mut html, parser);
|
cmark::html::push_html(&mut html, parser);
|
||||||
};
|
}
|
||||||
html
|
|
||||||
|
match error {
|
||||||
|
Some(e) => Err(e),
|
||||||
|
None => Ok(html.replace("<p></p>", "")),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{markdown_to_html};
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use site::GUTENBERG_TERA;
|
||||||
|
use tera::Tera;
|
||||||
|
|
||||||
|
use config::Config;
|
||||||
|
use super::{markdown_to_html, parse_shortcode};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_simple_shortcode_one_arg() {
|
||||||
|
let (name, args) = parse_shortcode(r#"{{ youtube(id="w7Ft2ymGmfc") }}"#);
|
||||||
|
assert_eq!(name, "youtube");
|
||||||
|
assert_eq!(args["id"], "w7Ft2ymGmfc");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_simple_shortcode_several_arg() {
|
||||||
|
let (name, args) = parse_shortcode(r#"{{ youtube(id="w7Ft2ymGmfc", autoplay=true) }}"#);
|
||||||
|
assert_eq!(name, "youtube");
|
||||||
|
assert_eq!(args["id"], "w7Ft2ymGmfc");
|
||||||
|
assert_eq!(args["autoplay"], "true");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_block_shortcode_several_arg() {
|
||||||
|
let (name, args) = parse_shortcode(r#"{% youtube(id="w7Ft2ymGmfc", autoplay=true) %}"#);
|
||||||
|
assert_eq!(name, "youtube");
|
||||||
|
assert_eq!(args["id"], "w7Ft2ymGmfc");
|
||||||
|
assert_eq!(args["autoplay"], "true");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_markdown_to_html_simple() {
|
fn test_markdown_to_html_simple() {
|
||||||
let res = markdown_to_html("# hello", true, "base16-ocean-dark");
|
let res = markdown_to_html("# hello", &HashMap::new(), &Tera::default(), &Config::default()).unwrap();
|
||||||
assert_eq!(res, "<h1>hello</h1>\n");
|
assert_eq!(res, "<h1>hello</h1>\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_markdown_to_html_code_block_highlighting_off() {
|
fn test_markdown_to_html_code_block_highlighting_off() {
|
||||||
let res = markdown_to_html("```\n$ gutenberg server\n```", false, "base16-ocean-dark");
|
let mut config = Config::default();
|
||||||
|
config.highlight_code = Some(false);
|
||||||
|
let res = markdown_to_html("```\n$ gutenberg server\n```", &HashMap::new(), &Tera::default(), &config).unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
res,
|
res,
|
||||||
"<pre><code>$ gutenberg server\n</code></pre>\n"
|
"<pre><code>$ gutenberg server\n</code></pre>\n"
|
||||||
|
@ -139,7 +293,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_markdown_to_html_code_block_no_lang() {
|
fn test_markdown_to_html_code_block_no_lang() {
|
||||||
let res = markdown_to_html("```\n$ gutenberg server\n$ ping\n```", true, "base16-ocean-dark");
|
let res = markdown_to_html("```\n$ gutenberg server\n$ ping\n```", &HashMap::new(), &Tera::default(), &Config::default()).unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
res,
|
res,
|
||||||
"<pre style=\"background-color:#2b303b\">\n<span style=\"background-color:#2b303b;color:#c0c5ce;\">$ gutenberg server\n</span><span style=\"background-color:#2b303b;color:#c0c5ce;\">$ ping\n</span></pre>"
|
"<pre style=\"background-color:#2b303b\">\n<span style=\"background-color:#2b303b;color:#c0c5ce;\">$ gutenberg server\n</span><span style=\"background-color:#2b303b;color:#c0c5ce;\">$ ping\n</span></pre>"
|
||||||
|
@ -148,19 +302,71 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_markdown_to_html_code_block_with_lang() {
|
fn test_markdown_to_html_code_block_with_lang() {
|
||||||
let res = markdown_to_html("```python\nlist.append(1)\n```", true, "base16-ocean-dark");
|
let res = markdown_to_html("```python\nlist.append(1)\n```", &HashMap::new(), &Tera::default(), &Config::default()).unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
res,
|
res,
|
||||||
"<pre style=\"background-color:#2b303b\">\n<span style=\"background-color:#2b303b;color:#c0c5ce;\">list</span><span style=\"background-color:#2b303b;color:#c0c5ce;\">.</span><span style=\"background-color:#2b303b;color:#bf616a;\">append</span><span style=\"background-color:#2b303b;color:#c0c5ce;\">(</span><span style=\"background-color:#2b303b;color:#d08770;\">1</span><span style=\"background-color:#2b303b;color:#c0c5ce;\">)</span><span style=\"background-color:#2b303b;color:#c0c5ce;\">\n</span></pre>"
|
"<pre style=\"background-color:#2b303b\">\n<span style=\"background-color:#2b303b;color:#c0c5ce;\">list</span><span style=\"background-color:#2b303b;color:#c0c5ce;\">.</span><span style=\"background-color:#2b303b;color:#bf616a;\">append</span><span style=\"background-color:#2b303b;color:#c0c5ce;\">(</span><span style=\"background-color:#2b303b;color:#d08770;\">1</span><span style=\"background-color:#2b303b;color:#c0c5ce;\">)</span><span style=\"background-color:#2b303b;color:#c0c5ce;\">\n</span></pre>"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_markdown_to_html_code_block_with_unknown_lang() {
|
fn test_markdown_to_html_code_block_with_unknown_lang() {
|
||||||
let res = markdown_to_html("```yolo\nlist.append(1)\n```", true, "base16-ocean-dark");
|
let res = markdown_to_html("```yolo\nlist.append(1)\n```", &HashMap::new(), &Tera::default(), &Config::default()).unwrap();
|
||||||
// defaults to plain text
|
// defaults to plain text
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
res,
|
res,
|
||||||
"<pre style=\"background-color:#2b303b\">\n<span style=\"background-color:#2b303b;color:#c0c5ce;\">list.append(1)\n</span></pre>"
|
"<pre style=\"background-color:#2b303b\">\n<span style=\"background-color:#2b303b;color:#c0c5ce;\">list.append(1)\n</span></pre>"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_markdown_to_html_with_shortcode() {
|
||||||
|
let res = markdown_to_html(r#"
|
||||||
|
Hello
|
||||||
|
|
||||||
|
{{ youtube(id="ub36ffWAqgQ") }}
|
||||||
|
"#, &HashMap::new(), &GUTENBERG_TERA, &Config::default()).unwrap();
|
||||||
|
assert!(res.contains("<p>Hello</p>\n<div >"));
|
||||||
|
assert!(res.contains(r#"<iframe src="https://www.youtube.com/embed/ub36ffWAqgQ""#));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_markdown_to_html_with_several_shortcode_in_row() {
|
||||||
|
let res = markdown_to_html(r#"
|
||||||
|
Hello
|
||||||
|
|
||||||
|
{{ youtube(id="ub36ffWAqgQ") }}
|
||||||
|
|
||||||
|
{{ youtube(id="ub36ffWAqgQ", autoplay=true) }}
|
||||||
|
|
||||||
|
{{ vimeo(id="210073083") }}
|
||||||
|
|
||||||
|
{{ gist(url="https://gist.github.com/Keats/32d26f699dcc13ebd41b") }}
|
||||||
|
|
||||||
|
"#, &HashMap::new(), &GUTENBERG_TERA, &Config::default()).unwrap();
|
||||||
|
assert!(res.contains("<p>Hello</p>\n<div >"));
|
||||||
|
assert!(res.contains(r#"<iframe src="https://www.youtube.com/embed/ub36ffWAqgQ""#));
|
||||||
|
assert!(res.contains(r#"<iframe src="https://www.youtube.com/embed/ub36ffWAqgQ?autoplay=1""#));
|
||||||
|
assert!(res.contains(r#"//player.vimeo.com/video/210073083""#));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_markdown_to_html_shortcode_in_code_block() {
|
||||||
|
let res = markdown_to_html(r#"```{{ youtube(id="w7Ft2ymGmfc") }}```"#, &HashMap::new(), &GUTENBERG_TERA, &Config::default()).unwrap();
|
||||||
|
assert_eq!(res, "<p><code>{{ youtube(id="w7Ft2ymGmfc") }}</code></p>\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_markdown_to_html_shortcode_with_body() {
|
||||||
|
let mut tera = Tera::default();
|
||||||
|
tera.extend(&GUTENBERG_TERA).unwrap();
|
||||||
|
tera.add_raw_template("shortcodes/quote.html", "<blockquote>{{ body }} - {{ author}}</blockquote>").unwrap();
|
||||||
|
let res = markdown_to_html(r#"
|
||||||
|
Hello
|
||||||
|
{% quote(author="Keats") %}
|
||||||
|
A quote
|
||||||
|
{% end %}
|
||||||
|
"#, &HashMap::new(), &tera, &Config::default()).unwrap();
|
||||||
|
assert_eq!(res, "<p>Hello\n</p><blockquote>A quote - Keats</blockquote>");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
32
src/page.rs
32
src/page.rs
|
@ -1,5 +1,6 @@
|
||||||
/// A page, can be a blog post or a basic page
|
/// A page, can be a blog post or a basic page
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::fs::{read_dir};
|
use std::fs::{read_dir};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::result::Result as StdResult;
|
use std::result::Result as StdResult;
|
||||||
|
@ -43,6 +44,8 @@ fn find_related_assets(path: &Path) -> Vec<PathBuf> {
|
||||||
pub struct Page {
|
pub struct Page {
|
||||||
/// The .md path
|
/// The .md path
|
||||||
pub file_path: PathBuf,
|
pub file_path: PathBuf,
|
||||||
|
/// The .md path, starting from the content directory, with / slashes
|
||||||
|
pub relative_path: String,
|
||||||
/// The parent directory of the file. Is actually the grand parent directory
|
/// The parent directory of the file. Is actually the grand parent directory
|
||||||
/// if it's an asset folder
|
/// if it's an asset folder
|
||||||
pub parent_path: PathBuf,
|
pub parent_path: PathBuf,
|
||||||
|
@ -88,6 +91,7 @@ impl Page {
|
||||||
pub fn new(meta: FrontMatter) -> Page {
|
pub fn new(meta: FrontMatter) -> Page {
|
||||||
Page {
|
Page {
|
||||||
file_path: PathBuf::new(),
|
file_path: PathBuf::new(),
|
||||||
|
relative_path: String::new(),
|
||||||
parent_path: PathBuf::new(),
|
parent_path: PathBuf::new(),
|
||||||
file_name: "".to_string(),
|
file_name: "".to_string(),
|
||||||
components: vec![],
|
components: vec![],
|
||||||
|
@ -131,16 +135,6 @@ impl Page {
|
||||||
page.parent_path = page.file_path.parent().unwrap().to_path_buf();
|
page.parent_path = page.file_path.parent().unwrap().to_path_buf();
|
||||||
page.raw_content = content;
|
page.raw_content = content;
|
||||||
|
|
||||||
let highlight_theme = config.highlight_theme.clone().unwrap();
|
|
||||||
page.content = markdown_to_html(&page.raw_content, config.highlight_code.unwrap(), &highlight_theme);
|
|
||||||
|
|
||||||
if page.raw_content.contains("<!-- more -->") {
|
|
||||||
page.summary = {
|
|
||||||
let summary = page.raw_content.splitn(2, "<!-- more -->").collect::<Vec<&str>>()[0];
|
|
||||||
markdown_to_html(summary, config.highlight_code.unwrap(), &highlight_theme)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let path = Path::new(file_path);
|
let path = Path::new(file_path);
|
||||||
page.file_name = path.file_stem().unwrap().to_string_lossy().to_string();
|
page.file_name = path.file_stem().unwrap().to_string_lossy().to_string();
|
||||||
|
|
||||||
|
@ -151,13 +145,14 @@ impl Page {
|
||||||
slugify(page.file_name.clone())
|
slugify(page.file_name.clone())
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
page.components = find_content_components(&page.file_path);
|
||||||
|
page.relative_path = format!("{}/{}.md", page.components.join("/"), page.file_name);
|
||||||
|
|
||||||
// 4. Find sections
|
// 4. Find sections
|
||||||
// Pages with custom urls exists outside of sections
|
// Pages with custom urls exists outside of sections
|
||||||
if let Some(ref u) = page.meta.url {
|
if let Some(ref u) = page.meta.url {
|
||||||
page.url = u.trim().to_string();
|
page.url = u.trim().to_string();
|
||||||
} else {
|
} else {
|
||||||
page.components = find_content_components(&page.file_path);
|
|
||||||
if !page.components.is_empty() {
|
if !page.components.is_empty() {
|
||||||
// If we have a folder with an asset, don't consider it as a component
|
// If we have a folder with an asset, don't consider it as a component
|
||||||
if page.file_name == "index" {
|
if page.file_name == "index" {
|
||||||
|
@ -193,6 +188,21 @@ impl Page {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// We need access to all pages url to render links relative to content
|
||||||
|
/// so that can't happen at the same time as parsing
|
||||||
|
pub fn render_markdown(&mut self, permalinks: &HashMap<String, String>, tera: &Tera, config: &Config) -> Result<()> {
|
||||||
|
self.content = markdown_to_html(&self.raw_content, permalinks, tera, config)?;
|
||||||
|
|
||||||
|
if self.raw_content.contains("<!-- more -->") {
|
||||||
|
self.summary = {
|
||||||
|
let summary = self.raw_content.splitn(2, "<!-- more -->").collect::<Vec<&str>>()[0];
|
||||||
|
markdown_to_html(summary, permalinks, tera, config)?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Renders the page using the default layout, unless specified in front-matter
|
/// Renders the page using the default layout, unless specified in front-matter
|
||||||
pub fn render_html(&self, tera: &Tera, config: &Config) -> Result<String> {
|
pub fn render_html(&self, tera: &Tera, config: &Config) -> Result<String> {
|
||||||
let tpl_name = match self.meta.template {
|
let tpl_name = match self.meta.template {
|
||||||
|
|
|
@ -15,6 +15,8 @@ use page::Page;
|
||||||
pub struct Section {
|
pub struct Section {
|
||||||
/// The _index.md full path
|
/// The _index.md full path
|
||||||
pub file_path: PathBuf,
|
pub file_path: PathBuf,
|
||||||
|
/// The .md path, starting from the content directory, with / slashes
|
||||||
|
pub relative_path: String,
|
||||||
/// Path of the directory containing the _index.md file
|
/// Path of the directory containing the _index.md file
|
||||||
pub parent_path: PathBuf,
|
pub parent_path: PathBuf,
|
||||||
/// The folder names from `content` to this section file
|
/// The folder names from `content` to this section file
|
||||||
|
@ -35,6 +37,7 @@ impl Section {
|
||||||
pub fn new(file_path: &Path, meta: FrontMatter) -> Section {
|
pub fn new(file_path: &Path, meta: FrontMatter) -> Section {
|
||||||
Section {
|
Section {
|
||||||
file_path: file_path.to_path_buf(),
|
file_path: file_path.to_path_buf(),
|
||||||
|
relative_path: "".to_string(),
|
||||||
parent_path: file_path.parent().unwrap().to_path_buf(),
|
parent_path: file_path.parent().unwrap().to_path_buf(),
|
||||||
components: vec![],
|
components: vec![],
|
||||||
url: "".to_string(),
|
url: "".to_string(),
|
||||||
|
@ -50,9 +53,9 @@ impl Section {
|
||||||
let mut section = Section::new(file_path, meta);
|
let mut section = Section::new(file_path, meta);
|
||||||
section.components = find_content_components(§ion.file_path);
|
section.components = find_content_components(§ion.file_path);
|
||||||
section.url = section.components.join("/");
|
section.url = section.components.join("/");
|
||||||
section.permalink = section.components.join("/");
|
|
||||||
|
|
||||||
section.permalink = config.make_permalink(§ion.url);
|
section.permalink = config.make_permalink(§ion.url);
|
||||||
|
section.relative_path = format!("{}/_index.md", section.components.join("/"));
|
||||||
|
|
||||||
|
|
||||||
Ok(section)
|
Ok(section)
|
||||||
}
|
}
|
||||||
|
|
44
src/site.rs
44
src/site.rs
|
@ -16,12 +16,16 @@ use section::{Section};
|
||||||
|
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref GUTENBERG_TERA: Tera = {
|
pub static ref GUTENBERG_TERA: Tera = {
|
||||||
let mut tera = Tera::default();
|
let mut tera = Tera::default();
|
||||||
tera.add_raw_templates(vec![
|
tera.add_raw_templates(vec![
|
||||||
("rss.xml", include_str!("templates/rss.xml")),
|
("rss.xml", include_str!("templates/rss.xml")),
|
||||||
("sitemap.xml", include_str!("templates/sitemap.xml")),
|
("sitemap.xml", include_str!("templates/sitemap.xml")),
|
||||||
("robots.txt", include_str!("templates/robots.txt")),
|
("robots.txt", include_str!("templates/robots.txt")),
|
||||||
|
|
||||||
|
("shortcodes/youtube.html", include_str!("templates/shortcodes/youtube.html")),
|
||||||
|
("shortcodes/vimeo.html", include_str!("templates/shortcodes/vimeo.html")),
|
||||||
|
("shortcodes/gist.html", include_str!("templates/shortcodes/gist.html")),
|
||||||
]).unwrap();
|
]).unwrap();
|
||||||
tera
|
tera
|
||||||
};
|
};
|
||||||
|
@ -58,7 +62,7 @@ pub struct Site {
|
||||||
pub config: Config,
|
pub config: Config,
|
||||||
pub pages: HashMap<PathBuf, Page>,
|
pub pages: HashMap<PathBuf, Page>,
|
||||||
pub sections: BTreeMap<PathBuf, Section>,
|
pub sections: BTreeMap<PathBuf, Section>,
|
||||||
pub templates: Tera,
|
pub tera: Tera,
|
||||||
live_reload: bool,
|
live_reload: bool,
|
||||||
output_path: PathBuf,
|
output_path: PathBuf,
|
||||||
pub tags: HashMap<String, Vec<PathBuf>>,
|
pub tags: HashMap<String, Vec<PathBuf>>,
|
||||||
|
@ -80,7 +84,7 @@ impl Site {
|
||||||
config: get_config(path, config_file),
|
config: get_config(path, config_file),
|
||||||
pages: HashMap::new(),
|
pages: HashMap::new(),
|
||||||
sections: BTreeMap::new(),
|
sections: BTreeMap::new(),
|
||||||
templates: tera,
|
tera: tera,
|
||||||
live_reload: false,
|
live_reload: false,
|
||||||
output_path: PathBuf::from("public"),
|
output_path: PathBuf::from("public"),
|
||||||
tags: HashMap::new(),
|
tags: HashMap::new(),
|
||||||
|
@ -119,6 +123,22 @@ impl Site {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A map of all .md files (section and pages) and their permalink
|
||||||
|
// We need that if there are relative links in the content that need to be resolved
|
||||||
|
let mut permalinks = HashMap::new();
|
||||||
|
|
||||||
|
for page in self.pages.values() {
|
||||||
|
permalinks.insert(page.relative_path.clone(), page.permalink.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
for section in self.sections.values() {
|
||||||
|
permalinks.insert(section.relative_path.clone(), section.permalink.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
for page in self.pages.values_mut() {
|
||||||
|
page.render_markdown(&permalinks, &self.tera, &self.config)?;
|
||||||
|
}
|
||||||
|
|
||||||
self.populate_sections();
|
self.populate_sections();
|
||||||
self.populate_tags_and_categories();
|
self.populate_tags_and_categories();
|
||||||
|
|
||||||
|
@ -262,7 +282,7 @@ impl Site {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn rebuild_after_template_change(&mut self) -> Result<()> {
|
pub fn rebuild_after_template_change(&mut self) -> Result<()> {
|
||||||
self.templates.full_reload()?;
|
self.tera.full_reload()?;
|
||||||
self.build_pages()
|
self.build_pages()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -291,7 +311,7 @@ impl Site {
|
||||||
create_directory(¤t_path)?;
|
create_directory(¤t_path)?;
|
||||||
|
|
||||||
// Finally, create a index.html file there with the page rendered
|
// Finally, create a index.html file there with the page rendered
|
||||||
let output = page.render_html(&self.templates, &self.config)?;
|
let output = page.render_html(&self.tera, &self.config)?;
|
||||||
create_file(current_path.join("index.html"), &self.inject_livereload(output))?;
|
create_file(current_path.join("index.html"), &self.inject_livereload(output))?;
|
||||||
|
|
||||||
// Copy any asset we found previously into the same directory as the index.html
|
// Copy any asset we found previously into the same directory as the index.html
|
||||||
|
@ -318,7 +338,7 @@ impl Site {
|
||||||
context.add("pages", &populate_previous_and_next_pages(&pages, false));
|
context.add("pages", &populate_previous_and_next_pages(&pages, false));
|
||||||
context.add("sections", &self.sections.values().collect::<Vec<&Section>>());
|
context.add("sections", &self.sections.values().collect::<Vec<&Section>>());
|
||||||
context.add("config", &self.config);
|
context.add("config", &self.config);
|
||||||
let index = self.templates.render("index.html", &context)?;
|
let index = self.tera.render("index.html", &context)?;
|
||||||
create_file(public.join("index.html"), &self.inject_livereload(index))?;
|
create_file(public.join("index.html"), &self.inject_livereload(index))?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -343,7 +363,7 @@ impl Site {
|
||||||
fn render_robots(&self) -> Result<()> {
|
fn render_robots(&self) -> Result<()> {
|
||||||
create_file(
|
create_file(
|
||||||
self.output_path.join("robots.txt"),
|
self.output_path.join("robots.txt"),
|
||||||
&self.templates.render("robots.txt", &Context::new())?
|
&self.tera.render("robots.txt", &Context::new())?
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -382,7 +402,7 @@ impl Site {
|
||||||
context.add(name, &sorted_items);
|
context.add(name, &sorted_items);
|
||||||
context.add("config", &self.config);
|
context.add("config", &self.config);
|
||||||
// And render it immediately
|
// And render it immediately
|
||||||
let list_output = self.templates.render(list_tpl_name, &context)?;
|
let list_output = self.tera.render(list_tpl_name, &context)?;
|
||||||
create_file(output_path.join("index.html"), &self.inject_livereload(list_output))?;
|
create_file(output_path.join("index.html"), &self.inject_livereload(list_output))?;
|
||||||
|
|
||||||
// Now, each individual item
|
// Now, each individual item
|
||||||
|
@ -400,7 +420,7 @@ impl Site {
|
||||||
context.add(&format!("{}_slug", var_name), &slug);
|
context.add(&format!("{}_slug", var_name), &slug);
|
||||||
context.add("pages", &pages);
|
context.add("pages", &pages);
|
||||||
context.add("config", &self.config);
|
context.add("config", &self.config);
|
||||||
let single_output = self.templates.render(single_tpl_name, &context)?;
|
let single_output = self.tera.render(single_tpl_name, &context)?;
|
||||||
|
|
||||||
create_directory(&output_path.join(&slug))?;
|
create_directory(&output_path.join(&slug))?;
|
||||||
create_file(
|
create_file(
|
||||||
|
@ -439,7 +459,7 @@ impl Site {
|
||||||
}
|
}
|
||||||
context.add("tags", &tags);
|
context.add("tags", &tags);
|
||||||
|
|
||||||
let sitemap = self.templates.render("sitemap.xml", &context)?;
|
let sitemap = self.tera.render("sitemap.xml", &context)?;
|
||||||
|
|
||||||
create_file(self.output_path.join("sitemap.xml"), &sitemap)?;
|
create_file(self.output_path.join("sitemap.xml"), &sitemap)?;
|
||||||
|
|
||||||
|
@ -470,7 +490,7 @@ impl Site {
|
||||||
};
|
};
|
||||||
context.add("feed_url", &rss_feed_url);
|
context.add("feed_url", &rss_feed_url);
|
||||||
|
|
||||||
let sitemap = self.templates.render("rss.xml", &context)?;
|
let sitemap = self.tera.render("rss.xml", &context)?;
|
||||||
|
|
||||||
create_file(self.output_path.join("rss.xml"), &sitemap)?;
|
create_file(self.output_path.join("rss.xml"), &sitemap)?;
|
||||||
|
|
||||||
|
@ -490,7 +510,7 @@ impl Site {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let output = section.render_html(&self.templates, &self.config)?;
|
let output = section.render_html(&self.tera, &self.config)?;
|
||||||
create_file(output_path.join("index.html"), &self.inject_livereload(output))?;
|
create_file(output_path.join("index.html"), &self.inject_livereload(output))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
3
src/templates/shortcodes/gist.html
Normal file
3
src/templates/shortcodes/gist.html
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<div>
|
||||||
|
<script src="{{ url }}.js{% if file %}?file={{file}}{% endif %}"></script>
|
||||||
|
</div>
|
4
src/templates/shortcodes/vimeo.html
Normal file
4
src/templates/shortcodes/vimeo.html
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
<div {% if class %}class="{{class}}"{% endif %}>
|
||||||
|
<iframe src="//player.vimeo.com/video/{{id}}" webkitallowfullscreen mozallowfullscreen allowfullscreen>
|
||||||
|
</iframe>
|
||||||
|
</div>
|
4
src/templates/shortcodes/youtube.html
Normal file
4
src/templates/shortcodes/youtube.html
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
<div {% if class %}class="{{class}}"{% endif %}>
|
||||||
|
<iframe src="https://www.youtube.com/embed/{{id}}{% if autoplay %}?autoplay=1{% endif %}" webkitallowfullscreen mozallowfullscreen allowfullscreen>
|
||||||
|
</iframe>
|
||||||
|
</div>
|
|
@ -5,3 +5,10 @@ date = "2017-04-01"
|
||||||
+++
|
+++
|
||||||
|
|
||||||
A simple page
|
A simple page
|
||||||
|
|
||||||
|
{{ youtube(id="e1C9kpMV2e8") }}
|
||||||
|
{{ youtube(id="e1C9kpMV2e8", autoplay=true) }}
|
||||||
|
|
||||||
|
{{ vimeo(id="210073083") }}
|
||||||
|
|
||||||
|
{{ gist(url="https://gist.github.com/Keats/32d26f699dcc13ebd41b") }}
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
extern crate gutenberg;
|
extern crate gutenberg;
|
||||||
|
extern crate tera;
|
||||||
extern crate tempdir;
|
extern crate tempdir;
|
||||||
|
|
||||||
use tempdir::TempDir;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
|
use tempdir::TempDir;
|
||||||
|
use tera::Tera;
|
||||||
|
|
||||||
use gutenberg::{Page, Config};
|
use gutenberg::{Page, Config};
|
||||||
|
|
||||||
|
|
||||||
|
@ -20,7 +23,8 @@ slug = "hello-world"
|
||||||
Hello world"#;
|
Hello world"#;
|
||||||
let res = Page::parse(Path::new("post.md"), content, &Config::default());
|
let res = Page::parse(Path::new("post.md"), content, &Config::default());
|
||||||
assert!(res.is_ok());
|
assert!(res.is_ok());
|
||||||
let page = res.unwrap();
|
let mut page = res.unwrap();
|
||||||
|
page.render_markdown(&HashMap::default(), &Tera::default(), &Config::default()).unwrap();
|
||||||
|
|
||||||
assert_eq!(page.meta.title, "Hello".to_string());
|
assert_eq!(page.meta.title, "Hello".to_string());
|
||||||
assert_eq!(page.meta.slug.unwrap(), "hello-world".to_string());
|
assert_eq!(page.meta.slug.unwrap(), "hello-world".to_string());
|
||||||
|
@ -39,7 +43,8 @@ slug = "hello-world"
|
||||||
Hello world"#;
|
Hello world"#;
|
||||||
let res = Page::parse(Path::new("content/posts/intro.md"), content, &Config::default());
|
let res = Page::parse(Path::new("content/posts/intro.md"), content, &Config::default());
|
||||||
assert!(res.is_ok());
|
assert!(res.is_ok());
|
||||||
let page = res.unwrap();
|
let mut page = res.unwrap();
|
||||||
|
page.render_markdown(&HashMap::default(), &Tera::default(), &Config::default()).unwrap();
|
||||||
assert_eq!(page.components, vec!["posts".to_string()]);
|
assert_eq!(page.components, vec!["posts".to_string()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,7 +59,8 @@ slug = "hello-world"
|
||||||
Hello world"#;
|
Hello world"#;
|
||||||
let res = Page::parse(Path::new("content/posts/intro/start.md"), content, &Config::default());
|
let res = Page::parse(Path::new("content/posts/intro/start.md"), content, &Config::default());
|
||||||
assert!(res.is_ok());
|
assert!(res.is_ok());
|
||||||
let page = res.unwrap();
|
let mut page = res.unwrap();
|
||||||
|
page.render_markdown(&HashMap::default(), &Tera::default(), &Config::default()).unwrap();
|
||||||
assert_eq!(page.components, vec!["posts".to_string(), "intro".to_string()]);
|
assert_eq!(page.components, vec!["posts".to_string(), "intro".to_string()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,7 +77,8 @@ Hello world"#;
|
||||||
conf.base_url = "http://hello.com/".to_string();
|
conf.base_url = "http://hello.com/".to_string();
|
||||||
let res = Page::parse(Path::new("content/posts/intro/start.md"), content, &conf);
|
let res = Page::parse(Path::new("content/posts/intro/start.md"), content, &conf);
|
||||||
assert!(res.is_ok());
|
assert!(res.is_ok());
|
||||||
let page = res.unwrap();
|
let mut page = res.unwrap();
|
||||||
|
page.render_markdown(&HashMap::default(), &Tera::default(), &Config::default()).unwrap();
|
||||||
assert_eq!(page.url, "posts/intro/hello-world");
|
assert_eq!(page.url, "posts/intro/hello-world");
|
||||||
assert_eq!(page.permalink, "http://hello.com/posts/intro/hello-world");
|
assert_eq!(page.permalink, "http://hello.com/posts/intro/hello-world");
|
||||||
}
|
}
|
||||||
|
@ -89,7 +96,8 @@ Hello world"#;
|
||||||
conf.base_url = "http://hello.com".to_string();
|
conf.base_url = "http://hello.com".to_string();
|
||||||
let res = Page::parse(Path::new("content/posts/intro/hello-world.md"), content, &conf);
|
let res = Page::parse(Path::new("content/posts/intro/hello-world.md"), content, &conf);
|
||||||
assert!(res.is_ok());
|
assert!(res.is_ok());
|
||||||
let page = res.unwrap();
|
let mut page = res.unwrap();
|
||||||
|
page.render_markdown(&HashMap::default(), &Tera::default(), &Config::default()).unwrap();
|
||||||
assert_eq!(page.url, "posts/intro/hello-world");
|
assert_eq!(page.url, "posts/intro/hello-world");
|
||||||
assert_eq!(page.permalink, format!("{}{}", conf.base_url, "/posts/intro/hello-world"));
|
assert_eq!(page.permalink, format!("{}{}", conf.base_url, "/posts/intro/hello-world"));
|
||||||
}
|
}
|
||||||
|
@ -105,7 +113,8 @@ slug = "hello-world"
|
||||||
Hello world"#;
|
Hello world"#;
|
||||||
let res = Page::parse(Path::new("start.md"), content, &Config::default());
|
let res = Page::parse(Path::new("start.md"), content, &Config::default());
|
||||||
assert!(res.is_ok());
|
assert!(res.is_ok());
|
||||||
let page = res.unwrap();
|
let mut page = res.unwrap();
|
||||||
|
page.render_markdown(&HashMap::default(), &Tera::default(), &Config::default()).unwrap();
|
||||||
assert_eq!(page.url, "hello-world");
|
assert_eq!(page.url, "hello-world");
|
||||||
assert_eq!(page.permalink, format!("{}{}", Config::default().base_url, "hello-world"));
|
assert_eq!(page.permalink, format!("{}{}", Config::default().base_url, "hello-world"));
|
||||||
}
|
}
|
||||||
|
@ -132,7 +141,8 @@ description = "hey there"
|
||||||
Hello world"#;
|
Hello world"#;
|
||||||
let res = Page::parse(Path::new("file with space.md"), content, &Config::default());
|
let res = Page::parse(Path::new("file with space.md"), content, &Config::default());
|
||||||
assert!(res.is_ok());
|
assert!(res.is_ok());
|
||||||
let page = res.unwrap();
|
let mut page = res.unwrap();
|
||||||
|
page.render_markdown(&HashMap::default(), &Tera::default(), &Config::default()).unwrap();
|
||||||
assert_eq!(page.slug, "file-with-space");
|
assert_eq!(page.slug, "file-with-space");
|
||||||
assert_eq!(page.permalink, format!("{}{}", Config::default().base_url, "file-with-space"));
|
assert_eq!(page.permalink, format!("{}{}", Config::default().base_url, "file-with-space"));
|
||||||
}
|
}
|
||||||
|
@ -147,7 +157,8 @@ description = "hey there"
|
||||||
Hello world"#;
|
Hello world"#;
|
||||||
let res = Page::parse(Path::new(" file with space.md"), content, &Config::default());
|
let res = Page::parse(Path::new(" file with space.md"), content, &Config::default());
|
||||||
assert!(res.is_ok());
|
assert!(res.is_ok());
|
||||||
let page = res.unwrap();
|
let mut page = res.unwrap();
|
||||||
|
page.render_markdown(&HashMap::default(), &Tera::default(), &Config::default()).unwrap();
|
||||||
assert_eq!(page.slug, "file-with-space");
|
assert_eq!(page.slug, "file-with-space");
|
||||||
assert_eq!(page.permalink, format!("{}{}", Config::default().base_url, "file-with-space"));
|
assert_eq!(page.permalink, format!("{}{}", Config::default().base_url, "file-with-space"));
|
||||||
}
|
}
|
||||||
|
@ -162,7 +173,8 @@ description = "hey there"
|
||||||
Hello world"#;
|
Hello world"#;
|
||||||
let res = Page::parse(Path::new("hello.md"), content, &Config::default());
|
let res = Page::parse(Path::new("hello.md"), content, &Config::default());
|
||||||
assert!(res.is_ok());
|
assert!(res.is_ok());
|
||||||
let page = res.unwrap();
|
let mut page = res.unwrap();
|
||||||
|
page.render_markdown(&HashMap::default(), &Tera::default(), &Config::default()).unwrap();
|
||||||
let (word_count, reading_time) = page.get_reading_analytics();
|
let (word_count, reading_time) = page.get_reading_analytics();
|
||||||
assert_eq!(word_count, 2);
|
assert_eq!(word_count, 2);
|
||||||
assert_eq!(reading_time, 0);
|
assert_eq!(reading_time, 0);
|
||||||
|
@ -181,7 +193,8 @@ Hello world"#.to_string();
|
||||||
}
|
}
|
||||||
let res = Page::parse(Path::new("hello.md"), &content, &Config::default());
|
let res = Page::parse(Path::new("hello.md"), &content, &Config::default());
|
||||||
assert!(res.is_ok());
|
assert!(res.is_ok());
|
||||||
let page = res.unwrap();
|
let mut page = res.unwrap();
|
||||||
|
page.render_markdown(&HashMap::default(), &Tera::default(), &Config::default()).unwrap();
|
||||||
let (word_count, reading_time) = page.get_reading_analytics();
|
let (word_count, reading_time) = page.get_reading_analytics();
|
||||||
assert_eq!(word_count, 2002);
|
assert_eq!(word_count, 2002);
|
||||||
assert_eq!(reading_time, 10);
|
assert_eq!(reading_time, 10);
|
||||||
|
@ -197,7 +210,8 @@ description = "hey there"
|
||||||
Hello world"#.to_string();
|
Hello world"#.to_string();
|
||||||
let res = Page::parse(Path::new("hello.md"), &content, &Config::default());
|
let res = Page::parse(Path::new("hello.md"), &content, &Config::default());
|
||||||
assert!(res.is_ok());
|
assert!(res.is_ok());
|
||||||
let page = res.unwrap();
|
let mut page = res.unwrap();
|
||||||
|
page.render_markdown(&HashMap::default(), &Tera::default(), &Config::default()).unwrap();
|
||||||
assert_eq!(page.summary, "");
|
assert_eq!(page.summary, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -213,7 +227,8 @@ Hello world
|
||||||
"#.to_string();
|
"#.to_string();
|
||||||
let res = Page::parse(Path::new("hello.md"), &content, &Config::default());
|
let res = Page::parse(Path::new("hello.md"), &content, &Config::default());
|
||||||
assert!(res.is_ok());
|
assert!(res.is_ok());
|
||||||
let page = res.unwrap();
|
let mut page = res.unwrap();
|
||||||
|
page.render_markdown(&HashMap::default(), &Tera::default(), &Config::default()).unwrap();
|
||||||
assert_eq!(page.summary, "<p>Hello world</p>\n");
|
assert_eq!(page.summary, "<p>Hello world</p>\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -232,7 +247,8 @@ Hey there
|
||||||
config.highlight_code = Some(true);
|
config.highlight_code = Some(true);
|
||||||
let res = Page::parse(Path::new("hello.md"), &content, &config);
|
let res = Page::parse(Path::new("hello.md"), &content, &config);
|
||||||
assert!(res.is_ok());
|
assert!(res.is_ok());
|
||||||
let page = res.unwrap();
|
let mut page = res.unwrap();
|
||||||
|
page.render_markdown(&HashMap::default(), &Tera::default(), &Config::default()).unwrap();
|
||||||
assert!(page.content.starts_with("<pre"));
|
assert!(page.content.starts_with("<pre"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,7 @@ fn test_can_parse_site() {
|
||||||
|
|
||||||
// Make sure the page with a url doesn't have any sections
|
// Make sure the page with a url doesn't have any sections
|
||||||
let url_post = &site.pages[&posts_path.join("fixed-url.md")];
|
let url_post = &site.pages[&posts_path.join("fixed-url.md")];
|
||||||
assert!(url_post.components.is_empty());
|
assert_eq!(url_post.url, "a-fixed-url");
|
||||||
|
|
||||||
// Make sure the article in a folder with only asset doesn't get counted as a section
|
// Make sure the article in a folder with only asset doesn't get counted as a section
|
||||||
let asset_folder_post = &site.pages[&posts_path.join("with-assets").join("index.md")];
|
let asset_folder_post = &site.pages[&posts_path.join("with-assets").join("index.md")];
|
||||||
|
|
Loading…
Reference in a new issue