use crate::ast::tokens::{
Attributes,
span::Span,
token::{Block, Inline},
};
use napi_derive::napi;
#[napi(object)]
#[derive(Debug, Clone)]
pub struct BlockParseContext {
pub line: String,
pub line_index: u32,
pub lines: Vec<String>,
}
#[napi(object)]
#[derive(Debug, Clone)]
pub struct InlineParseContext {
pub rest: String,
pub index: u32,
}
pub trait AstParser {
fn try_parse_block(&self, _ctx: &BlockParseContext) -> Option<(Block, usize)> {
None
}
fn try_parse_inline(&self, _ctx: &InlineParseContext) -> Option<(Inline, usize)> {
None
}
}
#[derive(Clone, Default)]
pub struct ParseHooks<'a> {
parsers: Vec<&'a dyn AstParser>,
}
impl<'a> ParseHooks<'a> {
pub fn empty() -> Self {
Self {
parsers: Vec::new(),
}
}
pub fn push(&mut self, parser: &'a dyn AstParser) {
self.parsers.push(parser);
}
pub fn with(mut self, parser: &'a dyn AstParser) -> Self {
self.parsers.push(parser);
self
}
pub fn is_empty(&self) -> bool {
self.parsers.is_empty()
}
pub fn try_parse_block(&self, ctx: &BlockParseContext) -> Option<(Block, usize)> {
for parser in &self.parsers {
if let Some(result) = parser.try_parse_block(ctx) {
return Some(result);
}
}
None
}
pub fn try_parse_inline(&self, ctx: &InlineParseContext) -> Option<(Inline, usize)> {
for parser in &self.parsers {
if let Some(result) = parser.try_parse_inline(ctx) {
return Some(result);
}
}
None
}
}
pub(crate) fn finalize_hook_block(
block: Block,
pos: Span,
trailing_attrs: Option<Attributes>,
) -> Block {
match block {
Block::Heading {
level,
children,
attrs,
..
} => Block::Heading {
level,
children,
pos,
attrs: trailing_attrs.or(attrs),
},
Block::Paragraph {
children, attrs, ..
} => Block::Paragraph {
children,
pos,
attrs: trailing_attrs.or(attrs),
},
Block::CodeBlock {
lang, code, attrs, ..
} => Block::CodeBlock {
lang,
code,
pos,
attrs: trailing_attrs.or(attrs),
},
Block::BlockQuote {
children, attrs, ..
} => Block::BlockQuote {
children,
pos,
attrs: trailing_attrs.or(attrs),
},
Block::List {
ordered,
items,
attrs,
..
} => Block::List {
ordered,
items,
pos,
attrs: trailing_attrs.or(attrs),
},
Block::ThematicBreak { .. } => Block::ThematicBreak { pos },
Block::HtmlBlock { html, attrs, .. } => Block::HtmlBlock {
html,
pos,
attrs: trailing_attrs.or(attrs),
},
Block::Table { table, attrs, .. } => Block::Table {
table,
pos,
attrs: trailing_attrs.or(attrs),
},
Block::LinkReferenceDefinition {
label, url, title, ..
} => Block::LinkReferenceDefinition {
label,
url,
title,
pos,
},
Block::Comment { value, .. } => Block::Comment { value, pos },
}
}
pub(crate) fn finalize_hook_inline(node: Inline, pos: Span) -> Inline {
match node {
Inline::Text { value, .. } => Inline::Text { value, pos },
Inline::Emphasis {
level, children, ..
} => Inline::Emphasis {
level,
children,
pos,
},
Inline::Code { code, .. } => Inline::Code { code, pos },
Inline::HtmlInline { html, .. } => Inline::HtmlInline { html, pos },
Inline::Strikethrough { children, .. } => Inline::Strikethrough { children, pos },
Inline::HardBreak { .. } => Inline::HardBreak { pos },
Inline::SoftBreak { .. } => Inline::SoftBreak { pos },
Inline::Image {
alt, url, title, ..
} => Inline::Image {
alt,
url,
title,
pos,
},
Inline::Link {
text,
url,
title,
autolink,
..
} => Inline::Link {
text,
url,
title,
autolink,
pos,
},
Inline::LinkReference {
text,
label,
url,
title,
..
} => Inline::LinkReference {
text,
label,
url,
title,
pos,
},
}
}