diff --git a/Cargo.lock b/Cargo.lock index e4dfd3f..5af7513 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -93,39 +93,6 @@ dependencies = [ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "crossbeam-deque" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crossbeam-epoch 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "memoffset 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", - "scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "crossbeam-queue" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "crossbeam-utils" version = "0.7.2" @@ -136,11 +103,6 @@ dependencies = [ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "either" -version = "1.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "flate2" version = "1.0.14" @@ -219,19 +181,6 @@ name = "linked-hash-map" version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "maybe-uninit" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "memoffset" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "minify" version = "1.1.1" @@ -262,15 +211,6 @@ dependencies = [ "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "num_cpus" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "hermit-abi 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "numtoa" version = "0.1.0" @@ -354,28 +294,6 @@ dependencies = [ "proc-macro2 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "rayon" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crossbeam-deque 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rayon-core 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rayon-core" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crossbeam-deque 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-queue 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.13.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "redox_syscall" version = "0.1.56" @@ -412,11 +330,6 @@ dependencies = [ "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "serde" version = "1.0.111" @@ -444,12 +357,11 @@ dependencies = [ [[package]] name = "snekdown" -version = "0.2.5" +version = "0.3.0" dependencies = [ "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "htmlescape 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "minify 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rayon 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "structopt 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "syntect 4.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "termion 1.5.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -631,11 +543,7 @@ dependencies = [ "checksum chrono 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "80094f509cf8b5ae86a4966a39b3ff66cd7e2a3e594accec3743ff3fabeab5b2" "checksum clap 2.33.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bdfa80d47f954d53a35a64987ca1422f495b8d6483c0fe9f7117b36c2a792129" "checksum crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1" -"checksum crossbeam-deque 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "9f02af974daeee82218205558e51ec8768b48cf524bd01d550abe5573a608285" -"checksum crossbeam-epoch 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace" -"checksum crossbeam-queue 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ab6bffe714b6bb07e42f201352c34f51fefd355ace793f9e638ebd52d23f98d2" "checksum crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" -"checksum either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" "checksum flate2 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)" = "2cfff41391129e0a856d6d822600b8d71179d46879e310417eb9c762eb178b42" "checksum fnv 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" "checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" @@ -648,13 +556,10 @@ dependencies = [ "checksum libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)" = "9457b06509d27052635f90d6466700c65095fdf75409b3fbdd903e988b886f49" "checksum line-wrap 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f30344350a2a51da54c1d53be93fade8a237e545dbcc4bdbe635413f2117cab9" "checksum linked-hash-map 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8dd5a6d5999d9907cda8ed67bbd137d3af8085216c2ac62de5be860bd41f304a" -"checksum maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" -"checksum memoffset 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b4fc2c02a7e374099d4ee95a193111f72d2110197fe200272371758f6c3643d8" "checksum minify 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2ef7c582bd7587da887914eaf294897e82f2f5c98b741f137f2a918cd26a885b" "checksum miniz_oxide 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "aa679ff6578b1cddee93d7e82e263b94a575e0bfced07284eb0c037c1d2416a5" "checksum num-integer 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba" "checksum num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096" -"checksum num_cpus 1.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" "checksum numtoa 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef" "checksum onig 6.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bd91ccd8a02fce2f7e8a86655aec67bc6c171e6f8e704118a0e8c4b866a05a8a" "checksum onig_sys 69.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3814583fad89f3c60ae0701d80e87e1fd3028741723deda72d0d4a0ecf0cb0db" @@ -664,15 +569,12 @@ dependencies = [ "checksum proc-macro-error-attr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4f5444ead4e9935abd7f27dc51f7e852a0569ac888096d5ec2499470794e2e53" "checksum proc-macro2 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)" = "1502d12e458c49a4c9cbff560d0fe0060c252bc29799ed94ca2ed4bb665a0101" "checksum quote 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "54a21852a652ad6f610c9510194f398ff6f8692e334fd1145fed931f7fbe44ea" -"checksum rayon 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "db6ce3297f9c85e16621bb8cca38a06779ffc31bb8184e1be4bed2be4678a098" -"checksum rayon-core 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "08a89b46efaf957e52b18062fb2f4660f8b8a4dde1807ca002690868ef2c85a9" "checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" "checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" "checksum regex-syntax 0.6.18 (registry+https://github.com/rust-lang/crates.io-index)" = "26412eb97c6b088a6997e05f69403a802a92d520de2f8e63c2b65f9e0f47c4e8" "checksum ryu 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "ed3d612bc64430efeb3f7ee6ef26d590dce0c43249217bddc62112540c7941e1" "checksum safemem 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" "checksum same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -"checksum scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" "checksum serde 1.0.111 (registry+https://github.com/rust-lang/crates.io-index)" = "c9124df5b40cbd380080b2cc6ab894c040a3070d995f5c9dc77e18c34a8ae37d" "checksum serde_derive 1.0.111 (registry+https://github.com/rust-lang/crates.io-index)" = "3f2c3ac8e6ca1e9c80b8be1023940162bf81ae3cffbb1809474152f2ce1eb250" "checksum serde_json 1.0.53 (registry+https://github.com/rust-lang/crates.io-index)" = "993948e75b189211a9b31a7528f950c6adc21f9720b6438ff80a7fa2f864cea2" diff --git a/Cargo.toml b/Cargo.toml index 4ed3b94..a4fca26 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "snekdown" -version = "0.2.5" +version = "0.3.0" authors = ["trivernis "] edition = "2018" license-file = "LICENSE" @@ -24,5 +24,4 @@ structopt = "0.3.14" termion = "1.5.5" minify = "1.1.1" htmlescape = "0.3.1" -syntect = "4.2.0" -rayon = "1.3.0" \ No newline at end of file +syntect = "4.2.0" \ No newline at end of file diff --git a/src/elements.rs b/src/elements.rs index e19dbd9..bcc34ff 100644 --- a/src/elements.rs +++ b/src/elements.rs @@ -23,7 +23,14 @@ macro_rules! test_block { }; } -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug)] +pub enum Element { + Block(Box), + Inline(Box), + SubText(Box), +} + +#[derive(Clone, Debug)] pub enum Block { Section(Section), Paragraph(Paragraph), @@ -32,44 +39,47 @@ pub enum Block { CodeBlock(CodeBlock), Quote(Quote), Import(Import), + Placeholder(Arc>), } -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug)] pub enum Inline { Text(Text), Ruler(Ruler), } -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug)] pub struct Document { pub(crate) elements: Vec, pub(crate) is_root: bool, + pub(crate) path: Option, + pub(crate) placeholders: Vec>>, } -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug)] pub struct Section { pub(crate) header: Header, pub(crate) elements: Vec, } -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug)] pub struct Header { pub(crate) size: u8, pub(crate) line: Inline, } -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug)] pub struct Paragraph { pub(crate) elements: Vec, } -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug)] pub struct List { pub(crate) ordered: bool, pub(crate) items: Vec, } -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug)] pub struct ListItem { pub(crate) text: Inline, pub(crate) level: u16, @@ -77,34 +87,29 @@ pub struct ListItem { pub(crate) children: Vec, } -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug)] pub struct Table { pub(crate) header: Row, pub(crate) rows: Vec, } -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug)] pub struct Row { pub(crate) cells: Vec, } -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug)] pub struct Cell { pub(crate) text: Inline, } -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug)] pub struct CodeBlock { pub(crate) language: String, pub(crate) code: String, } -#[derive(Clone, Debug, PartialEq)] -pub struct Code { - code: String, -} - -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug)] pub struct Quote { pub(crate) metadata: Option, pub(crate) text: Vec, @@ -116,28 +121,27 @@ pub struct Import { pub(crate) anchor: Arc>, } -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug)] pub struct ImportAnchor { pub(crate) document: Option, } -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug)] pub struct InlineMetadata { pub(crate) data: String, } -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug)] pub struct Ruler {} -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug)] pub struct Text { pub subtext: Vec, } -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug)] pub enum SubText { Plain(PlainText), - Code(Code), Bold(BoldText), Italic(ItalicText), Underlined(UnderlinedText), @@ -145,50 +149,57 @@ pub enum SubText { Monospace(MonospaceText), Url(Url), Image(Image), + Placeholder(Arc>), } -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug)] pub struct PlainText { pub(crate) value: String, } -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug)] pub struct BoldText { pub(crate) value: Box, } -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug)] pub struct ItalicText { pub(crate) value: Box, } -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug)] pub struct UnderlinedText { pub(crate) value: Box, } -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug)] pub struct StrikedText { pub(crate) value: Box, } -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug)] pub struct MonospaceText { pub(crate) value: PlainText, } -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug)] pub struct Url { pub description: Option, pub url: String, } -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug)] pub struct Image { pub(crate) url: Url, pub(crate) metadata: Option, } +#[derive(Clone, Debug)] +pub struct Placeholder { + pub(crate) name: String, + pub(crate) value: Option, +} + // implementations impl Document { @@ -196,6 +207,8 @@ impl Document { Self { elements: Vec::new(), is_root, + path: None, + placeholders: Vec::new(), } } @@ -203,6 +216,10 @@ impl Document { self.elements.push(element) } + pub fn add_placeholder(&mut self, placeholder: Arc>) { + self.placeholders.push(placeholder); + } + pub fn find(&self, block_type: &str, nested: bool) -> Vec<&Block> { let mut found = Vec::new(); let mut found_self = self @@ -223,6 +240,32 @@ impl Document { found } + + pub fn create_toc(&self) -> List { + let mut list = List::new(); + list.ordered = true; + self.elements.iter().for_each(|e| { + if let Block::Section(sec) = e { + let mut item = ListItem::new(sec.header.line.clone(), 1, true); + item.children.append(&mut sec.get_toc_list().items); + list.add_item(item); + } + }); + + list + } + + pub fn parse_placeholders(&mut self) { + self.placeholders.iter().for_each(|p| { + let mut pholder = p.lock().unwrap(); + match pholder.name.to_ascii_lowercase().as_str() { + "toc" => { + pholder.set_value(Element::Block(Box::new(Block::List(self.create_toc())))) + } + _ => {} + } + }) + } } impl Section { @@ -256,6 +299,19 @@ impl Section { found } + + pub fn get_toc_list(&self) -> List { + let mut list = List::new(); + self.elements.iter().for_each(|e| { + if let Block::Section(sec) = e { + let mut item = ListItem::new(sec.header.line.clone(), 1, true); + item.children.append(&mut sec.get_toc_list().items); + list.add_item(item); + } + }); + + list + } } impl Paragraph { @@ -367,3 +423,13 @@ impl PartialEq for Import { self.path == other.path } } + +impl Placeholder { + pub fn new(name: String) -> Self { + Self { name, value: None } + } + + pub fn set_value(&mut self, value: Element) { + self.value = Some(value); + } +} diff --git a/src/format/html.rs b/src/format/html.rs index e30cc87..cc7fe61 100644 --- a/src/format/html.rs +++ b/src/format/html.rs @@ -1,7 +1,6 @@ use crate::elements::*; use htmlescape::{encode_attribute, encode_minimal}; use minify::html::minify; -use rayon::prelude::*; use std::cell::RefCell; use syntect::highlighting::ThemeSet; use syntect::html::highlighted_html_for_string; @@ -21,6 +20,16 @@ pub trait ToHtml { fn to_html(&self) -> String; } +impl ToHtml for Element { + fn to_html(&self) -> String { + match self { + Element::Block(block) => block.to_html(), + Element::Inline(inline) => inline.to_html(), + Element::SubText(sub) => sub.to_html(), + } + } +} + impl ToHtml for Inline { fn to_html(&self) -> String { match self { @@ -41,7 +50,7 @@ impl ToHtml for SubText { SubText::Underlined(under) => under.to_html(), SubText::Bold(bold) => bold.to_html(), SubText::Image(img) => img.to_html(), - _ => "".to_string(), + SubText::Placeholder(placeholder) => placeholder.lock().unwrap().to_html(), } } } @@ -56,6 +65,7 @@ impl ToHtml for Block { Block::Quote(quote) => quote.to_html(), Block::Section(section) => section.to_html(), Block::Import(import) => import.to_html(), + Block::Placeholder(placeholder) => placeholder.lock().unwrap().to_html(), } } } @@ -64,19 +74,23 @@ impl ToHtml for Document { fn to_html(&self) -> String { let inner = self .elements - .par_iter() - .map(|e| e.to_html()) - .reduce(|| "".to_string(), |a, b| format!("{}{}", a, b)); + .iter() + .fold("".to_string(), |a, b| format!("{}{}", a, b.to_html())); + let path = if let Some(path) = &self.path { + format!("path='{}'", encode_attribute(path.as_str())) + } else { + "".to_string() + }; if self.is_root { let style = minify(std::include_str!("assets/style.css")); format!( - "\n
{}
", - style, inner + "\n
{}
", + path, style, inner ) } else { format!( - "
{}
", - inner + "
{}
", + path, inner ) } } @@ -328,3 +342,13 @@ impl ToHtml for PlainText { encode_minimal(self.value.clone().as_str()) } } + +impl ToHtml for Placeholder { + fn to_html(&self) -> String { + if let Some(value) = &self.value { + value.to_html() + } else { + format!("Unknown placeholder '{}'!", encode_minimal(&self.name)) + } + } +} diff --git a/src/parser.rs b/src/parser.rs index dafc340..45d0add 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -98,6 +98,7 @@ pub struct Parser { wg: WaitGroup, is_child: bool, subtext_break_at: Vec, + document: Document, } impl Parser { @@ -142,6 +143,7 @@ impl Parser { wg: WaitGroup::new(), is_child, subtext_break_at: Vec::new(), + document: Document::new(!is_child), } } @@ -331,10 +333,11 @@ impl Parser { /// parses the given text into a document pub fn parse(&mut self) -> Document { - let mut document = Document::new(!self.is_child); + self.document.path = self.path.clone(); + while self.index < self.text.len() { match self.parse_block() { - Ok(block) => document.add_element(block), + Ok(block) => self.document.add_element(block), Err(err) => { if let Some(path) = &self.path { if let Some(position) = err.get_position(&self.get_text()) { @@ -359,14 +362,18 @@ impl Parser { let wg = self.wg.clone(); self.wg = WaitGroup::new(); + self.document.parse_placeholders(); wg.wait(); + let document = self.document.clone(); + self.document = Document::new(!self.is_child); + document } /// Parses a block Token pub fn parse_block(&mut self) -> Result { if let Some(section) = self.section_return { - if section <= self.section_nesting { + if section <= self.section_nesting && (self.section_nesting > 0) { return Err(ParseError::new_with_message( self.index, "invalid section nesting", @@ -389,6 +396,10 @@ impl Parser { Block::Quote(quote) } else if let Ok(import) = self.parse_import() { Block::Import(import) + } else if let Some(_) = self.section_return { + return Err(ParseError::new(self.index)); + } else if let Ok(pholder) = self.parse_placeholder() { + Block::Placeholder(pholder) } else if let Ok(paragraph) = self.parse_paragraph() { Block::Paragraph(paragraph) } else { @@ -555,6 +566,15 @@ impl Parser { if self.check_special(&IMPORT_CLOSE) { parse_option!(self.next_char(), self.index); } + // parsing success + + if self.section_nesting > 0 { + self.section_return = Some(0); + let err = ParseError::new_with_message(self.index, "import section nesting error"); + self.revert_to(start_index)?; + return Err(err); + } + self.seek_whitespace(); if let Ok(anchor) = self.import_document(path.clone()) { @@ -751,6 +771,30 @@ impl Parser { } } + /// parses a placeholder element + fn parse_placeholder(&mut self) -> Result>, ParseError> { + let start_index = self.index; + self.check_special_sequence(&SQ_PHOLDER_START)?; + let mut name = String::new(); + + while let Some(char) = self.next_char() { + if self.check_special_group(&SQ_PHOLDER_STOP) || self.check_linebreak() { + break; + } + name.push(char); + } + if self.check_special(&PHOLDER_CLOSE) { + let _ = self.next_char(); + let _ = self.next_char(); + } else { + return Err(self.revert_with_error(start_index)); + } + let placeholder = Arc::new(Mutex::new(Placeholder::new(name))); + self.document.add_placeholder(Arc::clone(&placeholder)); + + Ok(placeholder) + } + /// parses a ruler fn parse_ruler(&mut self) -> Result { let start_index = self.index; @@ -798,6 +842,9 @@ impl Parser { if let Ok(url) = self.parse_url(false) { return Ok(SubText::Url(url)); } + if let Ok(pholder) = self.parse_placeholder() { + return Ok(SubText::Placeholder(pholder)); + } match self.current_char { ASTERISK if !self.check_escaped() => { parse_option!(self.next_char(), self.index); diff --git a/src/tokens.rs b/src/tokens.rs index d29d192..a15da9e 100644 --- a/src/tokens.rs +++ b/src/tokens.rs @@ -35,6 +35,8 @@ pub(crate) const URL_CLOSE: char = L_PARENTH; pub(crate) const IMPORT_START: char = LT; pub(crate) const IMPORT_OPEN: char = R_BRACKET; pub(crate) const IMPORT_CLOSE: char = L_BRACKET; +pub(crate) const PHOLDER_OPEN: char = R_BRACKET; +pub(crate) const PHOLDER_CLOSE: char = L_BRACKET; // groups @@ -57,6 +59,8 @@ pub(crate) const LIST_SPECIAL_CHARS: [char; 4] = [MINUS, PLUS, ASTERISK, O]; pub(crate) const SQ_CODE_BLOCK: [char; 3] = [BACKTICK, BACKTICK, BACKTICK]; pub(crate) const SQ_RULER: [char; 5] = [MINUS, SPACE, MINUS, SPACE, MINUS]; +pub(crate) const SQ_PHOLDER_START: [char; 2] = [PHOLDER_OPEN, PHOLDER_OPEN]; +pub(crate) const SQ_PHOLDER_STOP: [char; 2] = [PHOLDER_CLOSE, PHOLDER_CLOSE]; // expressions