From ba7fd40f92eadc59cd6e9580d3a126186d1bd212 Mon Sep 17 00:00:00 2001 From: trivernis Date: Mon, 1 Jun 2020 16:27:06 +0200 Subject: [PATCH] Encapsulate inline parsing - Rename Inline stuff to Line - Rename SubText stuff to Inline --- Cargo.lock | 2 +- Cargo.toml | 2 +- perf.data | Bin 0 -> 32252 bytes src/format/html.rs | 32 +++--- src/parsing/charstate.rs | 1 + src/parsing/elements.rs | 48 ++++----- src/parsing/mod.rs | 1 + src/parsing/parser.rs | 191 ++++-------------------------------- src/parsing/placeholders.rs | 14 +-- src/parsing/subtext.rs | 178 +++++++++++++++++++++++++++++++++ src/parsing/tokens.rs | 11 ++- 11 files changed, 257 insertions(+), 223 deletions(-) create mode 100644 perf.data create mode 100644 src/parsing/subtext.rs diff --git a/Cargo.lock b/Cargo.lock index 7e7de78..2445e43 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -358,7 +358,7 @@ dependencies = [ [[package]] name = "snekdown" -version = "0.5.4" +version = "0.5.5" dependencies = [ "chrono 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index 7e9632d..74ead05 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "snekdown" -version = "0.5.4" +version = "0.5.5" authors = ["trivernis "] edition = "2018" license-file = "LICENSE" diff --git a/perf.data b/perf.data new file mode 100644 index 0000000000000000000000000000000000000000..c9249113a0bcbe0d85279f022ef6dd434b739e1a GIT binary patch literal 32252 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 literal 0 HcmV?d00001 diff --git a/src/format/html.rs b/src/format/html.rs index 93b65d5..9faff09 100644 --- a/src/format/html.rs +++ b/src/format/html.rs @@ -25,33 +25,33 @@ impl ToHtml for Element { match self { Element::Block(block) => block.to_html(), Element::Inline(inline) => inline.to_html(), - Element::SubText(sub) => sub.to_html(), + Element::Line(line) => line.to_html(), } } } -impl ToHtml for Inline { +impl ToHtml for Line { fn to_html(&self) -> String { match self { - Inline::Text(text) => text.to_html(), - Inline::Ruler(ruler) => ruler.to_html(), - Inline::Anchor(anchor) => anchor.to_html(), + Line::Text(text) => text.to_html(), + Line::Ruler(ruler) => ruler.to_html(), + Line::Anchor(anchor) => anchor.to_html(), } } } -impl ToHtml for SubText { +impl ToHtml for Inline { fn to_html(&self) -> String { match self { - SubText::Url(url) => url.to_html(), - SubText::Monospace(mono) => mono.to_html(), - SubText::Striked(striked) => striked.to_html(), - SubText::Plain(plain) => plain.to_html(), - SubText::Italic(italic) => italic.to_html(), - SubText::Underlined(under) => under.to_html(), - SubText::Bold(bold) => bold.to_html(), - SubText::Image(img) => img.to_html(), - SubText::Placeholder(placeholder) => placeholder.lock().unwrap().to_html(), + Inline::Url(url) => url.to_html(), + Inline::Monospace(mono) => mono.to_html(), + Inline::Striked(striked) => striked.to_html(), + Inline::Plain(plain) => plain.to_html(), + Inline::Italic(italic) => italic.to_html(), + Inline::Underlined(under) => under.to_html(), + Inline::Bold(bold) => bold.to_html(), + Inline::Image(img) => img.to_html(), + Inline::Placeholder(placeholder) => placeholder.lock().unwrap().to_html(), } } } @@ -277,7 +277,7 @@ impl ToHtml for Ruler { } } -impl ToHtml for Text { +impl ToHtml for TextLine { fn to_html(&self) -> String { self.subtext .iter() diff --git a/src/parsing/charstate.rs b/src/parsing/charstate.rs index adcd49e..82e1212 100644 --- a/src/parsing/charstate.rs +++ b/src/parsing/charstate.rs @@ -281,6 +281,7 @@ impl CharStateMachine for Parser { } } + /// seeks until it encounters a linebreak character fn seek_until_linebreak(&mut self) { if self.check_special(&LB) { self.skip_char(); diff --git a/src/parsing/elements.rs b/src/parsing/elements.rs index 0581815..dd729ef 100644 --- a/src/parsing/elements.rs +++ b/src/parsing/elements.rs @@ -36,8 +36,8 @@ pub enum MetadataValue { #[derive(Clone, Debug)] pub enum Element { Block(Box), + Line(Box), Inline(Box), - SubText(Box), } #[derive(Clone, Debug)] @@ -53,8 +53,8 @@ pub enum Block { } #[derive(Clone, Debug)] -pub enum Inline { - Text(Text), +pub enum Line { + Text(TextLine), Ruler(Ruler), Anchor(Anchor), } @@ -77,13 +77,13 @@ pub struct Section { #[derive(Clone, Debug)] pub struct Header { pub(crate) size: u8, - pub(crate) line: Inline, + pub(crate) line: Line, pub(crate) anchor: String, } #[derive(Clone, Debug)] pub struct Paragraph { - pub(crate) elements: Vec, + pub(crate) elements: Vec, } #[derive(Clone, Debug)] @@ -94,7 +94,7 @@ pub struct List { #[derive(Clone, Debug)] pub struct ListItem { - pub(crate) text: Inline, + pub(crate) text: Line, pub(crate) level: u16, pub(crate) ordered: bool, pub(crate) children: Vec, @@ -113,7 +113,7 @@ pub struct Row { #[derive(Clone, Debug)] pub struct Cell { - pub(crate) text: Inline, + pub(crate) text: Line, } #[derive(Clone, Debug)] @@ -125,7 +125,7 @@ pub struct CodeBlock { #[derive(Clone, Debug)] pub struct Quote { pub(crate) metadata: Option, - pub(crate) text: Vec, + pub(crate) text: Vec, } #[derive(Clone, Debug)] @@ -148,12 +148,12 @@ pub struct InlineMetadata { pub struct Ruler {} #[derive(Clone, Debug)] -pub struct Text { - pub subtext: Vec, +pub struct TextLine { + pub subtext: Vec, } #[derive(Clone, Debug)] -pub enum SubText { +pub enum Inline { Plain(PlainText), Bold(BoldText), Italic(ItalicText), @@ -172,22 +172,22 @@ pub struct PlainText { #[derive(Clone, Debug)] pub struct BoldText { - pub(crate) value: Box, + pub(crate) value: Box, } #[derive(Clone, Debug)] pub struct ItalicText { - pub(crate) value: Box, + pub(crate) value: Box, } #[derive(Clone, Debug)] pub struct UnderlinedText { - pub(crate) value: Box, + pub(crate) value: Box, } #[derive(Clone, Debug)] pub struct StrikedText { - pub(crate) value: Box, + pub(crate) value: Box, } #[derive(Clone, Debug)] @@ -215,7 +215,7 @@ pub struct Placeholder { #[derive(Clone, Debug)] pub struct Anchor { - pub(crate) description: Box, + pub(crate) description: Box, pub(crate) reference: String, } @@ -266,7 +266,7 @@ impl Document { self.elements.iter().for_each(|e| match e { Block::Section(sec) => { if !sec.get_hide_in_toc() { - let mut item = ListItem::new(Inline::Anchor(sec.header.get_anchor()), 1, true); + let mut item = ListItem::new(Line::Anchor(sec.header.get_anchor()), 1, true); item.children.append(&mut sec.get_toc_list().items); list.add_item(item); } @@ -322,7 +322,7 @@ impl Section { self.elements.iter().for_each(|e| { if let Block::Section(sec) = e { if !sec.get_hide_in_toc() { - let mut item = ListItem::new(Inline::Anchor(sec.header.get_anchor()), 1, true); + let mut item = ListItem::new(Line::Anchor(sec.header.get_anchor()), 1, true); item.children.append(&mut sec.get_toc_list().items); list.add_item(item); } @@ -342,7 +342,7 @@ impl Section { } impl Header { - pub fn new(content: Inline, anchor: String) -> Self { + pub fn new(content: Line, anchor: String) -> Self { Self { size: 0, anchor, @@ -365,7 +365,7 @@ impl Paragraph { } } - pub fn add_element(&mut self, element: Inline) { + pub fn add_element(&mut self, element: Line) { self.elements.push(element) } } @@ -384,7 +384,7 @@ impl List { } impl ListItem { - pub fn new(text: Inline, level: u16, ordered: bool) -> Self { + pub fn new(text: Line, level: u16, ordered: bool) -> Self { Self { text, level, @@ -398,14 +398,14 @@ impl ListItem { } } -impl Text { +impl TextLine { pub fn new() -> Self { Self { subtext: Vec::new(), } } - pub fn add_subtext(&mut self, subtext: SubText) { + pub fn add_subtext(&mut self, subtext: Inline) { self.subtext.push(subtext) } } @@ -447,7 +447,7 @@ impl Quote { } } - pub fn add_text(&mut self, text: Text) { + pub fn add_text(&mut self, text: TextLine) { self.text.push(text) } } diff --git a/src/parsing/mod.rs b/src/parsing/mod.rs index c4bd97e..e9d6906 100644 --- a/src/parsing/mod.rs +++ b/src/parsing/mod.rs @@ -2,4 +2,5 @@ pub mod charstate; pub mod elements; pub mod parser; pub mod placeholders; +pub mod subtext; pub mod tokens; diff --git a/src/parsing/parser.rs b/src/parsing/parser.rs index 44d4b48..ac5b896 100644 --- a/src/parsing/parser.rs +++ b/src/parsing/parser.rs @@ -2,6 +2,7 @@ use super::elements::*; use super::tokens::*; use crate::parsing::charstate::CharStateMachine; use crate::parsing::placeholders::ProcessPlaceholders; +use crate::parsing::subtext::ParseInline; use crossbeam_utils::sync::WaitGroup; use std::collections::HashMap; use std::error::Error; @@ -100,7 +101,7 @@ pub struct Parser { paths: Arc>>, wg: WaitGroup, is_child: bool, - subtext_break_at: Vec, + pub(crate) inline_break_at: Vec, document: Document, } @@ -147,7 +148,7 @@ impl Parser { paths, wg: WaitGroup::new(), is_child, - subtext_break_at: Vec::new(), + inline_break_at: Vec::new(), document: Document::new(!is_child), } } @@ -352,7 +353,7 @@ impl Parser { /// parses the header of a section fn parse_header(&mut self) -> Result { let start_index = self.index; - let line = self.parse_inline()?; + let line = self.parse_line()?; let mut anchor = String::new(); self.text[start_index..self.index] .iter() @@ -399,7 +400,7 @@ impl Parser { && self.next_char() != None && (self.check_seek_inline_whitespace() || self.check_special(&LB)) { - if let Ok(text) = self.parse_text() { + if let Ok(text) = self.parse_text_line() { if text.subtext.len() > 0 { quote.add_text(text); } @@ -415,7 +416,7 @@ impl Parser { } /// Parses metadata - fn parse_inline_metadata(&mut self) -> Result { + pub(crate) fn parse_inline_metadata(&mut self) -> Result { let start_index = self.index; self.assert_special(&META_OPEN, start_index)?; self.skip_char(); @@ -523,7 +524,7 @@ impl Parser { fn parse_paragraph(&mut self) -> Result { self.seek_whitespace(); let mut paragraph = Paragraph::new(); - while let Ok(token) = self.parse_inline() { + while let Ok(token) = self.parse_line() { paragraph.add_element(token); let start_index = self.index; if self.check_special_sequence_group(&BLOCK_SPECIAL_CHARS) { @@ -623,7 +624,7 @@ impl Parser { return Err(self.revert_with_error(start_index)); } - let item = ListItem::new(self.parse_inline()?, level as u16, ordered); + let item = ListItem::new(self.parse_line()?, level as u16, ordered); Ok(item) } @@ -663,11 +664,11 @@ impl Parser { self.seek_inline_whitespace(); self.assert_special(&PIPE, start_index)?; self.skip_char(); - self.subtext_break_at.push(PIPE); + self.inline_break_at.push(PIPE); self.seek_inline_whitespace(); let mut row = Row::new(); - while let Ok(element) = self.parse_inline() { + while let Ok(element) = self.parse_line() { row.add_cell(Cell { text: element }); if self.check_special(&PIPE) { if self.next_char() == None { @@ -679,7 +680,7 @@ impl Parser { } self.seek_inline_whitespace(); } - self.subtext_break_at.clear(); + self.inline_break_at.clear(); if row.cells.len() > 0 { Ok(row) @@ -689,21 +690,21 @@ impl Parser { } /// parses inline definitions - fn parse_inline(&mut self) -> Result { + fn parse_line(&mut self) -> Result { if self.index > self.text.len() { Err(ParseError::new(self.index)) } else { if let Ok(ruler) = self.parse_ruler() { - return Ok(Inline::Ruler(ruler)); - } else if let Ok(text) = self.parse_text() { - return Ok(Inline::Text(text)); + return Ok(Line::Ruler(ruler)); + } else if let Ok(text) = self.parse_text_line() { + return Ok(Line::Text(text)); } return Err(ParseError::new(self.index)); } } /// parses a placeholder element - fn parse_placeholder(&mut self) -> Result>, ParseError> { + pub(crate) fn parse_placeholder(&mut self) -> Result>, ParseError> { let start_index = self.index; self.assert_special_sequence(&SQ_PHOLDER_START, self.index)?; self.skip_char(); @@ -731,9 +732,9 @@ impl Parser { } /// Parses a line of text - fn parse_text(&mut self) -> Result { - let mut text = Text::new(); - while let Ok(subtext) = self.parse_subtext() { + fn parse_text_line(&mut self) -> Result { + let mut text = TextLine::new(); + while let Ok(subtext) = self.parse_inline() { text.add_subtext(subtext); let current_index = self.index; if self.next_char() == None { @@ -748,158 +749,4 @@ impl Parser { Ok(text) } - - /// parses subtext, the formatting parts of a line (Text) - fn parse_subtext(&mut self) -> Result { - if self.check_linebreak() { - return Err(ParseError::new(self.index)); - } - if let Ok(image) = self.parse_image() { - return Ok(SubText::Image(image)); - } - 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); - - if self.check_special(&ASTERISK) { - parse_option!(self.next_char(), self.index); - let subtext = self.parse_subtext()?; - if self.check_special(&ASTERISK) { - self.skip_char(); - if self.check_special(&ASTERISK) { - self.skip_char(); - } - } - Ok(SubText::Bold(BoldText { - value: Box::new(subtext), - })) - } else { - let subtext = self.parse_subtext()?; - if self.check_special(&ASTERISK) { - self.skip_char(); - } - Ok(SubText::Italic(ItalicText { - value: Box::new(subtext), - })) - } - } - UNDERSCR if !self.check_escaped() => { - parse_option!(self.next_char(), self.index); - let subtext = self.parse_subtext()?; - parse_option!(self.next_char(), self.index); - Ok(SubText::Underlined(UnderlinedText { - value: Box::new(subtext), - })) - } - TILDE if !self.check_escaped() => { - parse_option!(self.next_char(), self.index); - let subtext = self.parse_subtext()?; - if self.check_special(&TILDE) { - parse_option!(self.next_char(), self.index); - } - Ok(SubText::Striked(StrikedText { - value: Box::new(subtext), - })) - } - BACKTICK if !self.check_escaped() => { - parse_option!(self.next_char(), self.index); - let content = self.get_string_until(&[BACKTICK, LB], &[])?; - if self.check_special(&BACKTICK) { - parse_option!(self.next_char(), self.index) - } - Ok(SubText::Monospace(MonospaceText { value: content })) - } - PIPE if !self.check_escaped() => Err(ParseError::new(self.index)), // handling of table cells - _ => Ok(SubText::Plain(self.parse_plain_text()?)), - } - } - - /// parses an image url - fn parse_image(&mut self) -> Result { - let start_index = self.index; - self.seek_inline_whitespace(); - self.assert_special(&IMG_START, start_index)?; - self.skip_char(); - - if let Ok(url) = self.parse_url(true) { - let metadata = if let Ok(meta) = self.parse_inline_metadata() { - Some(meta) - } else { - None - }; - Ok(Image { url, metadata }) - } else { - Err(self.revert_with_error(start_index)) - } - } - - // parses an url - fn parse_url(&mut self, short_syntax: bool) -> Result { - let start_index = self.index; - self.seek_inline_whitespace(); - - let mut description = String::new(); - if self.check_special(&DESC_OPEN) { - self.skip_char(); - description = if let Ok(desc) = self.get_string_until(&[DESC_CLOSE], &[LB]) { - desc - } else { - return Err(self.revert_with_error(start_index)); - }; - } else if !short_syntax { - return Err(self.revert_with_error(start_index)); - } - self.skip_char(); - self.assert_special(&URL_OPEN, start_index)?; - self.skip_char(); - self.seek_inline_whitespace(); - - let url = if let Ok(url_str) = self.get_string_until(&[URL_CLOSE], &[LB]) { - url_str - } else { - return Err(self.revert_with_error(start_index)); - }; - self.skip_char(); - - if description.is_empty() { - Ok(Url::new(None, url)) - } else { - Ok(Url::new(Some(description), url)) - } - } - - /// parses plain text as a string until it encounters an unescaped special inline char - fn parse_plain_text(&mut self) -> Result { - let mut current_char = self.current_char; - let mut characters = String::new(); - let mut count = 0; - loop { - if self.check_special_group(&INLINE_SPECIAL_CHARS) - || (count > 0 && self.check_special_group(&INLINE_SPECIAL_CHARS_SECOND)) - || (count > 0 && self.check_special_group(&self.subtext_break_at)) - { - break; - } else if !self.check_special(&SPECIAL_ESCAPE) { - characters.push(current_char) - } - if let Some(character) = self.next_char() { - current_char = character; - } else { - break; - } - count += 1; - } - - if characters.len() > 0 { - Ok(PlainText { value: characters }) - } else { - Err(ParseError::new(self.index)) - } - } } diff --git a/src/parsing/placeholders.rs b/src/parsing/placeholders.rs index 7345e26..923a833 100644 --- a/src/parsing/placeholders.rs +++ b/src/parsing/placeholders.rs @@ -8,15 +8,15 @@ macro_rules! block { } #[allow(unused)] -macro_rules! inline { +macro_rules! line { ($inner:expr) => { - Element::Inline(Box::new($inner)) + Element::Line(Box::new($inner)) }; } -macro_rules! subtext { +macro_rules! inline { ($inner:expr) => { - Element::SubText(Box::new($inner)) + Element::Inline(Box::new($inner)) }; } @@ -36,13 +36,13 @@ impl ProcessPlaceholders for Document { let mut pholder = p.lock().unwrap(); match pholder.name.to_ascii_lowercase().as_str() { P_TOC => pholder.set_value(block!(Block::List(self.create_toc()))), - P_DATE => pholder.set_value(subtext!(SubText::Plain(PlainText { + P_DATE => pholder.set_value(inline!(Inline::Plain(PlainText { value: get_date_string() }))), - P_TIME => pholder.set_value(subtext!(SubText::Plain(PlainText { + P_TIME => pholder.set_value(inline!(Inline::Plain(PlainText { value: get_time_string() }))), - P_DATETIME => pholder.set_value(subtext!(SubText::Plain(PlainText { + P_DATETIME => pholder.set_value(inline!(Inline::Plain(PlainText { value: format!("{} {}", get_date_string(), get_time_string()) }))), _ => {} diff --git a/src/parsing/subtext.rs b/src/parsing/subtext.rs new file mode 100644 index 0000000..7b5cd8e --- /dev/null +++ b/src/parsing/subtext.rs @@ -0,0 +1,178 @@ +use super::charstate::CharStateMachine; +use super::elements::*; +use super::parser::ParseError; +use super::tokens::*; +use crate::Parser; + +pub(crate) trait ParseInline { + fn parse_inline(&mut self) -> Result; + fn parse_image(&mut self) -> Result; + fn parse_url(&mut self, short_syntax: bool) -> Result; + fn parse_bold(&mut self) -> Result; + fn parse_italic(&mut self) -> Result; + fn parse_striked(&mut self) -> Result; + fn parse_monospace(&mut self) -> Result; + fn parse_underlined(&mut self) -> Result; + fn parse_plain(&mut self) -> Result; + fn parse_surrounded(&mut self, surrounding: &char) -> Result; +} + +impl ParseInline for Parser { + /// parses Inline, the formatting parts of a line (Text) + fn parse_inline(&mut self) -> Result { + if self.check_special(&PIPE) || self.check_linebreak() { + Err(ParseError::new(self.index)) + } else if let Ok(image) = self.parse_image() { + Ok(Inline::Image(image)) + } else if let Ok(url) = self.parse_url(false) { + Ok(Inline::Url(url)) + } else if let Ok(pholder) = self.parse_placeholder() { + Ok(Inline::Placeholder(pholder)) + } else if let Ok(bold) = self.parse_bold() { + Ok(Inline::Bold(bold)) + } else if let Ok(italic) = self.parse_italic() { + Ok(Inline::Italic(italic)) + } else if let Ok(under) = self.parse_underlined() { + Ok(Inline::Underlined(under)) + } else if let Ok(mono) = self.parse_monospace() { + Ok(Inline::Monospace(mono)) + } else if let Ok(striked) = self.parse_striked() { + Ok(Inline::Striked(striked)) + } else { + Ok(Inline::Plain(self.parse_plain()?)) + } + } + + /// parses an image url + fn parse_image(&mut self) -> Result { + let start_index = self.index; + self.seek_inline_whitespace(); + self.assert_special(&IMG_START, start_index)?; + self.skip_char(); + + if let Ok(url) = self.parse_url(true) { + let metadata = if let Ok(meta) = self.parse_inline_metadata() { + Some(meta) + } else { + None + }; + Ok(Image { url, metadata }) + } else { + Err(self.revert_with_error(start_index)) + } + } + + // parses an url + fn parse_url(&mut self, short_syntax: bool) -> Result { + let start_index = self.index; + self.seek_inline_whitespace(); + + let mut description = String::new(); + if self.check_special(&DESC_OPEN) { + self.skip_char(); + description = if let Ok(desc) = self.get_string_until(&[DESC_CLOSE], &[LB]) { + desc + } else { + return Err(self.revert_with_error(start_index)); + }; + } else if !short_syntax { + return Err(self.revert_with_error(start_index)); + } + self.skip_char(); + self.assert_special(&URL_OPEN, start_index)?; + self.skip_char(); + self.seek_inline_whitespace(); + + let url = if let Ok(url_str) = self.get_string_until(&[URL_CLOSE], &[LB]) { + url_str + } else { + return Err(self.revert_with_error(start_index)); + }; + self.skip_char(); + + if description.is_empty() { + Ok(Url::new(None, url)) + } else { + Ok(Url::new(Some(description), url)) + } + } + + /// parses bold text with must start with two asterisks + fn parse_bold(&mut self) -> Result { + let start_index = self.index; + self.assert_special_sequence(&BOLD, start_index)?; + self.skip_char(); + let inline = self.parse_inline()?; + self.assert_special_sequence(&BOLD, start_index)?; + self.skip_char(); + + Ok(BoldText { + value: Box::new(inline), + }) + } + + fn parse_italic(&mut self) -> Result { + Ok(ItalicText { + value: Box::new(self.parse_surrounded(&ITALIC)?), + }) + } + + fn parse_striked(&mut self) -> Result { + Ok(StrikedText { + value: Box::new(self.parse_surrounded(&STRIKED)?), + }) + } + + /// parses monospace text (inline-code) that isn't allowed to contain special characters + fn parse_monospace(&mut self) -> Result { + let start_index = self.index; + self.assert_special(&BACKTICK, start_index)?; + self.skip_char(); + let content = self.get_string_until(&[BACKTICK, LB], &[])?; + self.assert_special(&BACKTICK, start_index)?; + self.skip_char(); + + Ok(MonospaceText { value: content }) + } + + fn parse_underlined(&mut self) -> Result { + Ok(UnderlinedText { + value: Box::new(self.parse_surrounded(&UNDERLINED)?), + }) + } + + /// parses plain text as a string until it encounters an unescaped special inline char + fn parse_plain(&mut self) -> Result { + if self.check_linebreak() { + return Err(ParseError::new(self.index)); + } + let mut characters = String::new(); + characters.push(self.current_char); + while let Some(ch) = self.next_char() { + if self.check_special_group(&INLINE_SPECIAL_CHARS) + || self.check_special_group(&self.inline_break_at) + { + break; + } + characters.push(ch) + } + + if characters.len() > 0 { + Ok(PlainText { value: characters }) + } else { + Err(ParseError::new(self.index)) + } + } + + /// parses Inline surrounded by characters + fn parse_surrounded(&mut self, surrounding: &char) -> Result { + let start_index = self.index; + self.assert_special(surrounding, start_index)?; + self.skip_char(); + let inline = self.parse_inline()?; + self.assert_special(surrounding, start_index)?; + self.skip_char(); + + Ok(inline) + } +} diff --git a/src/parsing/tokens.rs b/src/parsing/tokens.rs index 8df9769..c0ef6d1 100644 --- a/src/parsing/tokens.rs +++ b/src/parsing/tokens.rs @@ -42,6 +42,12 @@ pub(crate) const IMPORT_CLOSE: char = L_BRACKET; pub(crate) const PHOLDER_OPEN: char = R_BRACKET; pub(crate) const PHOLDER_CLOSE: char = L_BRACKET; +pub(crate) const ITALIC: char = ASTERISK; +pub(crate) const MONOSPACE: char = BACKTICK; +pub(crate) const STRIKED: char = TILDE; +pub(crate) const UNDERLINED: char = UNDERSCR; +pub(crate) const BOLD: [char; 2] = [ASTERISK, ASTERISK]; + // groups pub(crate) const QUOTES: [char; 2] = [SINGLE_QUOTE, DOUBLE_QUOTE]; @@ -57,8 +63,9 @@ pub(crate) const BLOCK_SPECIAL_CHARS: [&[char]; 8] = [ &[IMPORT_START, IMPORT_OPEN], ]; -pub(crate) const INLINE_SPECIAL_CHARS: [char; 5] = [LB, ASTERISK, UNDERSCR, TILDE, BACKTICK]; -pub(crate) const INLINE_SPECIAL_CHARS_SECOND: [char; 3] = [DESC_OPEN, IMG_START, URL_OPEN]; +pub(crate) const INLINE_SPECIAL_CHARS: [char; 8] = [ + BACKTICK, TILDE, UNDERSCR, ASTERISK, DESC_OPEN, IMG_START, URL_OPEN, LB, +]; pub(crate) const LIST_SPECIAL_CHARS: [char; 14] = [ MINUS, PLUS, ASTERISK, O, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0',