feat: block parsing
This commit is contained in:
parent
55e6d67fae
commit
79477b812e
|
@ -16,7 +16,7 @@
|
||||||
## Elements
|
## Elements
|
||||||
|
|
||||||
- [ ] Babel Call
|
- [ ] Babel Call
|
||||||
- [ ] Blocks
|
- [x] Blocks
|
||||||
- [ ] Clock, Diary Sexp and Planning
|
- [ ] Clock, Diary Sexp and Planning
|
||||||
- [x] Comments
|
- [x] Comments
|
||||||
- [ ] Fixed Width Areas
|
- [ ] Fixed Width Areas
|
||||||
|
|
|
@ -1,14 +1,24 @@
|
||||||
pub enum BlockStart {
|
#[cfg_attr(test, derive(PartialEq, Debug))]
|
||||||
name: BlockName,
|
pub struct Block;
|
||||||
|
|
||||||
|
impl Block {
|
||||||
|
pub fn parse(src: &str) -> Option<(usize, usize, usize, usize)> {
|
||||||
|
if src.len() < 17 || !src[0..8].eq_ignore_ascii_case("#+BEGIN_") {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let args = eol!(src);
|
||||||
|
let name = until_while!(src, 8, |c| c == b' ' || c == b'\n', |c: u8| c
|
||||||
|
.is_ascii_alphabetic());
|
||||||
|
// TODO: ignore case match
|
||||||
|
let content = src.find(&format!("\n#+END_{}", &src[8..name]))?;
|
||||||
|
let end = eol!(src, content + 1);
|
||||||
|
|
||||||
|
Some((name, args, content, end + 1))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum BlockName {
|
#[test]
|
||||||
Center,
|
fn parse() {
|
||||||
Comment,
|
// TODO: testing
|
||||||
Example,
|
|
||||||
Export,
|
|
||||||
Quote,
|
|
||||||
Src,
|
|
||||||
Verbose,
|
|
||||||
Special
|
|
||||||
}
|
}
|
||||||
|
|
0
src/elements/dyn_block.rs
Normal file
0
src/elements/dyn_block.rs
Normal file
|
@ -1,7 +1,9 @@
|
||||||
|
pub mod block;
|
||||||
pub mod fn_def;
|
pub mod fn_def;
|
||||||
pub mod keyword;
|
pub mod keyword;
|
||||||
pub mod rule;
|
pub mod rule;
|
||||||
|
|
||||||
|
pub use self::block::Block;
|
||||||
pub use self::fn_def::FnDef;
|
pub use self::fn_def::FnDef;
|
||||||
pub use self::keyword::Keyword;
|
pub use self::keyword::Keyword;
|
||||||
pub use self::rule::Rule;
|
pub use self::rule::Rule;
|
||||||
|
@ -17,6 +19,42 @@ pub enum Element<'a> {
|
||||||
Keyword(Keyword<'a>),
|
Keyword(Keyword<'a>),
|
||||||
FnDef(FnDef<'a>),
|
FnDef(FnDef<'a>),
|
||||||
|
|
||||||
|
CenterBlock {
|
||||||
|
args: Option<&'a str>,
|
||||||
|
content_end: usize,
|
||||||
|
end: usize,
|
||||||
|
},
|
||||||
|
QuoteBlock {
|
||||||
|
args: Option<&'a str>,
|
||||||
|
content_end: usize,
|
||||||
|
end: usize,
|
||||||
|
},
|
||||||
|
SpecialBlock {
|
||||||
|
args: Option<&'a str>,
|
||||||
|
name: &'a str,
|
||||||
|
content_end: usize,
|
||||||
|
end: usize,
|
||||||
|
},
|
||||||
|
CommentBlock {
|
||||||
|
content: &'a str,
|
||||||
|
args: Option<&'a str>,
|
||||||
|
},
|
||||||
|
ExampleBlock {
|
||||||
|
content: &'a str,
|
||||||
|
args: Option<&'a str>,
|
||||||
|
},
|
||||||
|
ExportBlock {
|
||||||
|
content: &'a str,
|
||||||
|
args: Option<&'a str>,
|
||||||
|
},
|
||||||
|
SrcBlock {
|
||||||
|
content: &'a str,
|
||||||
|
args: Option<&'a str>,
|
||||||
|
},
|
||||||
|
VerseBlock {
|
||||||
|
content: &'a str,
|
||||||
|
args: Option<&'a str>,
|
||||||
|
},
|
||||||
Rule,
|
Rule,
|
||||||
Comment(&'a str),
|
Comment(&'a str),
|
||||||
}
|
}
|
||||||
|
@ -105,8 +143,305 @@ impl<'a> Element<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if bytes[pos] == b'#' {
|
if bytes[pos] == b'#' {
|
||||||
// Keyword
|
|
||||||
if bytes[pos + 1] == b'+' {
|
if bytes[pos + 1] == b'+' {
|
||||||
|
if let Some((name, args, content, end)) = Block::parse(&src[pos..]) {
|
||||||
|
// TODO: use macros
|
||||||
|
return match &src[pos + 8..pos + name] {
|
||||||
|
block_name if block_name.eq_ignore_ascii_case("CENTER") => {
|
||||||
|
if pos == start {
|
||||||
|
(
|
||||||
|
pos + args,
|
||||||
|
Some(Element::CenterBlock {
|
||||||
|
args: if name == args {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(&src[pos + name..pos + args])
|
||||||
|
},
|
||||||
|
content_end: content + 1,
|
||||||
|
end,
|
||||||
|
}),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(
|
||||||
|
start,
|
||||||
|
Some(Element::Paragraph {
|
||||||
|
end: if pos == start { end } else { end - 1 },
|
||||||
|
trailing: pos - 1,
|
||||||
|
}),
|
||||||
|
Some((
|
||||||
|
Element::CenterBlock {
|
||||||
|
args: if name == args {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(&src[pos + name..pos + args])
|
||||||
|
},
|
||||||
|
content_end: content + 1,
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
pos + args,
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
block_name if block_name.eq_ignore_ascii_case("QUOTE") => {
|
||||||
|
if pos == start {
|
||||||
|
(
|
||||||
|
pos + args,
|
||||||
|
Some(Element::QuoteBlock {
|
||||||
|
args: if name == args {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(&src[pos + name..pos + args].trim())
|
||||||
|
},
|
||||||
|
content_end: content + 1,
|
||||||
|
end,
|
||||||
|
}),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(
|
||||||
|
start,
|
||||||
|
Some(Element::Paragraph {
|
||||||
|
end: if pos == start { end } else { end - 1 },
|
||||||
|
trailing: pos - 1,
|
||||||
|
}),
|
||||||
|
Some((
|
||||||
|
Element::QuoteBlock {
|
||||||
|
args: if name == args {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(
|
||||||
|
&src[pos + name..pos + args].trim(),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
content_end: content + 1,
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
args + pos,
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
block_name if block_name.eq_ignore_ascii_case("COMMENT") => {
|
||||||
|
if pos == start {
|
||||||
|
(
|
||||||
|
args,
|
||||||
|
Some(Element::CommentBlock {
|
||||||
|
args: if name == args {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(&src[pos + name..pos + args])
|
||||||
|
},
|
||||||
|
content: &src[pos + args..pos + content],
|
||||||
|
}),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(
|
||||||
|
start,
|
||||||
|
Some(Element::Paragraph {
|
||||||
|
end: if pos == start { end } else { end - 1 },
|
||||||
|
trailing: pos - 1,
|
||||||
|
}),
|
||||||
|
Some((
|
||||||
|
Element::CommentBlock {
|
||||||
|
args: if name == args {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(&src[pos + name..pos + args])
|
||||||
|
},
|
||||||
|
content: &src[pos + args..pos + content],
|
||||||
|
},
|
||||||
|
args,
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
block_name if block_name.eq_ignore_ascii_case("EXAMPLE") => {
|
||||||
|
if pos == start {
|
||||||
|
(
|
||||||
|
args,
|
||||||
|
Some(Element::ExampleBlock {
|
||||||
|
args: if name == args {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(&src[pos + name..pos + args])
|
||||||
|
},
|
||||||
|
content: &src[pos + args..pos + content],
|
||||||
|
}),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(
|
||||||
|
start,
|
||||||
|
Some(Element::Paragraph {
|
||||||
|
end: if pos == start { end } else { end - 1 },
|
||||||
|
trailing: pos - 1,
|
||||||
|
}),
|
||||||
|
Some((
|
||||||
|
Element::ExampleBlock {
|
||||||
|
args: if name == args {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(&src[pos + name..pos + args])
|
||||||
|
},
|
||||||
|
content: &src[pos + args..pos + content],
|
||||||
|
},
|
||||||
|
args,
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
block_name if block_name.eq_ignore_ascii_case("EXPORT") => {
|
||||||
|
if pos == start {
|
||||||
|
(
|
||||||
|
args,
|
||||||
|
Some(Element::ExportBlock {
|
||||||
|
args: if name == args {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(&src[pos + name..pos + args])
|
||||||
|
},
|
||||||
|
content: &src[pos + args..pos + content],
|
||||||
|
}),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(
|
||||||
|
start,
|
||||||
|
Some(Element::Paragraph {
|
||||||
|
end: if pos == start { end } else { end - 1 },
|
||||||
|
trailing: pos - 1,
|
||||||
|
}),
|
||||||
|
Some((
|
||||||
|
Element::ExportBlock {
|
||||||
|
args: if name == args {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(&src[pos + name..pos + args])
|
||||||
|
},
|
||||||
|
content: &src[pos + args..pos + content],
|
||||||
|
},
|
||||||
|
args,
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
block_name if block_name.eq_ignore_ascii_case("SRC") => {
|
||||||
|
if pos == start {
|
||||||
|
(
|
||||||
|
args,
|
||||||
|
Some(Element::SrcBlock {
|
||||||
|
args: if name == args {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(&src[pos + name..pos + args])
|
||||||
|
},
|
||||||
|
content: &src[pos + args..pos + content],
|
||||||
|
}),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(
|
||||||
|
start,
|
||||||
|
Some(Element::Paragraph {
|
||||||
|
end: if pos == start { end } else { end - 1 },
|
||||||
|
trailing: pos - 1,
|
||||||
|
}),
|
||||||
|
Some((
|
||||||
|
Element::SrcBlock {
|
||||||
|
args: if name == args {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(&src[pos + name..pos + args])
|
||||||
|
},
|
||||||
|
content: &src[pos + args..pos + content],
|
||||||
|
},
|
||||||
|
args,
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
block_name if block_name.eq_ignore_ascii_case("VERSE") => {
|
||||||
|
if pos == start {
|
||||||
|
(
|
||||||
|
args,
|
||||||
|
Some(Element::VerseBlock {
|
||||||
|
args: if name == args {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(&src[pos + name..pos + args])
|
||||||
|
},
|
||||||
|
content: &src[pos + args..pos + content],
|
||||||
|
}),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(
|
||||||
|
start,
|
||||||
|
Some(Element::Paragraph {
|
||||||
|
end: if pos == start { end } else { end - 1 },
|
||||||
|
trailing: pos - 1,
|
||||||
|
}),
|
||||||
|
Some((
|
||||||
|
Element::VerseBlock {
|
||||||
|
args: if name == args {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(&src[pos + name..pos + args])
|
||||||
|
},
|
||||||
|
content: &src[pos + args..pos + content],
|
||||||
|
},
|
||||||
|
args,
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
block_name => {
|
||||||
|
if pos == start {
|
||||||
|
(
|
||||||
|
pos + args,
|
||||||
|
Some(Element::SpecialBlock {
|
||||||
|
name: block_name,
|
||||||
|
args: if name == args {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(&src[pos + name..pos + args].trim())
|
||||||
|
},
|
||||||
|
content_end: content + 1,
|
||||||
|
end,
|
||||||
|
}),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(
|
||||||
|
start,
|
||||||
|
Some(Element::Paragraph {
|
||||||
|
end: if pos == start { end } else { end - 1 },
|
||||||
|
trailing: pos - 1,
|
||||||
|
}),
|
||||||
|
Some((
|
||||||
|
Element::SpecialBlock {
|
||||||
|
name: block_name,
|
||||||
|
args: if name == args {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(
|
||||||
|
&src[pos + name..pos + args].trim(),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
content_end: content + 1,
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
pos + args,
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
if let Some((kw, off)) = Keyword::parse(&src[pos..]) {
|
if let Some((kw, off)) = Keyword::parse(&src[pos..]) {
|
||||||
return if pos == start {
|
return if pos == start {
|
||||||
(off, Some(Element::Keyword(kw)), None)
|
(off, Some(Element::Keyword(kw)), None)
|
||||||
|
|
|
@ -9,8 +9,10 @@ pub enum Container {
|
||||||
Section { end: usize },
|
Section { end: usize },
|
||||||
|
|
||||||
Paragraph { end: usize, trailing: usize },
|
Paragraph { end: usize, trailing: usize },
|
||||||
|
CenterBlock { content_end: usize, end: usize },
|
||||||
|
QuoteBlock { content_end: usize, end: usize },
|
||||||
|
SpecialBlock { content_end: usize, end: usize },
|
||||||
|
|
||||||
Block,
|
|
||||||
Drawer,
|
Drawer,
|
||||||
LatexEnv,
|
LatexEnv,
|
||||||
List,
|
List,
|
||||||
|
@ -33,8 +35,37 @@ pub enum Event<'a> {
|
||||||
StartParagraph,
|
StartParagraph,
|
||||||
EndParagraph,
|
EndParagraph,
|
||||||
|
|
||||||
BlockStart,
|
StartCenterBlock,
|
||||||
BlockEnd,
|
EndCenterBlock,
|
||||||
|
StartQuoteBlock,
|
||||||
|
EndQuoteBlock,
|
||||||
|
StartSpecialBlock {
|
||||||
|
name: &'a str,
|
||||||
|
args: Option<&'a str>,
|
||||||
|
},
|
||||||
|
EndSpecialBlock,
|
||||||
|
|
||||||
|
CommentBlock {
|
||||||
|
content: &'a str,
|
||||||
|
args: Option<&'a str>,
|
||||||
|
},
|
||||||
|
ExampleBlock {
|
||||||
|
content: &'a str,
|
||||||
|
args: Option<&'a str>,
|
||||||
|
},
|
||||||
|
ExportBlock {
|
||||||
|
content: &'a str,
|
||||||
|
args: Option<&'a str>,
|
||||||
|
},
|
||||||
|
SrcBlock {
|
||||||
|
content: &'a str,
|
||||||
|
args: Option<&'a str>,
|
||||||
|
},
|
||||||
|
VerseBlock {
|
||||||
|
content: &'a str,
|
||||||
|
args: Option<&'a str>,
|
||||||
|
},
|
||||||
|
|
||||||
DynBlockStart,
|
DynBlockStart,
|
||||||
DynBlockEnd,
|
DynBlockEnd,
|
||||||
ListStart,
|
ListStart,
|
||||||
|
@ -136,11 +167,30 @@ impl<'a> Parser<'a> {
|
||||||
self.off += off;
|
self.off += off;
|
||||||
|
|
||||||
if let Some(ele) = ele {
|
if let Some(ele) = ele {
|
||||||
if let Element::Paragraph { end, trailing } = ele {
|
match ele {
|
||||||
self.stack.push(Container::Paragraph {
|
Element::Paragraph { end, trailing } => self.stack.push(Container::Paragraph {
|
||||||
end: end + self.off - off,
|
end: end + self.off - off,
|
||||||
trailing: trailing + self.off - off,
|
trailing: trailing + self.off - off,
|
||||||
});
|
}),
|
||||||
|
Element::QuoteBlock {
|
||||||
|
end, content_end, ..
|
||||||
|
} => self.stack.push(Container::QuoteBlock {
|
||||||
|
content_end: content_end + self.off - off,
|
||||||
|
end: end + self.off - off,
|
||||||
|
}),
|
||||||
|
Element::CenterBlock {
|
||||||
|
end, content_end, ..
|
||||||
|
} => self.stack.push(Container::CenterBlock {
|
||||||
|
content_end: content_end + self.off - off,
|
||||||
|
end: end + self.off - off,
|
||||||
|
}),
|
||||||
|
Element::SpecialBlock {
|
||||||
|
end, content_end, ..
|
||||||
|
} => self.stack.push(Container::SpecialBlock {
|
||||||
|
content_end: content_end + self.off - off,
|
||||||
|
end: end + self.off - off,
|
||||||
|
}),
|
||||||
|
_ => (),
|
||||||
}
|
}
|
||||||
ele.into()
|
ele.into()
|
||||||
} else {
|
} else {
|
||||||
|
@ -187,6 +237,9 @@ impl<'a> Parser<'a> {
|
||||||
Container::Headline { .. } => Event::EndHeadline,
|
Container::Headline { .. } => Event::EndHeadline,
|
||||||
Container::Italic { .. } => Event::EndItalic,
|
Container::Italic { .. } => Event::EndItalic,
|
||||||
Container::Bold { .. } => Event::EndBold,
|
Container::Bold { .. } => Event::EndBold,
|
||||||
|
Container::CenterBlock { .. } => Event::EndCenterBlock,
|
||||||
|
Container::QuoteBlock { .. } => Event::EndQuoteBlock,
|
||||||
|
Container::SpecialBlock { .. } => Event::EndSpecialBlock,
|
||||||
_ => unimplemented!(),
|
_ => unimplemented!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -205,7 +258,7 @@ impl<'a> Iterator for Parser<'a> {
|
||||||
Some(self.start_section_or_headline(tail))
|
Some(self.start_section_or_headline(tail))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let last = *self.stack.last_mut()?;
|
let last = *self.stack.last_mut().unwrap();
|
||||||
|
|
||||||
Some(match last {
|
Some(match last {
|
||||||
Container::Headline { beg, end } => {
|
Container::Headline { beg, end } => {
|
||||||
|
@ -217,6 +270,22 @@ impl<'a> Iterator for Parser<'a> {
|
||||||
self.start_headline(tail)
|
self.start_headline(tail)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Container::CenterBlock {
|
||||||
|
content_end, end, ..
|
||||||
|
}
|
||||||
|
| Container::QuoteBlock {
|
||||||
|
content_end, end, ..
|
||||||
|
}
|
||||||
|
| Container::SpecialBlock {
|
||||||
|
content_end, end, ..
|
||||||
|
} => {
|
||||||
|
if self.off >= content_end {
|
||||||
|
self.off = end;
|
||||||
|
self.end()
|
||||||
|
} else {
|
||||||
|
self.next_ele(content_end)
|
||||||
|
}
|
||||||
|
}
|
||||||
Container::Section { end } => {
|
Container::Section { end } => {
|
||||||
if self.off >= end {
|
if self.off >= end {
|
||||||
self.end()
|
self.end()
|
||||||
|
@ -280,6 +349,14 @@ impl<'a> From<Element<'a>> for Event<'a> {
|
||||||
Element::Keyword(kw) => Event::Keyword(kw),
|
Element::Keyword(kw) => Event::Keyword(kw),
|
||||||
Element::Paragraph { .. } => Event::StartParagraph,
|
Element::Paragraph { .. } => Event::StartParagraph,
|
||||||
Element::Rule => Event::Rule,
|
Element::Rule => Event::Rule,
|
||||||
|
Element::CenterBlock { .. } => Event::StartCenterBlock,
|
||||||
|
Element::QuoteBlock { .. } => Event::StartQuoteBlock,
|
||||||
|
Element::SpecialBlock { name, args, .. } => Event::StartSpecialBlock { name, args },
|
||||||
|
Element::CommentBlock { args, content } => Event::CommentBlock { args, content },
|
||||||
|
Element::ExampleBlock { args, content } => Event::ExampleBlock { args, content },
|
||||||
|
Element::ExportBlock { args, content } => Event::ExportBlock { args, content },
|
||||||
|
Element::SrcBlock { args, content } => Event::SrcBlock { args, content },
|
||||||
|
Element::VerseBlock { args, content } => Event::VerseBlock { args, content },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue