├── .github └── workflows │ └── test.yml ├── .gitignore ├── Cargo.toml ├── LICENSE.txt ├── README.md ├── examples └── definition.rs ├── src ├── definition.rs ├── definition │ ├── extra.rs │ └── iter.rs └── lib.rs └── tests ├── inlines.txt ├── markdown-reader-more.txt ├── pandoc_compatibility.rs ├── tables.txt ├── tables_uppercase.txt ├── testsuite.txt └── testsuite_uppercase.txt /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | on: [push, pull_request] 2 | 3 | jobs: 4 | test: 5 | name: Test 6 | runs-on: ubuntu-latest 7 | steps: 8 | - name: Checkout sources 9 | uses: actions/checkout@v2 10 | 11 | - name: Install stable toolchain 12 | uses: actions-rs/toolchain@v1 13 | with: 14 | profile: minimal 15 | toolchain: stable 16 | override: true 17 | 18 | - name: Run cargo fmt 19 | uses: actions-rs/cargo@v1 20 | with: 21 | command: fmt 22 | args: --all -- --check 23 | 24 | - name: Run cargo clippy 25 | uses: actions-rs/cargo@v1 26 | with: 27 | command: clippy 28 | args: -- -D warnings 29 | 30 | - name: Setup pandoc 31 | uses: r-lib/actions/setup-pandoc@v2 32 | with: 33 | pandoc-version: 3.1 34 | 35 | - name: Run cargo test 36 | uses: actions-rs/cargo@v1 37 | with: 38 | command: test 39 | 40 | - name: Run cargo build 41 | uses: actions-rs/cargo@v1 42 | with: 43 | command: build 44 | 45 | - name: Run examples 46 | uses: actions-rs/cargo@v1 47 | with: 48 | command: run 49 | args: --example definition 50 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *~ 3 | 4 | *.bk 5 | 6 | /target 7 | /Cargo.lock 8 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "pandoc_types" 3 | version = "0.6.0" 4 | authors = ["Elliott Slaughter "] 5 | license = "Apache-2.0" 6 | description = "Rust port of pandoc-types" 7 | repository = "https://github.com/elliottslaughter/rust-pandoc-types" 8 | keywords = ["pandoc", "pandoc-types"] 9 | categories = ["text-processing"] 10 | readme = "README.md" 11 | include = ["Cargo.toml", "src/**/*.rs", "tests/**/*.rs", "tests/**/*.txt", "examples/**/*.rs", "README.md", "LICENSE.txt"] 12 | edition = "2021" 13 | 14 | [dependencies] 15 | serde = {version = "1.0", features=["derive"]} 16 | serde_tuple = "0.5.0" 17 | [dev-dependencies] 18 | serde_json = "1.0" 19 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rust port of pandoc-types ![Build status](https://github.com/elliottslaughter/rust-pandoc-types/actions/workflows/test.yml/badge.svg) 2 | 3 | This library provides a Rust port of the [pandoc-types Haskell 4 | package](https://hackage.haskell.org/package/pandoc-types). 5 | 6 | To install, add the following to your `Cargo.toml`: 7 | 8 | ``` 9 | [dependencies] 10 | pandoc_types = "0.6" 11 | ``` 12 | 13 | ## What this library is for 14 | 15 | The purpose of pandoc-types is to allow Rust programs to natively 16 | manipulate [Pandoc](http://pandoc.org/) documents. Using this library, 17 | Rust programs should be able to create and modify Pandoc documents in 18 | a principled way (i.e. via ASTs, not text). This library can also be 19 | used along with [serde_json](https://github.com/serde-rs/json) to 20 | serialize and deserialize Pandoc documents to and from Pandoc's JSON 21 | format. 22 | 23 | ## What this library is NOT for 24 | 25 | This library does *not* provide a way of calling the Pandoc executable 26 | itself. If that's what you're looking for, consider the 27 | [rust-pandoc](https://github.com/oli-obk/rust-pandoc) library. 28 | 29 | ## Compatibility 30 | 31 | The current version is **compatible with Haskell pandoc-types 32 | 1.23**. This is the most recent version at the time of writing. 33 | 34 | If you require support for a previous version of pandoc-types, please 35 | refer to the following support table to determine which version to 36 | use: 37 | 38 | | Rust Version | pandoc-types Version | pandoc Versions | 39 | | ------------ | -------------------- | --------------- | 40 | | 0.6 | 1.23 | 3.0– | 41 | | 0.5 | 1.22 | 2.11–2.19 | 42 | | 0.4 | 1.22 | 2.11–2.19 | 43 | | 0.3 | 1.20 | 2.8–2.9 | 44 | | 0.2 | 1.17 | 1.18–2.7 | 45 | | 0.1 | 1.17 | 1.18–2.7 | 46 | 47 | ## Supported modules 48 | 49 | The following modules from pandoc-types are supported: 50 | 51 | * Haskell `Text.Pandoc.Definition` (as `pandoc_types::definition` in Rust) 52 | 53 | Note that `Text.Pandoc.JSON` is unnecessary in Rust because all types 54 | implement `Serialize` and `Deserialize` from 55 | [serde](https://github.com/serde-rs/serde) and can be used directly 56 | with [serde_json](https://github.com/serde-rs/json). 57 | 58 | ## Example usage 59 | 60 | ```rust 61 | let para = Block::Para(vec![Inline::Str("b".to_owned())]); 62 | 63 | let s = serde_json::to_string(¶)?; 64 | println!("serialized = {}", s); 65 | 66 | let d: Block = serde_json::from_str(&s)?; 67 | println!("deserialized = {:?}", d); 68 | ``` 69 | 70 | For a full example, see [examples/definition.rs](examples/definition.rs). 71 | 72 | ## License 73 | 74 | This library is licensed under the Apache License, Version 2.0 (see 75 | [LICENSE.txt](LICENSE.txt)). 76 | -------------------------------------------------------------------------------- /examples/definition.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use serde_json; 4 | 5 | use pandoc_types::definition::*; 6 | 7 | fn main() { 8 | let mut meta = HashMap::default(); 9 | meta.insert( 10 | "title".to_owned(), 11 | MetaValue::MetaInlines(vec![Inline::Str("a".to_owned())]), 12 | ); 13 | 14 | let doc = Pandoc { 15 | meta, 16 | blocks: vec![ 17 | Block::Header( 18 | 1, 19 | Attr { 20 | identifier: "a".to_owned(), 21 | classes: vec![], 22 | attributes: vec![], 23 | }, 24 | vec![Inline::Str("a".to_owned())], 25 | ), 26 | Block::Para(vec![Inline::Str("b".to_owned())]), 27 | ], 28 | }; 29 | 30 | let s = serde_json::to_string(&doc).unwrap(); 31 | println!("serialized = {}", s); 32 | 33 | let d: Pandoc = serde_json::from_str(&s).unwrap(); 34 | println!("deserialized = {:?}", d); 35 | } 36 | -------------------------------------------------------------------------------- /src/definition.rs: -------------------------------------------------------------------------------- 1 | //! This module contatins the types from [Text.Pandoc.Definition] ported to Rust. 2 | //! 3 | //! [Text.Pandoc.Definition]: https://hackage.haskell.org/package/pandoc-types/docs/Text-Pandoc-Definition.html 4 | use std::collections::HashMap; 5 | 6 | pub use iter::*; 7 | use serde::ser::SerializeStruct; 8 | use serde::{Deserialize, Deserializer, Serialize, Serializer}; 9 | use serde_tuple::{Deserialize_tuple, Serialize_tuple}; 10 | 11 | pub mod extra; 12 | mod iter; 13 | 14 | const PANDOC_API_VERSION: [i32; 2] = [1, 23]; 15 | 16 | #[derive(Debug, Clone, PartialEq, Default)] 17 | pub struct Pandoc { 18 | pub blocks: Vec, 19 | pub meta: HashMap, 20 | } 21 | 22 | impl Serialize for Pandoc { 23 | fn serialize(&self, serializer: S) -> Result 24 | where 25 | S: Serializer, 26 | { 27 | let mut value = serializer.serialize_struct("Pandoc", 3)?; 28 | value.serialize_field("pandoc-api-version", &PANDOC_API_VERSION)?; 29 | value.serialize_field("meta", &self.meta)?; 30 | value.serialize_field("blocks", &self.blocks)?; 31 | value.end() 32 | } 33 | } 34 | 35 | impl<'a> Deserialize<'a> for Pandoc { 36 | fn deserialize(deserializer: D) -> Result 37 | where 38 | D: Deserializer<'a>, 39 | { 40 | #[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] 41 | #[serde(rename = "Pandoc")] 42 | struct Inner { 43 | meta: HashMap, 44 | blocks: Vec, 45 | #[serde(rename = "pandoc-api-version")] 46 | version: Vec, 47 | } 48 | 49 | let value = Inner::deserialize(deserializer)?; 50 | 51 | if value.version.len() < 2 52 | || value.version[0] != PANDOC_API_VERSION[0] 53 | || value.version[1] != PANDOC_API_VERSION[1] 54 | { 55 | return Err(serde::de::Error::custom(format!( 56 | "expected pandoc-api-version to start with {},{}", 57 | PANDOC_API_VERSION[0], PANDOC_API_VERSION[1] 58 | ))); 59 | } 60 | 61 | Ok(Pandoc { 62 | meta: value.meta, 63 | blocks: value.blocks, 64 | }) 65 | } 66 | } 67 | 68 | #[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] 69 | #[serde(tag = "t", content = "c")] 70 | pub enum MetaValue { 71 | MetaMap(HashMap), 72 | MetaList(Vec), 73 | MetaBool(bool), 74 | MetaString(String), 75 | MetaInlines(Vec), 76 | MetaBlocks(Vec), 77 | } 78 | 79 | #[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] 80 | #[serde(tag = "t", content = "c")] 81 | pub enum Block { 82 | /// Plain text, not a paragraph 83 | Plain(Vec), 84 | /// Paragraph 85 | Para(Vec), 86 | /// Multiple non-breaking lines 87 | LineBlock(Vec>), 88 | /// Code block (literal) with attributes 89 | CodeBlock(Attr, String), 90 | /// Raw block 91 | RawBlock(Format, String), 92 | /// Block quote 93 | BlockQuote(Vec), 94 | /// Ordered list (attributes and a list of items, each a list of blocks) 95 | OrderedList(ListAttributes, Vec>), 96 | /// Bullet list (list of items, each a list of blocks) 97 | BulletList(Vec>), 98 | /// Definition list. Each list item is a pair consisting of a term (a list of inlines) and one or more definitions (each a list of blocks) 99 | DefinitionList(Vec<(Vec, Vec>)>), 100 | /// Header - level (integer) and text (inlines) 101 | Header(i32, Attr, Vec), 102 | /// Horizontal rule 103 | HorizontalRule, 104 | /// Table 105 | Table(Table), 106 | /// Figure 107 | Figure(Attr, Caption, Vec), 108 | /// Generic block container with attributes 109 | Div(Attr, Vec), 110 | /// Nothing 111 | Null, 112 | } 113 | 114 | #[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, PartialEq, Default)] 115 | pub struct Table { 116 | pub attr: Attr, 117 | pub caption: Caption, 118 | pub colspecs: Vec, 119 | pub head: TableHead, 120 | pub bodies: Vec, 121 | pub foot: TableFoot, 122 | } 123 | 124 | #[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] 125 | #[serde(tag = "t", content = "c")] 126 | pub enum Inline { 127 | /// Text 128 | Str(String), 129 | /// Emphasized text 130 | Emph(Vec), 131 | /// Underlined text 132 | Underline(Vec), 133 | /// Strongly emphasized text 134 | Strong(Vec), 135 | /// Strikeout text 136 | Strikeout(Vec), 137 | /// Superscripted text 138 | Superscript(Vec), 139 | /// Subscripted text 140 | Subscript(Vec), 141 | /// Small caps text 142 | SmallCaps(Vec), 143 | /// Quoted text 144 | Quoted(QuoteType, Vec), 145 | /// Citation 146 | Cite(Vec, Vec), 147 | /// Inline code 148 | Code(Attr, String), 149 | /// Inter-word space 150 | Space, 151 | /// Soft line break 152 | SoftBreak, 153 | /// Hard line break 154 | LineBreak, 155 | /// TeX math 156 | Math(MathType, String), 157 | /// Raw inline 158 | RawInline(Format, String), 159 | /// Hyperlink: alt text (list of inlines), target 160 | Link(Attr, Vec, Target), 161 | /// Image: alt text (list of inlines), target 162 | Image(Attr, Vec, Target), 163 | /// Footnote or endnote 164 | Note(Vec), 165 | /// Generic inline container with attributes 166 | Span(Attr, Vec), 167 | } 168 | 169 | #[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] 170 | #[serde(tag = "t", content = "c")] 171 | pub enum Alignment { 172 | AlignLeft, 173 | AlignRight, 174 | AlignCenter, 175 | AlignDefault, 176 | } 177 | 178 | impl Default for Alignment { 179 | fn default() -> Self { 180 | Self::AlignDefault 181 | } 182 | } 183 | 184 | #[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] 185 | #[serde(tag = "t", content = "c")] 186 | pub enum ColWidth { 187 | ColWidth(f64), 188 | ColWidthDefault, 189 | } 190 | 191 | impl Default for ColWidth { 192 | fn default() -> Self { 193 | Self::ColWidthDefault 194 | } 195 | } 196 | 197 | #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)] 198 | pub struct ColSpec(pub Alignment, pub ColWidth); 199 | 200 | #[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, PartialEq)] 201 | pub struct Row { 202 | pub attr: Attr, 203 | pub cells: Vec, 204 | } 205 | 206 | #[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, PartialEq, Default)] 207 | pub struct TableHead { 208 | pub attr: Attr, 209 | pub rows: Vec, 210 | } 211 | 212 | #[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, PartialEq, Default)] 213 | pub struct TableBody { 214 | pub attr: Attr, 215 | pub row_head_columns: i32, 216 | pub head: Vec, 217 | pub body: Vec, 218 | } 219 | 220 | #[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, PartialEq, Default)] 221 | pub struct TableFoot { 222 | pub attr: Attr, 223 | pub rows: Vec, 224 | } 225 | 226 | #[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, PartialEq, Default)] 227 | pub struct Caption { 228 | pub short: Option>, 229 | pub long: Vec, 230 | } 231 | 232 | #[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, PartialEq)] 233 | pub struct Cell { 234 | pub attr: Attr, 235 | pub align: Alignment, 236 | pub row_span: i32, 237 | pub col_span: i32, 238 | pub content: Vec, 239 | } 240 | 241 | impl Default for Cell { 242 | fn default() -> Self { 243 | Self { 244 | attr: Default::default(), 245 | align: Default::default(), 246 | row_span: 1, 247 | col_span: 1, 248 | content: Default::default(), 249 | } 250 | } 251 | } 252 | 253 | #[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, PartialEq)] 254 | pub struct ListAttributes { 255 | pub start_number: i32, 256 | pub style: ListNumberStyle, 257 | pub delim: ListNumberDelim, 258 | } 259 | 260 | impl Default for ListAttributes { 261 | fn default() -> Self { 262 | Self { 263 | start_number: 1, 264 | style: ListNumberStyle::default(), 265 | delim: ListNumberDelim::default(), 266 | } 267 | } 268 | } 269 | 270 | #[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] 271 | #[serde(tag = "t", content = "c")] 272 | pub enum ListNumberStyle { 273 | DefaultStyle, 274 | Example, 275 | Decimal, 276 | LowerRoman, 277 | UpperRoman, 278 | LowerAlpha, 279 | UpperAlpha, 280 | } 281 | 282 | impl Default for ListNumberStyle { 283 | fn default() -> Self { 284 | Self::DefaultStyle 285 | } 286 | } 287 | 288 | #[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] 289 | #[serde(tag = "t", content = "c")] 290 | pub enum ListNumberDelim { 291 | DefaultDelim, 292 | Period, 293 | OneParen, 294 | TwoParens, 295 | } 296 | 297 | impl Default for ListNumberDelim { 298 | fn default() -> Self { 299 | Self::DefaultDelim 300 | } 301 | } 302 | 303 | #[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] 304 | pub struct Format(pub String); 305 | 306 | #[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, PartialEq, Default)] 307 | pub struct Attr { 308 | pub identifier: String, 309 | pub classes: Vec, 310 | pub attributes: Vec<(String, String)>, 311 | } 312 | 313 | #[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] 314 | #[serde(tag = "t", content = "c")] 315 | pub enum QuoteType { 316 | SingleQuote, 317 | DoubleQuote, 318 | } 319 | 320 | #[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, PartialEq)] 321 | pub struct Target { 322 | pub url: String, 323 | pub title: String, 324 | } 325 | 326 | #[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] 327 | #[serde(tag = "t", content = "c")] 328 | pub enum MathType { 329 | DisplayMath, 330 | InlineMath, 331 | } 332 | 333 | #[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] 334 | #[serde(rename_all = "camelCase")] 335 | pub struct Citation { 336 | pub citation_id: String, 337 | pub citation_prefix: Vec, 338 | pub citation_suffix: Vec, 339 | pub citation_mode: CitationMode, 340 | pub citation_note_num: i32, 341 | pub citation_hash: i32, 342 | } 343 | 344 | #[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] 345 | #[serde(tag = "t", content = "c")] 346 | pub enum CitationMode { 347 | AuthorInText, 348 | SuppressAuthor, 349 | NormalCitation, 350 | } 351 | 352 | #[cfg(test)] 353 | mod tests { 354 | use super::*; 355 | use serde_json::json; 356 | 357 | #[test] 358 | fn version() { 359 | assert!(serde_json::from_value::(json!({ 360 | "pandoc-api-version": PANDOC_API_VERSION, 361 | "meta": {}, 362 | "blocks": [], 363 | })) 364 | .is_ok()); 365 | 366 | assert!(serde_json::from_value::(json!({ 367 | "pandoc-api-version": [], 368 | "meta": {}, 369 | "blocks": [], 370 | })) 371 | .is_err()); 372 | 373 | assert!(serde_json::from_value::(json!({ 374 | "pandoc-api-version": [PANDOC_API_VERSION[0], PANDOC_API_VERSION[1] + 1], 375 | "meta": {}, 376 | "blocks": [], 377 | })) 378 | .is_err()); 379 | } 380 | } 381 | -------------------------------------------------------------------------------- /src/definition/extra.rs: -------------------------------------------------------------------------------- 1 | //! Additional types that aren't present in the Haskell package but are useful. 2 | use super::{Block, Inline}; 3 | 4 | /// A utility type to provide better error messages when 5 | /// an array of blocks doesn't match an expected pattern. 6 | /// 7 | /// For example: 8 | /// 9 | /// ``` 10 | /// use pandoc_types::definition::{extra::BlockType, Attr, Block, Inline}; 11 | /// 12 | /// fn parse_as_para(blocks: &[Block]) -> Result<&Vec, String> { 13 | /// match blocks { 14 | /// [Block::Para(inlines)] => Ok(inlines), 15 | /// unexpected => { 16 | /// return Err(format!( 17 | /// "expected [Para] but found {:?}", 18 | /// unexpected.iter().map(BlockType::from).collect::>(), 19 | /// )) 20 | /// } 21 | /// } 22 | /// } 23 | /// 24 | /// assert_eq!( 25 | /// parse_as_para(&[Block::CodeBlock( 26 | /// Attr::default(), 27 | /// "some very long string".into() 28 | /// )]), 29 | /// Err("expected [Para] but found [CodeBlock]".into()) 30 | /// ); 31 | /// ``` 32 | /// 33 | /// Note that if we didn't use `BlockType` the error message would include 34 | /// the contained data of the CodeBlock which isn't actually relevant. 35 | #[derive(PartialEq, Eq, Clone, Debug)] 36 | pub enum BlockType { 37 | Plain, 38 | Para, 39 | LineBlock, 40 | CodeBlock, 41 | RawBlock, 42 | BlockQuote, 43 | OrderedList, 44 | BulletList, 45 | DefinitionList, 46 | Header, 47 | HorizontalRule, 48 | Table, 49 | Figure, 50 | Div, 51 | Null, 52 | } 53 | 54 | impl From<&Block> for BlockType { 55 | fn from(block: &Block) -> Self { 56 | match block { 57 | Block::Plain(_) => Self::Plain, 58 | Block::Para(_) => Self::Para, 59 | Block::LineBlock(_) => Self::LineBlock, 60 | Block::CodeBlock(_, _) => Self::CodeBlock, 61 | Block::RawBlock(_, _) => Self::RawBlock, 62 | Block::BlockQuote(_) => Self::BlockQuote, 63 | Block::OrderedList(_, _) => Self::OrderedList, 64 | Block::BulletList(_) => Self::BulletList, 65 | Block::DefinitionList(_) => Self::DefinitionList, 66 | Block::Header(_, _, _) => Self::Header, 67 | Block::HorizontalRule => Self::HorizontalRule, 68 | Block::Table(_) => Self::Table, 69 | Block::Figure(_, _, _) => Self::Figure, 70 | Block::Div(_, _) => Self::Div, 71 | Block::Null => Self::Null, 72 | } 73 | } 74 | } 75 | 76 | /// A utility type to provide better error messages when 77 | /// an array of inlines doesn't match an expected pattern. 78 | /// 79 | /// For example: 80 | /// 81 | /// ``` 82 | /// use pandoc_types::definition::{extra::InlineType, Attr, Block, Inline, Target}; 83 | /// 84 | /// fn parse_as_link(inlines: &[Inline]) -> Result<(&Vec, &Target), String> { 85 | /// match inlines { 86 | /// [Inline::Link(_, label, target)] => Ok((label, target)), 87 | /// unexpected => { 88 | /// return Err(format!( 89 | /// "expected [Link] but found {:?}", 90 | /// unexpected.iter().map(InlineType::from).collect::>(), 91 | /// )) 92 | /// } 93 | /// } 94 | /// } 95 | /// 96 | /// assert_eq!( 97 | /// parse_as_link(&[Inline::Underline(vec![Inline::Str( 98 | /// "some very long string".into() 99 | /// )],)]), 100 | /// Err("expected [Link] but found [Underline]".into()) 101 | /// ); 102 | /// ``` 103 | /// 104 | /// Note that if we didn't use `InlineType` the error message would include 105 | /// the contained text of the Underline which isn't actually relevant. 106 | #[derive(PartialEq, Eq, Clone, Debug)] 107 | pub enum InlineType { 108 | Str, 109 | Emph, 110 | Underline, 111 | Strong, 112 | Strikeout, 113 | Superscript, 114 | Subscript, 115 | SmallCaps, 116 | Quoted, 117 | Cite, 118 | Code, 119 | Space, 120 | SoftBreak, 121 | LineBreak, 122 | Math, 123 | RawInline, 124 | Link, 125 | Image, 126 | Note, 127 | Span, 128 | } 129 | 130 | impl From<&Inline> for InlineType { 131 | fn from(inline: &Inline) -> Self { 132 | match inline { 133 | Inline::Str(_) => Self::Str, 134 | Inline::Emph(_) => Self::Emph, 135 | Inline::Underline(_) => Self::Underline, 136 | Inline::Strong(_) => Self::Strong, 137 | Inline::Strikeout(_) => Self::Strikeout, 138 | Inline::Superscript(_) => Self::Superscript, 139 | Inline::Subscript(_) => Self::Subscript, 140 | Inline::SmallCaps(_) => Self::SmallCaps, 141 | Inline::Quoted(_, _) => Self::Quoted, 142 | Inline::Cite(_, _) => Self::Cite, 143 | Inline::Code(_, _) => Self::Code, 144 | Inline::Space => Self::Space, 145 | Inline::SoftBreak => Self::SoftBreak, 146 | Inline::LineBreak => Self::LineBreak, 147 | Inline::Math(_, _) => Self::Math, 148 | Inline::RawInline(_, _) => Self::RawInline, 149 | Inline::Link(_, _, _) => Self::Link, 150 | Inline::Image(_, _, _) => Self::Image, 151 | Inline::Note(_) => Self::Note, 152 | Inline::Span(_, _) => Self::Span, 153 | } 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /src/definition/iter.rs: -------------------------------------------------------------------------------- 1 | use super::{Block, Caption, Format, Inline, Pandoc, Row, Table}; 2 | 3 | /// A trait to iterate over the immediately contained blocks in a type. 4 | pub trait IterBlocks<'a> { 5 | type Iter: Iterator; 6 | type IterMut: Iterator; 7 | 8 | /// Returns an iterator over the immediately contained blocks. 9 | fn iter_blocks(&'a self) -> Self::Iter; 10 | 11 | /// Returns an iterator over the immediately contained blocks, allowing each block to be modified. 12 | fn iter_blocks_mut(&'a mut self) -> Self::IterMut; 13 | } 14 | 15 | /// A trait to iterate over the immediately contained inlines in a type. 16 | pub trait IterInlines<'a> { 17 | type Iter: Iterator; 18 | type IterMut: Iterator; 19 | 20 | /// Returns an iterator over the immediately contained inlines. 21 | fn iter_inlines(&'a self) -> Self::Iter; 22 | 23 | /// Returns an iterator over the immediately contained inlines, allowing each inline to be modified. 24 | fn iter_inlines_mut(&'a mut self) -> Self::IterMut; 25 | } 26 | 27 | impl<'a> IterBlocks<'a> for Block { 28 | type Iter = Box + 'a>; 29 | type IterMut = Box + 'a>; 30 | 31 | fn iter_blocks(&'a self) -> Self::Iter { 32 | Box::new(match self { 33 | Block::BlockQuote(blocks) => IterTypes::Iter(blocks.iter()), 34 | Block::Figure(_, _, blocks) => IterTypes::Iter(blocks.iter()), 35 | Block::Div(_, blocks) => IterTypes::Iter(blocks.iter()), 36 | Block::BulletList(items) => IterTypes::FlattenIter(items.iter().flatten()), 37 | Block::OrderedList(_, items) => IterTypes::FlattenIter(items.iter().flatten()), 38 | Block::DefinitionList(definitions) => { 39 | IterTypes::FlatMap(definitions.iter().flat_map(|(_dt, dd)| dd.iter().flatten())) 40 | } 41 | Block::Table(table) => IterTypes::Table(table.iter_blocks()), 42 | Block::Plain(_) => IterTypes::Empty, 43 | Block::Para(_) => IterTypes::Empty, 44 | Block::LineBlock(_) => IterTypes::Empty, 45 | Block::CodeBlock(_, _) => IterTypes::Empty, 46 | Block::RawBlock(_, _) => IterTypes::Empty, 47 | Block::Header(_, _, _) => IterTypes::Empty, 48 | Block::HorizontalRule => IterTypes::Empty, 49 | Block::Null => IterTypes::Empty, 50 | }) 51 | } 52 | 53 | fn iter_blocks_mut(&'a mut self) -> Self::IterMut { 54 | Box::new(match self { 55 | Block::BlockQuote(blocks) => IterTypes::Iter(blocks.iter_mut()), 56 | Block::Figure(_, _, blocks) => IterTypes::Iter(blocks.iter_mut()), 57 | Block::Div(_, blocks) => IterTypes::Iter(blocks.iter_mut()), 58 | Block::BulletList(items) => IterTypes::FlattenIter(items.iter_mut().flatten()), 59 | Block::OrderedList(_, items) => IterTypes::FlattenIter(items.iter_mut().flatten()), 60 | Block::DefinitionList(definitions) => IterTypes::FlatMap( 61 | definitions 62 | .iter_mut() 63 | .flat_map(|(_dt, dd)| dd.iter_mut().flatten()), 64 | ), 65 | Block::Table(table) => IterTypes::Table(table.iter_blocks_mut()), 66 | Block::Plain(_) => IterTypes::Empty, 67 | Block::Para(_) => IterTypes::Empty, 68 | Block::LineBlock(_) => IterTypes::Empty, 69 | Block::CodeBlock(_, _) => IterTypes::Empty, 70 | Block::RawBlock(_, _) => IterTypes::Empty, 71 | Block::Header(_, _, _) => IterTypes::Empty, 72 | Block::HorizontalRule => IterTypes::Empty, 73 | Block::Null => IterTypes::Empty, 74 | }) 75 | } 76 | } 77 | 78 | impl<'a> IterBlocks<'a> for Table { 79 | type Iter = Box + 'a>; 80 | type IterMut = Box + 'a>; 81 | 82 | fn iter_blocks(&'a self) -> Self::Iter { 83 | Box::new( 84 | self.caption.long.iter().chain( 85 | self.head 86 | .rows 87 | .iter() 88 | .chain( 89 | self.bodies 90 | .iter() 91 | .flat_map(|b| b.head.iter().chain(b.body.iter())) 92 | .chain(self.foot.rows.iter()), 93 | ) 94 | .flat_map(|Row { cells, .. }| { 95 | cells.iter().flat_map(|cell| cell.content.iter()) 96 | }), 97 | ), 98 | ) 99 | } 100 | 101 | fn iter_blocks_mut(&'a mut self) -> Self::IterMut { 102 | Box::new( 103 | self.caption.long.iter_mut().chain( 104 | self.head 105 | .rows 106 | .iter_mut() 107 | .chain( 108 | self.bodies 109 | .iter_mut() 110 | .flat_map(|b| b.head.iter_mut().chain(b.body.iter_mut())) 111 | .chain(self.foot.rows.iter_mut()), 112 | ) 113 | .flat_map(|Row { cells, .. }| { 114 | cells.iter_mut().flat_map(|cell| cell.content.iter_mut()) 115 | }), 116 | ), 117 | ) 118 | } 119 | } 120 | 121 | /// Currently only yields blocks for `Inline::Note`. 122 | impl<'a> IterBlocks<'a> for Inline { 123 | type Iter = std::slice::Iter<'a, Block>; 124 | type IterMut = std::slice::IterMut<'a, Block>; 125 | 126 | fn iter_blocks(&'a self) -> Self::Iter { 127 | match self { 128 | Inline::Note(blocks) => blocks.iter(), 129 | _ => [].iter(), 130 | } 131 | } 132 | 133 | fn iter_blocks_mut(&'a mut self) -> Self::IterMut { 134 | match self { 135 | Inline::Note(blocks) => blocks.iter_mut(), 136 | _ => [].iter_mut(), 137 | } 138 | } 139 | } 140 | 141 | impl<'a> IterBlocks<'a> for Pandoc { 142 | type Iter = std::slice::Iter<'a, Block>; 143 | type IterMut = std::slice::IterMut<'a, Block>; 144 | 145 | fn iter_blocks(&'a self) -> Self::Iter { 146 | self.blocks.iter() 147 | } 148 | 149 | fn iter_blocks_mut(&'a mut self) -> Self::IterMut { 150 | self.blocks.iter_mut() 151 | } 152 | } 153 | 154 | impl<'a> IterInlines<'a> for Block { 155 | type Iter = Box + 'a>; 156 | type IterMut = Box + 'a>; 157 | 158 | fn iter_inlines(&'a self) -> Self::Iter { 159 | Box::new(match self { 160 | Block::Plain(inlines) => IterTypes::Iter(inlines.iter()), 161 | Block::Para(inlines) => IterTypes::Iter(inlines.iter()), 162 | Block::LineBlock(lines) => IterTypes::FlattenIter(lines.iter().flatten()), 163 | Block::DefinitionList(definitions) => { 164 | IterTypes::FlatMap(definitions.iter().flat_map(|(dt, _)| dt)) 165 | } 166 | Block::Header(_, _, inlines) => IterTypes::Iter(inlines.iter()), 167 | Block::Table(Table { 168 | caption: Caption { short, .. }, 169 | .. 170 | }) => IterTypes::Table(short.iter().flatten()), 171 | Block::CodeBlock(_, _) => IterTypes::Empty, 172 | Block::RawBlock(_, _) => IterTypes::Empty, 173 | Block::BlockQuote(_) => IterTypes::Empty, 174 | Block::OrderedList(_, _) => IterTypes::Empty, 175 | Block::BulletList(_) => IterTypes::Empty, 176 | Block::HorizontalRule => IterTypes::Empty, 177 | Block::Figure(_, _, _) => IterTypes::Empty, 178 | Block::Div(_, _) => IterTypes::Empty, 179 | Block::Null => IterTypes::Empty, 180 | }) 181 | } 182 | 183 | fn iter_inlines_mut(&'a mut self) -> Self::IterMut { 184 | Box::new(match self { 185 | Block::Plain(inlines) => IterTypes::Iter(inlines.iter_mut()), 186 | Block::Para(inlines) => IterTypes::Iter(inlines.iter_mut()), 187 | Block::LineBlock(lines) => IterTypes::FlattenIter(lines.iter_mut().flatten()), 188 | Block::DefinitionList(definitions) => { 189 | IterTypes::FlatMap(definitions.iter_mut().flat_map(|(dt, _)| dt)) 190 | } 191 | Block::Header(_, _, inlines) => IterTypes::Iter(inlines.iter_mut()), 192 | Block::Table(Table { 193 | caption: Caption { short, .. }, 194 | .. 195 | }) => IterTypes::Table(short.iter_mut().flatten()), 196 | Block::CodeBlock(_, _) => IterTypes::Empty, 197 | Block::RawBlock(_, _) => IterTypes::Empty, 198 | Block::BlockQuote(_) => IterTypes::Empty, 199 | Block::OrderedList(_, _) => IterTypes::Empty, 200 | Block::BulletList(_) => IterTypes::Empty, 201 | Block::HorizontalRule => IterTypes::Empty, 202 | Block::Figure(_, _, _) => IterTypes::Empty, 203 | Block::Div(_, _) => IterTypes::Empty, 204 | Block::Null => IterTypes::Empty, 205 | }) 206 | } 207 | } 208 | 209 | impl<'a> IterInlines<'a> for Inline { 210 | type Iter = std::slice::Iter<'a, Inline>; 211 | type IterMut = std::slice::IterMut<'a, Inline>; 212 | 213 | fn iter_inlines(&'a self) -> Self::Iter { 214 | match self { 215 | Inline::Emph(inlines) => inlines.iter(), 216 | Inline::Underline(inlines) => inlines.iter(), 217 | Inline::Strong(inlines) => inlines.iter(), 218 | Inline::Strikeout(inlines) => inlines.iter(), 219 | Inline::Superscript(inlines) => inlines.iter(), 220 | Inline::Subscript(inlines) => inlines.iter(), 221 | Inline::SmallCaps(inlines) => inlines.iter(), 222 | Inline::Quoted(_, inlines) => inlines.iter(), 223 | Inline::Cite(_, inlines) => inlines.iter(), 224 | Inline::Link(_, inlines, _) => inlines.iter(), 225 | Inline::Image(_, inlines, _) => inlines.iter(), 226 | Inline::Span(_, inlines) => inlines.iter(), 227 | Inline::Str(_) => [].iter(), 228 | Inline::Code(_, _) => [].iter(), 229 | Inline::Space => [].iter(), 230 | Inline::SoftBreak => [].iter(), 231 | Inline::LineBreak => [].iter(), 232 | Inline::Math(_, _) => [].iter(), 233 | Inline::RawInline(_, _) => [].iter(), 234 | Inline::Note(_) => [].iter(), 235 | } 236 | } 237 | 238 | fn iter_inlines_mut(&'a mut self) -> Self::IterMut { 239 | match self { 240 | Inline::Emph(inlines) => inlines.iter_mut(), 241 | Inline::Underline(inlines) => inlines.iter_mut(), 242 | Inline::Strong(inlines) => inlines.iter_mut(), 243 | Inline::Strikeout(inlines) => inlines.iter_mut(), 244 | Inline::Superscript(inlines) => inlines.iter_mut(), 245 | Inline::Subscript(inlines) => inlines.iter_mut(), 246 | Inline::SmallCaps(inlines) => inlines.iter_mut(), 247 | Inline::Quoted(_, inlines) => inlines.iter_mut(), 248 | Inline::Cite(_, inlines) => inlines.iter_mut(), 249 | Inline::Link(_, inlines, _) => inlines.iter_mut(), 250 | Inline::Image(_, inlines, _) => inlines.iter_mut(), 251 | Inline::Span(_, inlines) => inlines.iter_mut(), 252 | Inline::Str(_) => [].iter_mut(), 253 | Inline::Code(_, _) => [].iter_mut(), 254 | Inline::Space => [].iter_mut(), 255 | Inline::SoftBreak => [].iter_mut(), 256 | Inline::LineBreak => [].iter_mut(), 257 | Inline::Math(_, _) => [].iter_mut(), 258 | Inline::RawInline(_, _) => [].iter_mut(), 259 | Inline::Note(_) => [].iter_mut(), 260 | } 261 | } 262 | } 263 | 264 | enum IterTypes { 265 | Empty, 266 | Iter(A), 267 | FlattenIter(B), 268 | FlatMap(C), 269 | Table(D), 270 | } 271 | 272 | impl Iterator for IterTypes 273 | where 274 | A: Iterator, 275 | B: Iterator, 276 | C: Iterator, 277 | D: Iterator, 278 | { 279 | type Item = A::Item; 280 | 281 | fn next(&mut self) -> Option { 282 | match self { 283 | IterTypes::Empty => None, 284 | IterTypes::Iter(iter) => iter.next(), 285 | IterTypes::FlattenIter(iter) => iter.next(), 286 | IterTypes::FlatMap(iter) => iter.next(), 287 | IterTypes::Table(iter) => iter.next(), 288 | } 289 | } 290 | } 291 | 292 | /// Converts the given element into a string with all formatting removed. 293 | pub trait Stringify { 294 | /// Converts the given element into a string with all formatting removed. 295 | fn stringify(&self) -> String { 296 | let mut s = String::new(); 297 | self.stringify_to(&mut s); 298 | s 299 | } 300 | 301 | /// Appends the stringified element to the given string. 302 | fn stringify_to(&self, str: &mut String); 303 | } 304 | 305 | impl Stringify for Inline { 306 | fn stringify_to(&self, str: &mut String) { 307 | // Should match the implementation of pandoc's Haskell API 308 | // https://hackage.haskell.org/package/pandoc/docs/src/Text.Pandoc.Shared.html#stringify 309 | match self { 310 | Inline::Space => str.push(' '), 311 | Inline::SoftBreak => str.push(' '), 312 | Inline::Str(x) => str.push_str(x), 313 | Inline::Code(_, x) => str.push_str(x), 314 | Inline::Math(_, x) => str.push_str(x), 315 | Inline::RawInline(Format(format), raw) 316 | if format == "html" && raw.starts_with(" 317 | { 318 | str.push(' ') 319 | } 320 | Inline::LineBreak => str.push('\n'), 321 | Inline::Quoted(super::QuoteType::SingleQuote, inlines) => { 322 | str.push('\u{2018}'); 323 | for inline in inlines { 324 | inline.stringify_to(str); 325 | } 326 | str.push('\u{2019}'); 327 | } 328 | Inline::Quoted(super::QuoteType::DoubleQuote, inlines) => { 329 | str.push('\u{201C}'); 330 | for inline in inlines { 331 | inline.stringify_to(str); 332 | } 333 | str.push('\u{201D}'); 334 | } 335 | other => { 336 | for inline in other.iter_inlines() { 337 | inline.stringify_to(str); 338 | } 339 | } 340 | } 341 | } 342 | } 343 | 344 | impl Stringify for T 345 | where 346 | for<'a> &'a T: IntoIterator, 347 | { 348 | fn stringify_to(&self, str: &mut String) { 349 | for inline in self.into_iter() { 350 | inline.stringify_to(str); 351 | } 352 | } 353 | } 354 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![warn(clippy::all, rust_2018_idioms)] 2 | 3 | pub mod definition; 4 | -------------------------------------------------------------------------------- /tests/inlines.txt: -------------------------------------------------------------------------------- 1 | str 2 | *emph* 3 | underline 4 | **strong** 5 | ~~strikeout~~ 6 | ^superscript^ 7 | ~subscript~ 8 | caps 9 | 'single' 10 | "double" 11 | [see @cite] 12 | `code` 13 | line break: \ 14 | $math$ 15 | \rawlatex{something} 16 | [link](http://pandoc.org) 17 | ![alt](image.png) 18 | -------------------------------------------------------------------------------- /tests/markdown-reader-more.txt: -------------------------------------------------------------------------------- 1 | % Title 2 | spanning multiple lines 3 | % Author One 4 | Author Two; Author Three; 5 | Author Four 6 | 7 | # Additional markdown reader tests 8 | 9 | ## Blank line before URL in link reference 10 | 11 | [foo] and [bar] 12 | 13 | [foo]: 14 | /url 15 | 16 | [bar]: 17 | /url 18 | "title" 19 | 20 | ## Raw ConTeXt environments 21 | 22 | \placeformula \startformula 23 | L_{1} = L_{2} 24 | \stopformula 25 | 26 | \start[a2] 27 | \start[a2] 28 | \stop[a2] 29 | \stop[a2] 30 | 31 | ## Raw LaTeX environments 32 | 33 | \begin{center} 34 | \begin{tikzpicture}[baseline={([yshift=+-.5ex]current bounding box.center)}, level distance=24pt] 35 | \Tree [.{S} [.NP John\index{i} ] [.VP [.V likes ] [.NP himself\index{i,*j} ]]] 36 | \end{tikzpicture} 37 | \end{center} 38 | 39 | ## URLs with spaces and punctuation 40 | 41 | [foo](/bar and baz) 42 | [foo](/bar 43 | and baz ) 44 | [foo]( /bar and baz ) 45 | [foo](bar baz "title" ) 46 | 47 | [baz][] [bam][] [bork][] 48 | 49 | [baz]: /foo foo 50 | [bam]: /foo fee 51 | [bork]: /foo/zee zob (title) 52 | 53 | [Ward's method.](http://en.wikipedia.org/wiki/Ward's_method) 54 | 55 | ## Horizontal rules with spaces at end 56 | 57 | * * * * * 58 | 59 | -- - -- -- - 60 | 61 | ## Raw HTML before header 62 | 63 | 64 | 65 | ### my header 66 | 67 | ## $ in math 68 | 69 | $\$2 + \$3$ 70 | 71 | $x = \text{the $n$th root of $y$}$ 72 | 73 | This should not be math: 74 | 75 | $PATH 90 $PATH 76 | 77 | ## Commented-out list item 78 | 79 | - one 80 | 83 | - three 84 | 85 | ## Indented code at beginning of list 86 | 87 | - code 88 | code 89 | 90 | 1. code 91 | code 92 | 93 | 12345678. code 94 | code 95 | 96 | - code 97 | code 98 | 99 | - no code 100 | 101 | ## Backslash newline 102 | 103 | hi\ 104 | there 105 | 106 | ## Code spans 107 | 108 | `hi\` 109 | 110 | `hi 111 | there` 112 | 113 | `` hi````there `` 114 | 115 | `hi 116 | 117 | there` 118 | 119 | ## Multilingual URLs 120 | 121 | 122 | 123 | [foo](/bar/测?x=测 "title") 124 | 125 | <测@foo.测.baz> 126 | 127 | ## Numbered examples 128 | 129 | (@) First example. 130 | (@foo) Second example. 131 | 132 | Explanation of examples (@foo) and (@bar). 133 | 134 | (@bar) Third example. 135 | 136 | ## Macros 137 | 138 | \newcommand{\tuple}[1]{\langle #1 \rangle} 139 | 140 | $\tuple{x,y}$ 141 | 142 | ## Case-insensitive references 143 | 144 | [Fum] 145 | 146 | [FUM] 147 | 148 | [bat] 149 | 150 | [fum]: /fum 151 | [BAT]: /bat 152 | 153 | ## Curly smart quotes 154 | 155 | “Hi” 156 | 157 | ‘Hi’ 158 | 159 | ## Consecutive lists 160 | 161 | - one 162 | - two 163 | 1. one 164 | 2. two 165 | 166 | a. one 167 | b. two 168 | 169 | ## Implicit header references 170 | 171 | ### My header 172 | 173 | ### My other header 174 | 175 | A link to [My header]. 176 | 177 | Another link to [it][My header]. 178 | 179 | Should be [case insensitive][my header]. 180 | 181 | Link to [Explicit header attributes]. 182 | 183 | [my other header]: /foo 184 | 185 | But this is not a link to [My other header], since the reference is defined. 186 | 187 | ## Explicit header attributes {#foobar .baz key="val"} 188 | 189 | > ## Header attributes inside block quote {#foobar .baz key="val"} 190 | 191 | ## Line blocks 192 | 193 | | But can a bee be said to be 194 | | or not to be an entire bee, 195 | | when half the bee is not a bee, 196 | | due to some ancient injury? 197 | | 198 | | Continuation 199 | line 200 | | and 201 | another 202 | 203 | ## Grid Tables 204 | 205 | +------------------+-----------+------------+ 206 | | col 1 | col 2 | col 3 | 207 | +==================+===========+============+ 208 | | r1 a | b | c | 209 | | r1 bis | b 2 | c 2 | 210 | +------------------+-----------+------------+ 211 | | r2 d | e | f | 212 | +------------------+-----------+------------+ 213 | 214 | Headless 215 | 216 | +------------------+-----------+------------+ 217 | | r1 a | b | c | 218 | | r1 bis | b 2 | c 2 | 219 | +------------------+-----------+------------+ 220 | | r2 d | e | f | 221 | +------------------+-----------+------------+ 222 | 223 | With alignments 224 | 225 | +------------------+-----------+------------+ 226 | | col 1 | col 2 | col 3 | 227 | +=================:+:==========+:==========:+ 228 | | r1 a | b | c | 229 | | r1 bis | b 2 | c 2 | 230 | +------------------+-----------+------------+ 231 | | r2 d | e | f | 232 | +------------------+-----------+------------+ 233 | 234 | Headless with alignments 235 | 236 | +-----------------:+:----------+:----------:+ 237 | | r1 a | b | c | 238 | | r1 bis | b 2 | c 2 | 239 | +------------------+-----------+------------+ 240 | | r2 d | e | f | 241 | +------------------+-----------+------------+ 242 | 243 | Spaces at ends of lines 244 | 245 | +------------------+-----------+------------+ 246 | | r1 a | b | c | 247 | | r1 bis | b 2 | c 2 | 248 | +------------------+-----------+------------+ 249 | | r2 d | e | f | 250 | +------------------+-----------+------------+ 251 | 252 | Multiple blocks in a cell 253 | 254 | +------------------+-----------+------------+ 255 | | # col 1 | # col 2 | # col 3 | 256 | | col 1 | col 2 | col 3 | 257 | +------------------+-----------+------------+ 258 | | r1 a | - b | c | 259 | | | - b 2 | c 2 | 260 | | r1 bis | - b 2 | c 2 | 261 | +------------------+-----------+------------+ 262 | 263 | Empty cells 264 | 265 | +---+---+ 266 | | | | 267 | +---+---+ 268 | 269 | ## Entities in links and titles 270 | 271 | [link](/ürl "öö!") 272 | 273 | 274 | 275 | 276 | 277 | [foobar] 278 | 279 | [foobar]: /ürl "öö!" 280 | 281 | ## Parentheses in URLs 282 | 283 | [link](/hi(there)) 284 | 285 | [link](/hithere\)) 286 | 287 | [linky] 288 | 289 | [linky]: hi_(there_(nested)) 290 | 291 | ## Backslashes in link references 292 | 293 | [\*\a](b) 294 | 295 | ## Reference link fallbacks 296 | 297 | [*not a link*] [*nope*]... 298 | 299 | ## Reference link followed by a citation 300 | 301 | MapReduce is a paradigm popularized by [Google] [@mapreduce] as its 302 | most vocal proponent. 303 | 304 | [Google]: http://google.com 305 | 306 | ## Empty reference links 307 | 308 | [foo2]: 309 | 310 | bar 311 | 312 | [foo2] 313 | 314 | ## Wrapping shouldn't introduce new list items 315 | 316 | - blah blah blah blah blah blah blah blah blah blah blah blah blah blah 2015. 317 | 318 | ## Bracketed spans 319 | 320 | [*foo* bar baz [link](url)]{.class #id key=val} 321 | -------------------------------------------------------------------------------- /tests/pandoc_compatibility.rs: -------------------------------------------------------------------------------- 1 | //! This test checks that the JSON we produce actually matches what 2 | //! Pandoc itself supports. We do this by running roundtrips through 3 | //! Pandoc and back, making sure that the document before and after is 4 | //! identical and that there are no errors. 5 | //! 6 | //! This requires that Pandoc be installed and on PATH. 7 | 8 | use pandoc_types::definition::{Block, Inline, IterBlocks, IterInlines, Pandoc, Stringify}; 9 | 10 | use std::io::{self, Read, Write}; 11 | use std::process::{Command, Stdio}; 12 | 13 | fn pandoc_convert(input: &str, from: &str, to: &str) -> io::Result { 14 | let process = Command::new("pandoc") 15 | .arg("-s") 16 | .arg("-f") 17 | .arg(from) 18 | .arg("-t") 19 | .arg(to) 20 | .stdin(Stdio::piped()) 21 | .stdout(Stdio::piped()) 22 | .spawn()?; 23 | 24 | process.stdin.unwrap().write_all(input.as_bytes())?; 25 | 26 | let mut s = String::new(); 27 | process.stdout.unwrap().read_to_string(&mut s).map(|_| s) 28 | } 29 | 30 | fn check_roundtrip_stability(md_1: &str) { 31 | // Do an initial roundtrip to settle whitespace issues. 32 | let md_2 = pandoc_convert(md_1, "markdown", "markdown").unwrap(); 33 | 34 | // Now do a roundtrip through our own parser and back. 35 | let json_2 = pandoc_convert(&md_2, "markdown", "json").unwrap(); 36 | let doc_2: Pandoc = serde_json::from_str(&json_2).unwrap(); 37 | let json_3 = serde_json::to_string(&doc_2).unwrap(); 38 | let json_4 = pandoc_convert(&json_3, "json", "json").unwrap(); 39 | let doc_4: Pandoc = serde_json::from_str(&json_4).unwrap(); 40 | assert_eq!(doc_2, doc_4); 41 | } 42 | 43 | #[test] 44 | fn pandoc_available() { 45 | pandoc_convert("", "markdown", "json").unwrap(); 46 | } 47 | 48 | #[test] 49 | fn empty() { 50 | check_roundtrip_stability(""); 51 | } 52 | 53 | #[test] 54 | fn title() { 55 | check_roundtrip_stability( 56 | r#"% title 57 | % author 58 | % date 59 | "#, 60 | ); 61 | } 62 | 63 | #[test] 64 | fn meta() { 65 | check_roundtrip_stability( 66 | r#" 67 | --- 68 | title: asdf 69 | author: qwer 70 | date: zxcv 71 | foo: bar 72 | fun: 73 | - 2 74 | - times 75 | - 2 76 | --- 77 | "#, 78 | ); 79 | } 80 | 81 | #[test] 82 | fn para() { 83 | check_roundtrip_stability( 84 | r#" 85 | first paragraph 86 | 87 | second paragraph 88 | "#, 89 | ); 90 | } 91 | 92 | #[test] 93 | fn line_block() { 94 | check_roundtrip_stability( 95 | r#" 96 | | line 97 | | block 98 | "#, 99 | ); 100 | } 101 | 102 | #[test] 103 | fn code_block() { 104 | check_roundtrip_stability( 105 | r#" 106 | ```bash 107 | $ echo hi 108 | $ uname -a 109 | ``` 110 | "#, 111 | ); 112 | } 113 | 114 | #[test] 115 | fn raw_block() { 116 | check_roundtrip_stability( 117 | r#" 118 | ```bash 119 | \begin{enumerate} 120 | \item one 121 | \item two 122 | \end{enumerate} 123 | ``` 124 | "#, 125 | ); 126 | } 127 | 128 | #[test] 129 | fn block_quote() { 130 | check_roundtrip_stability( 131 | r#" 132 | > "Don't worry about what anybody else is going to do. The best way to 133 | > predict the future is to invent it." 134 | > 135 | > --- Alan Kay 136 | "#, 137 | ); 138 | } 139 | 140 | #[test] 141 | fn lists() { 142 | check_roundtrip_stability( 143 | r#" 144 | 1. d 145 | e 146 | 2. f 147 | * g 148 | 149 | a. 1 150 | b. 2 151 | c. 3 152 | 153 | * a 154 | * b 155 | * c 156 | 157 | fun 158 | 159 | : something you do with friends 160 | 161 | "#, 162 | ); 163 | } 164 | 165 | #[test] 166 | fn headers() { 167 | check_roundtrip_stability( 168 | r#" 169 | # a 170 | 171 | ## b 172 | 173 | ### c 174 | 175 | #### d 176 | 177 | ##### e 178 | 179 | ###### f 180 | "#, 181 | ); 182 | } 183 | 184 | #[test] 185 | fn horizontal_rule() { 186 | check_roundtrip_stability( 187 | r#" 188 | ---- 189 | "#, 190 | ); 191 | } 192 | 193 | #[test] 194 | fn table() { 195 | check_roundtrip_stability( 196 | r#" 197 | right left center 198 | ------- ------ -------- 199 | 1 2 3 200 | 4 5 6 201 | "#, 202 | ); 203 | } 204 | 205 | #[test] 206 | fn div() { 207 | check_roundtrip_stability( 208 | r#" 209 |
210 | 211 | * 1 212 | * 2 213 | * 3 214 | 215 |
216 | "#, 217 | ); 218 | } 219 | 220 | #[test] 221 | fn inline_roundtrip() { 222 | check_roundtrip_stability(concat!( 223 | include_str!("inlines.txt"), 224 | r#" 225 | [^footnote] 226 | 227 | [^footnote]: span 228 | "#, 229 | )); 230 | } 231 | 232 | #[test] 233 | fn markdown_reader_more() { 234 | check_roundtrip_stability(include_str!("markdown-reader-more.txt")); 235 | } 236 | 237 | #[test] 238 | fn tables() { 239 | check_roundtrip_stability(include_str!("tables.txt")); 240 | } 241 | 242 | #[test] 243 | fn testsuite() { 244 | check_roundtrip_stability(include_str!("testsuite.txt")); 245 | } 246 | 247 | #[test] 248 | fn stringify() { 249 | let json = pandoc_convert(include_str!("inlines.txt"), "markdown", "json").unwrap(); 250 | let pandoc: Pandoc = serde_json::from_str(&json).unwrap(); 251 | match &pandoc.blocks[..] { 252 | [Block::Para(inlines)] => { 253 | assert_eq!(inlines.stringify(), "str emph underline strong strikeout superscript subscript caps ‘single’ “double” [see @cite] code line break:\nmath link alt"); 254 | } 255 | _ => panic!("expected inlines.txt to return only one Para"), 256 | } 257 | } 258 | 259 | fn make_blocks_uppercase<'a>(blocks: impl Iterator) { 260 | for block in blocks { 261 | make_blocks_uppercase(block.iter_blocks_mut()); 262 | make_inlines_uppercase(block.iter_inlines_mut()); 263 | } 264 | } 265 | 266 | fn make_inlines_uppercase<'a>(inlines: impl Iterator) { 267 | for inline in inlines { 268 | if let Inline::Str(text) = inline { 269 | *text = text.to_uppercase() 270 | } 271 | make_inlines_uppercase(inline.iter_inlines_mut()); 272 | make_blocks_uppercase(inline.iter_blocks_mut()); 273 | } 274 | } 275 | 276 | #[test] 277 | fn iter_mut_blocks() { 278 | let json = pandoc_convert(include_str!("testsuite.txt"), "markdown", "json").unwrap(); 279 | let mut doc: Pandoc = serde_json::from_str(&json).unwrap(); 280 | make_blocks_uppercase(doc.blocks.iter_mut()); 281 | let json = serde_json::to_string(&doc).unwrap(); 282 | let markdown = pandoc_convert(&json, "json", "markdown").unwrap(); 283 | assert_eq!(markdown, include_str!("testsuite_uppercase.txt")); 284 | } 285 | 286 | #[test] 287 | fn iter_mut_tables() { 288 | let json = pandoc_convert(include_str!("tables.txt"), "markdown", "json").unwrap(); 289 | let mut doc: Pandoc = serde_json::from_str(&json).unwrap(); 290 | make_blocks_uppercase(doc.blocks.iter_mut()); 291 | let json = serde_json::to_string(&doc).unwrap(); 292 | let markdown = pandoc_convert(&json, "json", "markdown").unwrap(); 293 | assert_eq!(markdown, include_str!("tables_uppercase.txt")); 294 | } 295 | -------------------------------------------------------------------------------- /tests/tables.txt: -------------------------------------------------------------------------------- 1 | Simple table with caption: 2 | 3 | Right Left Center Default 4 | ------- ------ ---------- ------- 5 | 12 12 12 12 6 | 123 123 123 123 7 | 1 1 1 1 8 | 9 | Table: Demonstration of simple table syntax. 10 | 11 | Simple table without caption: 12 | 13 | Right Left Center Default 14 | ------- ------ ---------- ------- 15 | 12 12 12 12 16 | 123 123 123 123 17 | 1 1 1 1 18 | 19 | Simple table indented two spaces: 20 | 21 | Right Left Center Default 22 | ------- ------ ---------- ------- 23 | 12 12 12 12 24 | 123 123 123 123 25 | 1 1 1 1 26 | 27 | : Demonstration of simple table syntax. 28 | 29 | Multiline table with caption: 30 | 31 | : Here's the caption. 32 | It may span multiple lines. 33 | 34 | --------------------------------------------------------------- 35 | Centered Left Right 36 | Header Aligned Aligned Default aligned 37 | ---------- --------- ----------- --------------------------- 38 | First row 12.0 Example of a row that spans 39 | multiple lines. 40 | 41 | Second row 5.0 Here's another one. Note 42 | the blank line between rows. 43 | --------------------------------------------------------------- 44 | 45 | Multiline table without caption: 46 | 47 | --------------------------------------------------------------- 48 | Centered Left Right 49 | Header Aligned Aligned Default aligned 50 | ---------- --------- ----------- --------------------------- 51 | First row 12.0 Example of a row that spans 52 | multiple lines. 53 | 54 | Second row 5.0 Here's another one. Note 55 | the blank line between rows. 56 | --------------------------------------------------------------- 57 | 58 | Table without column headers: 59 | 60 | ------- ------ ---------- ------- 61 | 12 12 12 12 62 | 123 123 123 123 63 | 1 1 1 1 64 | ------- ------ ---------- ------- 65 | 66 | Multiline table without column headers: 67 | 68 | ---------- --------- ----------- --------------------------- 69 | First row 12.0 Example of a row that spans 70 | multiple lines. 71 | 72 | Second row 5.0 Here's another one. Note 73 | the blank line between rows. 74 | ---------- --------- ----------- --------------------------- 75 | 76 | Table with column widths: 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 |
FirstSecond
AB
CD
100 | -------------------------------------------------------------------------------- /tests/tables_uppercase.txt: -------------------------------------------------------------------------------- 1 | SIMPLE TABLE WITH CAPTION: 2 | 3 | RIGHT LEFT CENTER DEFAULT 4 | ------- ------ -------- --------- 5 | 12 12 12 12 6 | 123 123 123 123 7 | 1 1 1 1 8 | 9 | : DEMONSTRATION OF SIMPLE TABLE SYNTAX. 10 | 11 | SIMPLE TABLE WITHOUT CAPTION: 12 | 13 | RIGHT LEFT CENTER DEFAULT 14 | ------- ------ -------- --------- 15 | 12 12 12 12 16 | 123 123 123 123 17 | 1 1 1 1 18 | 19 | SIMPLE TABLE INDENTED TWO SPACES: 20 | 21 | RIGHT LEFT CENTER DEFAULT 22 | ------- ------ -------- --------- 23 | 12 12 12 12 24 | 123 123 123 123 25 | 1 1 1 1 26 | 27 | : DEMONSTRATION OF SIMPLE TABLE SYNTAX. 28 | 29 | MULTILINE TABLE WITH CAPTION: 30 | 31 | --------------------------------------------------------------- 32 | CENTERED LEFT RIGHT DEFAULT ALIGNED 33 | HEADER ALIGNED ALIGNED 34 | ----------- ---------- ------------ --------------------------- 35 | FIRST ROW 12.0 EXAMPLE OF A ROW THAT SPANS 36 | MULTIPLE LINES. 37 | 38 | SECOND ROW 5.0 HERE'S ANOTHER ONE. NOTE 39 | THE BLANK LINE BETWEEN 40 | ROWS. 41 | --------------------------------------------------------------- 42 | 43 | : HERE'S THE CAPTION. IT MAY SPAN MULTIPLE LINES. 44 | 45 | MULTILINE TABLE WITHOUT CAPTION: 46 | 47 | --------------------------------------------------------------- 48 | CENTERED LEFT RIGHT DEFAULT ALIGNED 49 | HEADER ALIGNED ALIGNED 50 | ----------- ---------- ------------ --------------------------- 51 | FIRST ROW 12.0 EXAMPLE OF A ROW THAT SPANS 52 | MULTIPLE LINES. 53 | 54 | SECOND ROW 5.0 HERE'S ANOTHER ONE. NOTE 55 | THE BLANK LINE BETWEEN 56 | ROWS. 57 | --------------------------------------------------------------- 58 | 59 | TABLE WITHOUT COLUMN HEADERS: 60 | 61 | ----- ----- ----- ----- 62 | 12 12 12 12 63 | 123 123 123 123 64 | 1 1 1 1 65 | ----- ----- ----- ----- 66 | 67 | MULTILINE TABLE WITHOUT COLUMN HEADERS: 68 | 69 | ----------- ---------- ------------ --------------------------- 70 | FIRST ROW 12.0 EXAMPLE OF A ROW THAT SPANS 71 | MULTIPLE LINES. 72 | 73 | SECOND ROW 5.0 HERE'S ANOTHER ONE. NOTE 74 | THE BLANK LINE BETWEEN 75 | ROWS. 76 | ----------- ---------- ------------ --------------------------- 77 | 78 | TABLE WITH COLUMN WIDTHS: 79 | 80 | ```{=html} 81 | 82 | ``` 83 | ```{=html} 84 | 85 | ``` 86 | ```{=html} 87 | 88 | ``` 89 | ```{=html} 90 | 91 | ``` 92 | ```{=html} 93 | 94 | ``` 95 | ```{=html} 96 | 97 | ``` 98 | ```{=html} 99 | 100 | ``` 101 | ```{=html} 102 | 107 | ``` 108 | ```{=html} 109 | 114 | ``` 115 | ```{=html} 116 | 117 | ``` 118 | ```{=html} 119 | 120 | ``` 121 | ```{=html} 122 | 123 | ``` 124 | ```{=html} 125 | 126 | ``` 127 | ```{=html} 128 | 133 | ``` 134 | ```{=html} 135 | 140 | ``` 141 | ```{=html} 142 | 143 | ``` 144 | ```{=html} 145 | 146 | ``` 147 | ```{=html} 148 | 153 | ``` 154 | ```{=html} 155 | 160 | ``` 161 | ```{=html} 162 | 163 | ``` 164 | ```{=html} 165 | 166 | ``` 167 | ```{=html} 168 |
103 | ``` 104 | FIRST 105 | ```{=html} 106 | 110 | ``` 111 | SECOND 112 | ```{=html} 113 |
129 | ``` 130 | A 131 | ```{=html} 132 | 136 | ``` 137 | B 138 | ```{=html} 139 |
149 | ``` 150 | C 151 | ```{=html} 152 | 156 | ``` 157 | D 158 | ```{=html} 159 |
169 | ``` 170 | -------------------------------------------------------------------------------- /tests/testsuite.txt: -------------------------------------------------------------------------------- 1 | % Pandoc Test Suite 2 | % John MacFarlane; Anonymous 3 | % July 17, 2006 4 | 5 | This is a set of tests for pandoc. Most of them are adapted from 6 | John Gruber's markdown test suite. 7 | 8 | ----- 9 | 10 | # Headers 11 | 12 | ## Level 2 with an [embedded link](/url) 13 | 14 | ### Level 3 with *emphasis* 15 | 16 | #### Level 4 17 | 18 | ##### Level 5 19 | 20 | Level 1 21 | ======= 22 | 23 | Level 2 with *emphasis* 24 | ----------------------- 25 | 26 | ### Level 3 27 | with no blank line 28 | 29 | Level 2 30 | ------- 31 | with no blank line 32 | 33 | ---------- 34 | 35 | # Paragraphs 36 | 37 | Here's a regular paragraph. 38 | 39 | In Markdown 1.0.0 and earlier. Version 40 | 8. This line turns into a list item. 41 | Because a hard-wrapped line in the 42 | middle of a paragraph looked like a 43 | list item. 44 | 45 | Here's one with a bullet. 46 | * criminey. 47 | 48 | There should be a hard line break 49 | here. 50 | 51 | --- 52 | 53 | # Block Quotes 54 | 55 | E-mail style: 56 | 57 | > This is a block quote. 58 | > It is pretty short. 59 | 60 | > Code in a block quote: 61 | > 62 | > sub status { 63 | > print "working"; 64 | > } 65 | > 66 | > A list: 67 | > 68 | > 1. item one 69 | > 2. item two 70 | > 71 | > Nested block quotes: 72 | > 73 | > > nested 74 | > 75 | >> nested 76 | > 77 | 78 | This should not be a block quote: 2 79 | > 1. 80 | 81 | And a following paragraph. 82 | 83 | * * * * 84 | 85 | # Code Blocks 86 | 87 | Code: 88 | 89 | ---- (should be four hyphens) 90 | 91 | sub status { 92 | print "working"; 93 | } 94 | 95 | this code block is indented by one tab 96 | 97 | And: 98 | 99 | this code block is indented by two tabs 100 | 101 | These should not be escaped: \$ \\ \> \[ \{ 102 | 103 | ___________ 104 | 105 | # Lists 106 | 107 | ## Unordered 108 | 109 | Asterisks tight: 110 | 111 | * asterisk 1 112 | * asterisk 2 113 | * asterisk 3 114 | 115 | Asterisks loose: 116 | 117 | * asterisk 1 118 | 119 | * asterisk 2 120 | 121 | * asterisk 3 122 | 123 | Pluses tight: 124 | 125 | + Plus 1 126 | + Plus 2 127 | + Plus 3 128 | 129 | Pluses loose: 130 | 131 | + Plus 1 132 | 133 | + Plus 2 134 | 135 | + Plus 3 136 | 137 | Minuses tight: 138 | 139 | - Minus 1 140 | - Minus 2 141 | - Minus 3 142 | 143 | Minuses loose: 144 | 145 | - Minus 1 146 | 147 | - Minus 2 148 | 149 | - Minus 3 150 | 151 | ## Ordered 152 | 153 | Tight: 154 | 155 | 1. First 156 | 2. Second 157 | 3. Third 158 | 159 | and: 160 | 161 | 1. One 162 | 2. Two 163 | 3. Three 164 | 165 | Loose using tabs: 166 | 167 | 1. First 168 | 169 | 2. Second 170 | 171 | 3. Third 172 | 173 | and using spaces: 174 | 175 | 1. One 176 | 177 | 2. Two 178 | 179 | 3. Three 180 | 181 | Multiple paragraphs: 182 | 183 | 1. Item 1, graf one. 184 | 185 | Item 1. graf two. The quick brown fox jumped over the lazy dog's 186 | back. 187 | 188 | 2. Item 2. 189 | 190 | 3. Item 3. 191 | 192 | ## Nested 193 | 194 | * Tab 195 | * Tab 196 | * Tab 197 | 198 | Here's another: 199 | 200 | 1. First 201 | 2. Second: 202 | * Fee 203 | * Fie 204 | * Foe 205 | 3. Third 206 | 207 | Same thing but with paragraphs: 208 | 209 | 1. First 210 | 211 | 2. Second: 212 | 213 | * Fee 214 | * Fie 215 | * Foe 216 | 217 | 3. Third 218 | 219 | ## Tabs and spaces 220 | 221 | + this is a list item 222 | indented with tabs 223 | 224 | + this is a list item 225 | indented with spaces 226 | 227 | + this is an example list item 228 | indented with tabs 229 | 230 | + this is an example list item 231 | indented with spaces 232 | 233 | ## Fancy list markers 234 | 235 | (2) begins with 2 236 | (3) and now 3 237 | 238 | with a continuation 239 | 240 | iv. sublist with roman numerals, 241 | starting with 4 242 | v. more items 243 | (A) a subsublist 244 | (B) a subsublist 245 | 246 | Nesting: 247 | 248 | A. Upper Alpha 249 | I. Upper Roman. 250 | (6) Decimal start with 6 251 | c) Lower alpha with paren 252 | 253 | Autonumbering: 254 | 255 | #. Autonumber. 256 | #. More. 257 | #. Nested. 258 | 259 | Should not be a list item: 260 | 261 | M.A. 2007 262 | 263 | B. Williams 264 | 265 | * * * * * 266 | 267 | # Definition Lists 268 | 269 | Tight using spaces: 270 | 271 | apple 272 | : red fruit 273 | 274 | orange 275 | : orange fruit 276 | 277 | banana 278 | : yellow fruit 279 | 280 | Tight using tabs: 281 | 282 | apple 283 | : red fruit 284 | 285 | orange 286 | : orange fruit 287 | 288 | banana 289 | : yellow fruit 290 | 291 | Loose: 292 | 293 | apple 294 | 295 | : red fruit 296 | 297 | orange 298 | 299 | : orange fruit 300 | 301 | banana 302 | 303 | : yellow fruit 304 | 305 | Multiple blocks with italics: 306 | 307 | *apple* 308 | 309 | : red fruit 310 | 311 | contains seeds, 312 | crisp, pleasant to taste 313 | 314 | *orange* 315 | 316 | : orange fruit 317 | 318 | { orange code block } 319 | 320 | > orange block quote 321 | 322 | Multiple definitions, tight: 323 | 324 | apple 325 | : red fruit 326 | : computer 327 | 328 | orange 329 | : orange fruit 330 | : bank 331 | 332 | Multiple definitions, loose: 333 | 334 | apple 335 | 336 | : red fruit 337 | 338 | : computer 339 | 340 | orange 341 | 342 | : orange fruit 343 | 344 | : bank 345 | 346 | Blank line after term, indented marker, alternate markers: 347 | 348 | apple 349 | 350 | ~ red fruit 351 | 352 | ~ computer 353 | 354 | orange 355 | 356 | ~ orange fruit 357 | 358 | 1. sublist 359 | 2. sublist 360 | 361 | # HTML Blocks 362 | 363 | Simple block on one line: 364 | 365 |
foo
366 | 367 | And nested without indentation: 368 | 369 |
370 |
371 |
372 | foo 373 |
374 |
375 |
bar
376 |
377 | 378 | Interpreted markdown in a table: 379 | 380 | 381 | 382 | 383 | 384 | 385 |
This is *emphasized*And this is **strong**
386 | 387 | 388 | 389 | Here's a simple block: 390 | 391 |
392 | foo 393 |
394 | 395 | This should be a code block, though: 396 | 397 |
398 | foo 399 |
400 | 401 | As should this: 402 | 403 |
foo
404 | 405 | Now, nested: 406 | 407 |
408 |
409 |
410 | foo 411 |
412 |
413 |
414 | 415 | This should just be an HTML comment: 416 | 417 | 418 | 419 | Multiline: 420 | 421 | 425 | 426 | 429 | 430 | Code block: 431 | 432 | 433 | 434 | Just plain comment, with trailing spaces on the line: 435 | 436 | 437 | 438 | Code: 439 | 440 |
441 | 442 | Hr's: 443 | 444 |
445 | 446 |
447 | 448 |
449 | 450 |
451 | 452 |
453 | 454 |
455 | 456 |
457 | 458 |
459 | 460 |
461 | 462 | ----- 463 | 464 | # Inline Markup 465 | 466 | This is *emphasized*, and so _is this_. 467 | 468 | This is **strong**, and so __is this__. 469 | 470 | An *[emphasized link](/url)*. 471 | 472 | ***This is strong and em.*** 473 | 474 | So is ***this*** word. 475 | 476 | ___This is strong and em.___ 477 | 478 | So is ___this___ word. 479 | 480 | This is code: `>`, `$`, `\`, `\$`, ``. 481 | 482 | ~~This is *strikeout*.~~ 483 | 484 | Superscripts: a^bc^d a^*hello*^ a^hello\ there^. 485 | 486 | Subscripts: H~2~O, H~23~O, H~many\ of\ them~O. 487 | 488 | These should not be superscripts or subscripts, 489 | because of the unescaped spaces: a^b c^d, a~b c~d. 490 | 491 | ----- 492 | 493 | # Smart quotes, ellipses, dashes 494 | 495 | "Hello," said the spider. "'Shelob' is my name." 496 | 497 | 'A', 'B', and 'C' are letters. 498 | 499 | 'Oak,' 'elm,' and 'beech' are names of trees. 500 | So is 'pine.' 501 | 502 | 'He said, "I want to go."' Were you alive in the 503 | 70's? 504 | 505 | Here is some quoted '`code`' and a "[quoted link][1]". 506 | 507 | Some dashes: one---two --- three---four --- five. 508 | 509 | Dashes between numbers: 5--7, 255--66, 1987--1999. 510 | 511 | Ellipses...and...and.... 512 | 513 | ----- 514 | 515 | # LaTeX 516 | 517 | - \cite[22-23]{smith.1899} 518 | - $2+2=4$ 519 | - $x \in y$ 520 | - $\alpha \wedge \omega$ 521 | - $223$ 522 | - $p$-Tree 523 | - Here's some display math: 524 | $$\frac{d}{dx}f(x)=\lim_{h\to 0}\frac{f(x+h)-f(x)}{h}$$ 525 | - Here's one that has a line break in it: $\alpha + \omega \times 526 | x^2$. 527 | 528 | These shouldn't be math: 529 | 530 | - To get the famous equation, write `$e = mc^2$`. 531 | - $22,000 is a *lot* of money. So is $34,000. 532 | (It worked if "lot" is emphasized.) 533 | - Shoes ($20) and socks ($5). 534 | - Escaped `$`: $73 *this should be emphasized* 23\$. 535 | 536 | Here's a LaTeX table: 537 | 538 | \begin{tabular}{|l|l|}\hline 539 | Animal & Number \\ \hline 540 | Dog & 2 \\ 541 | Cat & 1 \\ \hline 542 | \end{tabular} 543 | 544 | * * * * * 545 | 546 | # Special Characters 547 | 548 | Here is some unicode: 549 | 550 | - I hat: Î 551 | - o umlaut: ö 552 | - section: § 553 | - set membership: ∈ 554 | - copyright: © 555 | 556 | AT&T has an ampersand in their name. 557 | 558 | AT&T is another way to write it. 559 | 560 | This & that. 561 | 562 | 4 < 5. 563 | 564 | 6 > 5. 565 | 566 | Backslash: \\ 567 | 568 | Backtick: \` 569 | 570 | Asterisk: \* 571 | 572 | Underscore: \_ 573 | 574 | Left brace: \{ 575 | 576 | Right brace: \} 577 | 578 | Left bracket: \[ 579 | 580 | Right bracket: \] 581 | 582 | Left paren: \( 583 | 584 | Right paren: \) 585 | 586 | Greater-than: \> 587 | 588 | Hash: \# 589 | 590 | Period: \. 591 | 592 | Bang: \! 593 | 594 | Plus: \+ 595 | 596 | Minus: \- 597 | 598 | - - - - - - - - - - - - - 599 | 600 | # Links 601 | 602 | ## Explicit 603 | 604 | Just a [URL](/url/). 605 | 606 | [URL and title](/url/ "title"). 607 | 608 | [URL and title](/url/ "title preceded by two spaces"). 609 | 610 | [URL and title](/url/ "title preceded by a tab"). 611 | 612 | [URL and title](/url/ "title with "quotes" in it") 613 | 614 | [URL and title](/url/ 'title with single quotes') 615 | 616 | [with\_underscore](/url/with_underscore) 617 | 618 | [Email link](mailto:nobody@nowhere.net) 619 | 620 | [Empty](). 621 | 622 | ## Reference 623 | 624 | Foo [bar][a]. 625 | 626 | [a]: /url/ 627 | 628 | With [embedded [brackets]][b]. 629 | 630 | [b] by itself should be a link. 631 | 632 | Indented [once][]. 633 | 634 | Indented [twice][]. 635 | 636 | Indented [thrice][]. 637 | 638 | This should [not][] be a link. 639 | 640 | [once]: /url 641 | [twice]: /url 642 | 643 | [thrice]: /url 644 | 645 | [not]: /url 646 | 647 | [b]: /url/ 648 | 649 | Foo [bar][]. 650 | 651 | Foo [biz](/url/ "Title with "quote" inside"). 652 | 653 | [bar]: /url/ "Title with "quotes" inside" 654 | 655 | ## With ampersands 656 | 657 | Here's a [link with an ampersand in the URL][1]. 658 | 659 | Here's a link with an amersand in the link text: [AT&T][2]. 660 | 661 | Here's an [inline link](/script?foo=1&bar=2). 662 | 663 | Here's an [inline link in pointy braces](). 664 | 665 | [1]: http://example.com/?foo=1&bar=2 666 | [2]: http://att.com/ "AT&T" 667 | 668 | ## Autolinks 669 | 670 | With an ampersand: 671 | 672 | * In a list? 673 | * 674 | * It should. 675 | 676 | An e-mail address: 677 | 678 | > Blockquoted: 679 | 680 | Auto-links should not occur here: `` 681 | 682 | or here: 683 | 684 | ---- 685 | 686 | # Images 687 | 688 | From "Voyage dans la Lune" by Georges Melies (1902): 689 | 690 | ![lalune][] 691 | 692 | [lalune]: lalune.jpg "Voyage dans la Lune" 693 | 694 | Here is a movie ![movie](movie.jpg) icon. 695 | 696 | ---- 697 | 698 | # Footnotes 699 | 700 | Here is a footnote reference,[^1] and another.[^longnote] 701 | This should *not* be a footnote reference, because it 702 | contains a space.[^my note] Here is an inline note.^[This 703 | is *easier* to type. Inline notes may contain 704 | [links](http://google.com) and `]` verbatim characters, 705 | as well as [bracketed text].] 706 | 707 | > Notes can go in quotes.^[In quote.] 708 | 709 | 1. And in list items.^[In list.] 710 | 711 | [^longnote]: Here's the long note. This one contains multiple 712 | blocks. 713 | 714 | Subsequent blocks are indented to show that they belong to the 715 | footnote (as with list items). 716 | 717 | { } 718 | 719 | If you want, you can indent every line, but you can also be 720 | lazy and just indent the first line of each block. 721 | 722 | This paragraph should not be part of the note, as it is not indented. 723 | 724 | [^1]: Here is the footnote. It can go anywhere after the footnote 725 | reference. It need not be placed at the end of the document. 726 | -------------------------------------------------------------------------------- /tests/testsuite_uppercase.txt: -------------------------------------------------------------------------------- 1 | --- 2 | author: 3 | - John MacFarlane 4 | - Anonymous 5 | date: July 17, 2006 6 | title: Pandoc Test Suite 7 | --- 8 | 9 | THIS IS A SET OF TESTS FOR PANDOC. MOST OF THEM ARE ADAPTED FROM JOHN 10 | GRUBER'S MARKDOWN TEST SUITE. 11 | 12 | ------------------------------------------------------------------------ 13 | 14 | # HEADERS 15 | 16 | ## LEVEL 2 WITH AN [EMBEDDED LINK](/url) 17 | 18 | ### LEVEL 3 WITH *EMPHASIS* 19 | 20 | #### LEVEL 4 21 | 22 | ##### LEVEL 5 23 | 24 | # LEVEL 1 25 | 26 | ## LEVEL 2 WITH *EMPHASIS* 27 | 28 | ### LEVEL 3 29 | 30 | WITH NO BLANK LINE 31 | 32 | ## LEVEL 2 33 | 34 | WITH NO BLANK LINE 35 | 36 | ------------------------------------------------------------------------ 37 | 38 | # PARAGRAPHS 39 | 40 | HERE'S A REGULAR PARAGRAPH. 41 | 42 | IN MARKDOWN 1.0.0 AND EARLIER. VERSION 8. THIS LINE TURNS INTO A LIST 43 | ITEM. BECAUSE A HARD-WRAPPED LINE IN THE MIDDLE OF A PARAGRAPH LOOKED 44 | LIKE A LIST ITEM. 45 | 46 | HERE'S ONE WITH A BULLET. \* CRIMINEY. 47 | 48 | THERE SHOULD BE A HARD LINE BREAK\ 49 | HERE. 50 | 51 | ------------------------------------------------------------------------ 52 | 53 | # BLOCK QUOTES 54 | 55 | E-MAIL STYLE: 56 | 57 | > THIS IS A BLOCK QUOTE. IT IS PRETTY SHORT. 58 | 59 | > CODE IN A BLOCK QUOTE: 60 | > 61 | > sub status { 62 | > print "working"; 63 | > } 64 | > 65 | > A LIST: 66 | > 67 | > 1. ITEM ONE 68 | > 2. ITEM TWO 69 | > 70 | > NESTED BLOCK QUOTES: 71 | > 72 | > > NESTED 73 | > 74 | > > NESTED 75 | 76 | THIS SHOULD NOT BE A BLOCK QUOTE: 2 \> 1. 77 | 78 | AND A FOLLOWING PARAGRAPH. 79 | 80 | ------------------------------------------------------------------------ 81 | 82 | # CODE BLOCKS 83 | 84 | CODE: 85 | 86 | ---- (should be four hyphens) 87 | 88 | sub status { 89 | print "working"; 90 | } 91 | 92 | this code block is indented by one tab 93 | 94 | AND: 95 | 96 | this code block is indented by two tabs 97 | 98 | These should not be escaped: \$ \\ \> \[ \{ 99 | 100 | ------------------------------------------------------------------------ 101 | 102 | # LISTS 103 | 104 | ## UNORDERED 105 | 106 | ASTERISKS TIGHT: 107 | 108 | - ASTERISK 1 109 | - ASTERISK 2 110 | - ASTERISK 3 111 | 112 | ASTERISKS LOOSE: 113 | 114 | - ASTERISK 1 115 | 116 | - ASTERISK 2 117 | 118 | - ASTERISK 3 119 | 120 | PLUSES TIGHT: 121 | 122 | - PLUS 1 123 | - PLUS 2 124 | - PLUS 3 125 | 126 | PLUSES LOOSE: 127 | 128 | - PLUS 1 129 | 130 | - PLUS 2 131 | 132 | - PLUS 3 133 | 134 | MINUSES TIGHT: 135 | 136 | - MINUS 1 137 | - MINUS 2 138 | - MINUS 3 139 | 140 | MINUSES LOOSE: 141 | 142 | - MINUS 1 143 | 144 | - MINUS 2 145 | 146 | - MINUS 3 147 | 148 | ## ORDERED 149 | 150 | TIGHT: 151 | 152 | 1. FIRST 153 | 2. SECOND 154 | 3. THIRD 155 | 156 | AND: 157 | 158 | 1. ONE 159 | 2. TWO 160 | 3. THREE 161 | 162 | LOOSE USING TABS: 163 | 164 | 1. FIRST 165 | 166 | 2. SECOND 167 | 168 | 3. THIRD 169 | 170 | AND USING SPACES: 171 | 172 | 1. ONE 173 | 174 | 2. TWO 175 | 176 | 3. THREE 177 | 178 | MULTIPLE PARAGRAPHS: 179 | 180 | 1. ITEM 1, GRAF ONE. 181 | 182 | ITEM 1. GRAF TWO. THE QUICK BROWN FOX JUMPED OVER THE LAZY DOG'S 183 | BACK. 184 | 185 | 2. ITEM 2. 186 | 187 | 3. ITEM 3. 188 | 189 | ## NESTED 190 | 191 | - TAB 192 | - TAB 193 | - TAB 194 | 195 | HERE'S ANOTHER: 196 | 197 | 1. FIRST 198 | 2. SECOND: 199 | - FEE 200 | - FIE 201 | - FOE 202 | 3. THIRD 203 | 204 | SAME THING BUT WITH PARAGRAPHS: 205 | 206 | 1. FIRST 207 | 208 | 2. SECOND: 209 | 210 | - FEE 211 | - FIE 212 | - FOE 213 | 214 | 3. THIRD 215 | 216 | ## TABS AND SPACES 217 | 218 | - THIS IS A LIST ITEM INDENTED WITH TABS 219 | 220 | - THIS IS A LIST ITEM INDENTED WITH SPACES 221 | 222 | - THIS IS AN EXAMPLE LIST ITEM INDENTED WITH TABS 223 | 224 | - THIS IS AN EXAMPLE LIST ITEM INDENTED WITH SPACES 225 | 226 | ## FANCY LIST MARKERS 227 | 228 | (2) BEGINS WITH 2 229 | 230 | (3) AND NOW 3 231 | 232 | WITH A CONTINUATION 233 | 234 | iv. SUBLIST WITH ROMAN NUMERALS, STARTING WITH 4 235 | v. MORE ITEMS 236 | (A) A SUBSUBLIST 237 | (B) A SUBSUBLIST 238 | 239 | NESTING: 240 | 241 | A. UPPER ALPHA 242 | I. UPPER ROMAN. 243 | (6) DECIMAL START WITH 6 244 | c) LOWER ALPHA WITH PAREN 245 | 246 | AUTONUMBERING: 247 | 248 | 1. AUTONUMBER. 249 | 2. MORE. 250 | 1. NESTED. 251 | 252 | SHOULD NOT BE A LIST ITEM: 253 | 254 | M.A. 2007 255 | 256 | B. WILLIAMS 257 | 258 | ------------------------------------------------------------------------ 259 | 260 | # DEFINITION LISTS 261 | 262 | TIGHT USING SPACES: 263 | 264 | APPLE 265 | : RED FRUIT 266 | 267 | ORANGE 268 | : ORANGE FRUIT 269 | 270 | BANANA 271 | : YELLOW FRUIT 272 | 273 | TIGHT USING TABS: 274 | 275 | APPLE 276 | : RED FRUIT 277 | 278 | ORANGE 279 | : ORANGE FRUIT 280 | 281 | BANANA 282 | : YELLOW FRUIT 283 | 284 | LOOSE: 285 | 286 | APPLE 287 | 288 | : RED FRUIT 289 | 290 | ORANGE 291 | 292 | : ORANGE FRUIT 293 | 294 | BANANA 295 | 296 | : YELLOW FRUIT 297 | 298 | MULTIPLE BLOCKS WITH ITALICS: 299 | 300 | *APPLE* 301 | 302 | : RED FRUIT 303 | 304 | CONTAINS SEEDS, CRISP, PLEASANT TO TASTE 305 | 306 | *ORANGE* 307 | 308 | : ORANGE FRUIT 309 | 310 | { orange code block } 311 | 312 | > ORANGE BLOCK QUOTE 313 | 314 | MULTIPLE DEFINITIONS, TIGHT: 315 | 316 | APPLE 317 | : RED FRUIT 318 | : COMPUTER 319 | 320 | ORANGE 321 | : ORANGE FRUIT 322 | : BANK 323 | 324 | MULTIPLE DEFINITIONS, LOOSE: 325 | 326 | APPLE 327 | 328 | : RED FRUIT 329 | 330 | : COMPUTER 331 | 332 | ORANGE 333 | 334 | : ORANGE FRUIT 335 | 336 | : BANK 337 | 338 | BLANK LINE AFTER TERM, INDENTED MARKER, ALTERNATE MARKERS: 339 | 340 | APPLE 341 | 342 | : RED FRUIT 343 | 344 | : COMPUTER 345 | 346 | ORANGE 347 | 348 | : ORANGE FRUIT 349 | 350 | 1. SUBLIST 351 | 2. SUBLIST 352 | 353 | # HTML BLOCKS 354 | 355 | SIMPLE BLOCK ON ONE LINE: 356 | 357 |
358 | 359 | FOO 360 | 361 |
362 | 363 | AND NESTED WITHOUT INDENTATION: 364 | 365 |
366 | 367 |
368 | 369 |
370 | 371 | FOO 372 | 373 |
374 | 375 |
376 | 377 |
378 | 379 | BAR 380 | 381 |
382 | 383 |
384 | 385 | INTERPRETED MARKDOWN IN A TABLE: 386 | 387 | ```{=html} 388 | 389 | ``` 390 | ```{=html} 391 | 392 | ``` 393 | ```{=html} 394 | 399 | ``` 400 | ```{=html} 401 | 406 | ``` 407 | ```{=html} 408 | 409 | ``` 410 | ```{=html} 411 |
395 | ``` 396 | THIS IS *EMPHASIZED* 397 | ```{=html} 398 | 402 | ``` 403 | AND THIS IS **STRONG** 404 | ```{=html} 405 |
412 | ``` 413 | ```{=html} 414 | 415 | ``` 416 | HERE'S A SIMPLE BLOCK: 417 | 418 |
419 | 420 | FOO 421 | 422 |
423 | 424 | THIS SHOULD BE A CODE BLOCK, THOUGH: 425 | 426 |
427 | foo 428 |
429 | 430 | AS SHOULD THIS: 431 | 432 |
foo
433 | 434 | NOW, NESTED: 435 | 436 |
437 | 438 |
439 | 440 |
441 | 442 | FOO 443 | 444 |
445 | 446 |
447 | 448 |
449 | 450 | THIS SHOULD JUST BE AN HTML COMMENT: 451 | 452 | ```{=html} 453 | 454 | ``` 455 | MULTILINE: 456 | 457 | ```{=html} 458 | 462 | ``` 463 | ```{=html} 464 | 467 | ``` 468 | CODE BLOCK: 469 | 470 | 471 | 472 | JUST PLAIN COMMENT, WITH TRAILING SPACES ON THE LINE: 473 | 474 | ```{=html} 475 | 476 | ``` 477 | CODE: 478 | 479 |
480 | 481 | HR'S: 482 | 483 | ```{=html} 484 |
485 | ``` 486 | ```{=html} 487 |
488 | ``` 489 | ```{=html} 490 |
491 | ``` 492 | ```{=html} 493 |
494 | ``` 495 | ```{=html} 496 |
497 | ``` 498 | ```{=html} 499 |
500 | ``` 501 | ```{=html} 502 |
503 | ``` 504 | ```{=html} 505 |
506 | ``` 507 | ```{=html} 508 |
509 | ``` 510 | 511 | ------------------------------------------------------------------------ 512 | 513 | # INLINE MARKUP 514 | 515 | THIS IS *EMPHASIZED*, AND SO *IS THIS*. 516 | 517 | THIS IS **STRONG**, AND SO **IS THIS**. 518 | 519 | AN *[EMPHASIZED LINK](/url)*. 520 | 521 | ***THIS IS STRONG AND EM.*** 522 | 523 | SO IS ***THIS*** WORD. 524 | 525 | ***THIS IS STRONG AND EM.*** 526 | 527 | SO IS ***THIS*** WORD. 528 | 529 | THIS IS CODE: `>`, `$`, `\`, `\$`, ``. 530 | 531 | ~~THIS IS *STRIKEOUT*.~~ 532 | 533 | SUPERSCRIPTS: A^BC^D A^*HELLO*^ A^HELLO THERE^. 534 | 535 | SUBSCRIPTS: H~2~O, H~23~O, H~MANY OF THEM~O. 536 | 537 | THESE SHOULD NOT BE SUPERSCRIPTS OR SUBSCRIPTS, BECAUSE OF THE UNESCAPED 538 | SPACES: A\^B C\^D, A\~B C\~D. 539 | 540 | ------------------------------------------------------------------------ 541 | 542 | # SMART QUOTES, ELLIPSES, DASHES 543 | 544 | "HELLO," SAID THE SPIDER. "'SHELOB' IS MY NAME." 545 | 546 | 'A', 'B', AND 'C' ARE LETTERS. 547 | 548 | 'OAK,' 'ELM,' AND 'BEECH' ARE NAMES OF TREES. SO IS 'PINE.' 549 | 550 | 'HE SAID, "I WANT TO GO."' WERE YOU ALIVE IN THE 70'S? 551 | 552 | HERE IS SOME QUOTED '`code`' AND A "[QUOTED 553 | LINK](http://example.com/?foo=1&bar=2)". 554 | 555 | SOME DASHES: ONE---TWO --- THREE---FOUR --- FIVE. 556 | 557 | DASHES BETWEEN NUMBERS: 5--7, 255--66, 1987--1999. 558 | 559 | ELLIPSES...AND...AND.... 560 | 561 | ------------------------------------------------------------------------ 562 | 563 | # LATEX 564 | 565 | - `\cite[22-23]{smith.1899}`{=tex} 566 | - $2+2=4$ 567 | - $x \in y$ 568 | - $\alpha \wedge \omega$ 569 | - $223$ 570 | - $p$-TREE 571 | - HERE'S SOME DISPLAY MATH: 572 | $$\frac{d}{dx}f(x)=\lim_{h\to 0}\frac{f(x+h)-f(x)}{h}$$ 573 | - HERE'S ONE THAT HAS A LINE BREAK IN IT: 574 | $\alpha + \omega \times x^2$. 575 | 576 | THESE SHOULDN'T BE MATH: 577 | 578 | - TO GET THE FAMOUS EQUATION, WRITE `$e = mc^2$`. 579 | - \$22,000 IS A *LOT* OF MONEY. SO IS \$34,000. (IT WORKED IF "LOT" IS 580 | EMPHASIZED.) 581 | - SHOES (\$20) AND SOCKS (\$5). 582 | - ESCAPED `$`: \$73 *THIS SHOULD BE EMPHASIZED* 23\$. 583 | 584 | HERE'S A LATEX TABLE: 585 | 586 | ```{=tex} 587 | \begin{tabular}{|l|l|}\hline 588 | Animal & Number \\ \hline 589 | Dog & 2 \\ 590 | Cat & 1 \\ \hline 591 | \end{tabular} 592 | ``` 593 | 594 | ------------------------------------------------------------------------ 595 | 596 | # SPECIAL CHARACTERS 597 | 598 | HERE IS SOME UNICODE: 599 | 600 | - I HAT: Î 601 | - O UMLAUT: Ö 602 | - SECTION: § 603 | - SET MEMBERSHIP: ∈ 604 | - COPYRIGHT: © 605 | 606 | AT&T HAS AN AMPERSAND IN THEIR NAME. 607 | 608 | AT&T IS ANOTHER WAY TO WRITE IT. 609 | 610 | THIS & THAT. 611 | 612 | 4 \< 5. 613 | 614 | 6 \> 5. 615 | 616 | BACKSLASH: \\ 617 | 618 | BACKTICK: \` 619 | 620 | ASTERISK: \* 621 | 622 | UNDERSCORE: \_ 623 | 624 | LEFT BRACE: { 625 | 626 | RIGHT BRACE: } 627 | 628 | LEFT BRACKET: \[ 629 | 630 | RIGHT BRACKET: \] 631 | 632 | LEFT PAREN: ( 633 | 634 | RIGHT PAREN: ) 635 | 636 | GREATER-THAN: \> 637 | 638 | HASH: \# 639 | 640 | PERIOD: . 641 | 642 | BANG: ! 643 | 644 | PLUS: + 645 | 646 | MINUS: - 647 | 648 | ------------------------------------------------------------------------ 649 | 650 | # LINKS 651 | 652 | ## EXPLICIT 653 | 654 | JUST A [URL](/url/). 655 | 656 | [URL AND TITLE](/url/ "title"). 657 | 658 | [URL AND TITLE](/url/ "title preceded by two spaces"). 659 | 660 | [URL AND TITLE](/url/ "title preceded by a tab"). 661 | 662 | [URL AND TITLE](/url/ "title with "quotes" in it") 663 | 664 | [URL AND TITLE](/url/ "title with single quotes") 665 | 666 | [WITH_UNDERSCORE](/url/with_underscore) 667 | 668 | [EMAIL LINK](mailto:nobody@nowhere.net) 669 | 670 | [EMPTY](). 671 | 672 | ## REFERENCE 673 | 674 | FOO [BAR](/url/). 675 | 676 | WITH [EMBEDDED \[BRACKETS\]](/url/). 677 | 678 | [B](/url/) BY ITSELF SHOULD BE A LINK. 679 | 680 | INDENTED [ONCE](/url). 681 | 682 | INDENTED [TWICE](/url). 683 | 684 | INDENTED [THRICE](/url). 685 | 686 | THIS SHOULD \[NOT\]\[\] BE A LINK. 687 | 688 | [not]: /url 689 | 690 | FOO [BAR](/url/ "Title with "quotes" inside"). 691 | 692 | FOO [BIZ](/url/ "Title with "quote" inside"). 693 | 694 | ## WITH AMPERSANDS 695 | 696 | HERE'S A [LINK WITH AN AMPERSAND IN THE 697 | URL](http://example.com/?foo=1&bar=2). 698 | 699 | HERE'S A LINK WITH AN AMERSAND IN THE LINK TEXT: 700 | [AT&T](http://att.com/ "AT&T"). 701 | 702 | HERE'S AN [INLINE LINK](/script?foo=1&bar=2). 703 | 704 | HERE'S AN [INLINE LINK IN POINTY BRACES](/script?foo=1&bar=2). 705 | 706 | ## AUTOLINKS 707 | 708 | WITH AN AMPERSAND: 709 | [HTTP://EXAMPLE.COM/?FOO=1&BAR=2](http://example.com/?foo=1&bar=2){.uri} 710 | 711 | - IN A LIST? 712 | - [HTTP://EXAMPLE.COM/](http://example.com/){.uri} 713 | - IT SHOULD. 714 | 715 | AN E-MAIL ADDRESS: 716 | [NOBODY@NOWHERE.NET](mailto:nobody@nowhere.net){.email} 717 | 718 | > BLOCKQUOTED: [HTTP://EXAMPLE.COM/](http://example.com/){.uri} 719 | 720 | AUTO-LINKS SHOULD NOT OCCUR HERE: `` 721 | 722 | or here: 723 | 724 | ------------------------------------------------------------------------ 725 | 726 | # IMAGES 727 | 728 | FROM "VOYAGE DANS LA LUNE" BY GEORGES MELIES (1902): 729 | 730 |
731 | LALUNE 732 |
lalune
733 |
734 | 735 | HERE IS A MOVIE ![MOVIE](movie.jpg) ICON. 736 | 737 | ------------------------------------------------------------------------ 738 | 739 | # FOOTNOTES 740 | 741 | HERE IS A FOOTNOTE REFERENCE,[^1] AND ANOTHER.[^2] THIS SHOULD *NOT* BE 742 | A FOOTNOTE REFERENCE, BECAUSE IT CONTAINS A SPACE.\[\^MY NOTE\] HERE IS 743 | AN INLINE NOTE.[^3] 744 | 745 | > NOTES CAN GO IN QUOTES.[^4] 746 | 747 | 1. AND IN LIST ITEMS.[^5] 748 | 749 | THIS PARAGRAPH SHOULD NOT BE PART OF THE NOTE, AS IT IS NOT INDENTED. 750 | 751 | [^1]: HERE IS THE FOOTNOTE. IT CAN GO ANYWHERE AFTER THE FOOTNOTE 752 | REFERENCE. IT NEED NOT BE PLACED AT THE END OF THE DOCUMENT. 753 | 754 | [^2]: HERE'S THE LONG NOTE. THIS ONE CONTAINS MULTIPLE BLOCKS. 755 | 756 | SUBSEQUENT BLOCKS ARE INDENTED TO SHOW THAT THEY BELONG TO THE 757 | FOOTNOTE (AS WITH LIST ITEMS). 758 | 759 | { } 760 | 761 | IF YOU WANT, YOU CAN INDENT EVERY LINE, BUT YOU CAN ALSO BE LAZY AND 762 | JUST INDENT THE FIRST LINE OF EACH BLOCK. 763 | 764 | [^3]: THIS IS *EASIER* TO TYPE. INLINE NOTES MAY CONTAIN 765 | [LINKS](http://google.com) AND `]` VERBATIM CHARACTERS, AS WELL AS 766 | \[BRACKETED TEXT\]. 767 | 768 | [^4]: IN QUOTE. 769 | 770 | [^5]: IN LIST. 771 | --------------------------------------------------------------------------------