use crate::markdown::ast::tokens::{
Attributes,
span::Span,
token::{Block, Inline},
};
#[cfg(feature = "npm")]
use napi_derive::napi;
pub trait PluginFactory {
fn try_parse_block(&self, _ctx: &BlockParserContext) -> Option<(Block, usize)> {
None
}
fn try_parse_inline(&self, _ctx: &InlineParserContext) -> Option<(Inline, usize)> {
None
}
fn visit_block(&mut self, _block: &mut Block) -> VisitControl {
VisitControl::default()
}
fn visit_inline(&mut self, _inline: &mut Inline) -> InlineVisitControl {
InlineVisitControl::default()
}
fn has_parse_hooks(&self) -> bool {
false
}
}
#[deprecated(since = "1.3.0", note = "Use `PluginFactory` instead")]
pub trait AstVisitor {
fn visit_block(&mut self, _block: &mut Block) -> VisitControl {
VisitControl::default()
}
fn visit_inline(&mut self, _inline: &mut Inline) -> InlineVisitControl {
InlineVisitControl::default()
}
}
#[deprecated(since = "1.3.0", note = "Use `PluginFactory` instead")]
pub trait AstParser {
fn try_parse_block(&self, _ctx: &BlockParserContext) -> Option<(Block, usize)> {
None
}
fn try_parse_inline(&self, _ctx: &InlineParserContext) -> Option<(Inline, usize)> {
None
}
}
#[cfg_attr(feature = "npm", napi(object))]
#[derive(Debug, Clone)]
pub struct BlockParserContext {
pub line: String,
pub line_index: u32,
pub lines: Vec<String>,
}
#[cfg_attr(feature = "npm", napi(object))]
#[derive(Debug, Clone)]
pub struct InlineParserContext {
pub rest: String,
pub index: u32,
}
#[allow(rustdoc::private_intra_doc_links)]
#[deprecated(since = "1.3.0", note = "Use `BlockParserContext` instead")]
pub type BlockParseContext = BlockParserContext;
#[allow(rustdoc::private_intra_doc_links)]
#[deprecated(since = "1.3.0", note = "Use `InlineParserContext` instead")]
pub type InlineParseContext = InlineParserContext;
#[derive(Debug, Default)]
pub struct VisitControl {
pub insert_before: Vec<Block>,
pub insert_after: Vec<Block>,
pub replace_with: Option<Vec<Block>>,
pub remove: bool,
pub recurse: bool,
}
impl VisitControl {
pub fn keep_and_recurse() -> Self {
VisitControl {
recurse: true,
..Default::default()
}
}
pub fn remove() -> Self {
VisitControl {
remove: true,
..Default::default()
}
}
pub fn replace_with(nodes: Vec<Block>) -> Self {
VisitControl {
replace_with: Some(nodes),
..Default::default()
}
}
}
#[derive(Debug, Default)]
pub struct InlineVisitControl {
pub insert_before: Vec<Inline>,
pub insert_after: Vec<Inline>,
pub replace_with: Option<Vec<Inline>>,
pub remove: bool,
pub recurse: bool,
}
impl InlineVisitControl {
pub fn keep_and_recurse() -> Self {
InlineVisitControl {
recurse: true,
..Default::default()
}
}
pub fn remove() -> Self {
InlineVisitControl {
remove: true,
..Default::default()
}
}
pub fn replace_with(nodes: Vec<Inline>) -> Self {
InlineVisitControl {
replace_with: Some(nodes),
..Default::default()
}
}
}
pub fn visit_document_mut<V: PluginFactory + ?Sized>(doc: &mut super::Document, visitor: &mut V) {
visit_blocks_vec(&mut doc.children, visitor)
}
fn visit_blocks_vec<V: PluginFactory + ?Sized>(children: &mut Vec<Block>, visitor: &mut V) {
let mut i = 0usize;
while i < children.len() {
let mut node = children.remove(i);
let ctrl = visitor.visit_block(&mut node);
let VisitControl {
insert_before,
insert_after,
replace_with,
remove,
recurse,
} = ctrl;
if !insert_before.is_empty() {
let before_len = insert_before.len();
children.splice(i..i, insert_before);
i += before_len;
}
if remove {
if !insert_after.is_empty() {
let after_len = insert_after.len();
children.splice(i..i, insert_after);
i += after_len;
}
continue;
}
if let Some(reps) = replace_with {
let replacement_len = reps.len();
children.splice(i..i, reps);
i += replacement_len;
continue;
}
if recurse {
match &mut node {
Block::Heading {
children: inline_children,
..
}
| Block::Paragraph {
children: inline_children,
..
} => {
visit_inlines_vec(inline_children, visitor);
}
Block::BlockQuote {
children: block_children,
..
} => {
visit_blocks_vec(block_children, visitor);
}
Block::List { items, .. } => {
for item in items.iter_mut() {
visit_blocks_vec(&mut item.children, visitor);
}
}
Block::Table { table, .. } => {
visit_table(table, visitor);
}
_ => {}
}
}
children.insert(i, node);
i += 1;
if !insert_after.is_empty() {
let after_len = insert_after.len();
children.splice(i..i, insert_after);
i += after_len;
}
}
}
fn visit_table<V: PluginFactory + ?Sized>(
table: &mut crate::markdown::ast::tokens::token::Table,
visitor: &mut V,
) {
for cell in table.header.cells.iter_mut() {
visit_inlines_vec(&mut cell.children, visitor);
}
for row in table.rows.iter_mut() {
for cell in row.cells.iter_mut() {
visit_inlines_vec(&mut cell.children, visitor);
}
}
}
fn visit_inlines_vec<V: PluginFactory + ?Sized>(inlines: &mut Vec<Inline>, visitor: &mut V) {
let mut i = 0usize;
while i < inlines.len() {
let mut node = inlines.remove(i);
let ctrl = visitor.visit_inline(&mut node);
let InlineVisitControl {
insert_before,
insert_after,
replace_with,
remove,
recurse,
} = ctrl;
if !insert_before.is_empty() {
let before_len = insert_before.len();
inlines.splice(i..i, insert_before);
i += before_len;
}
if remove {
if !insert_after.is_empty() {
let after_len = insert_after.len();
inlines.splice(i..i, insert_after);
i += after_len;
}
continue;
}
#[allow(unused_mut)]
if let Some(reps) = replace_with {
inlines.splice(i..i, reps);
continue;
}
if recurse {
match &mut node {
Inline::Emphasis { children, .. } | Inline::Strikethrough { children, .. } => {
visit_inlines_vec(children, visitor);
}
Inline::Link { text, .. } | Inline::LinkReference { text, .. } => {
visit_inlines_vec(text, visitor);
}
_ => {}
}
}
inlines.insert(i, node);
i += 1;
if !insert_after.is_empty() {
let after_len = insert_after.len();
inlines.splice(i..i, insert_after);
i += after_len;
}
}
}
#[derive(Clone, Default)]
pub struct ParserHooks<'a> {
parsers: Vec<&'a dyn PluginFactory>,
}
impl<'a> ParserHooks<'a> {
pub fn empty() -> Self {
Self {
parsers: Vec::new(),
}
}
pub fn push(&mut self, parser: &'a dyn PluginFactory) {
self.parsers.push(parser);
}
pub fn with(mut self, parser: &'a dyn PluginFactory) -> Self {
self.parsers.push(parser);
self
}
pub fn is_empty(&self) -> bool {
self.parsers.is_empty()
}
pub fn try_parse_block(&self, ctx: &BlockParserContext) -> 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: &InlineParserContext) -> Option<(Inline, usize)> {
for parser in &self.parsers {
if let Some(result) = parser.try_parse_inline(ctx) {
return Some(result);
}
}
None
}
}
#[deprecated(since = "1.3.0", note = "Use `ParserHooks` instead")]
pub type ParseHooks<'a> = ParserHooks<'a>;
#[allow(deprecated)]
#[deprecated(since = "1.3.0", note = "Use `PluginFactory` instead")]
pub struct AstParserAdapter<'a> {
pub(crate) inner: &'a dyn AstParser,
}
#[allow(deprecated)]
impl<'a> AstParserAdapter<'a> {
#[deprecated(since = "1.3.0", note = "implement `PluginFactory` instead")]
pub fn new(parser: &'a dyn AstParser) -> Self {
Self { inner: parser }
}
}
#[allow(deprecated)]
impl PluginFactory for AstParserAdapter<'_> {
fn try_parse_block(&self, ctx: &BlockParserContext) -> Option<(Block, usize)> {
self.inner.try_parse_block(ctx)
}
fn try_parse_inline(&self, ctx: &InlineParserContext) -> Option<(Inline, usize)> {
self.inner.try_parse_inline(ctx)
}
}
#[allow(deprecated)]
#[deprecated(since = "1.3.0", note = "Use `PluginFactory` instead")]
pub struct AstVisitorAdapter<'a> {
pub(crate) inner: &'a mut dyn AstVisitor,
}
#[allow(deprecated)]
impl<'a> AstVisitorAdapter<'a> {
#[deprecated(since = "1.3.0", note = "implement `PluginFactory` instead")]
pub fn new(visitor: &'a mut dyn AstVisitor) -> Self {
Self { inner: visitor }
}
}
#[allow(deprecated)]
impl PluginFactory for AstVisitorAdapter<'_> {
fn visit_block(&mut self, block: &mut Block) -> VisitControl {
self.inner.visit_block(block)
}
fn visit_inline(&mut self, inline: &mut Inline) -> InlineVisitControl {
self.inner.visit_inline(inline)
}
}
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,
},
}
}
#[allow(deprecated)]
#[deprecated(since = "1.3.0", note = "Use `visit_document_mut` instead")]
pub fn visit_document_mut_dep<V: AstVisitor + ?Sized>(doc: &mut super::Document, visitor: &mut V) {
visit_blocks_vec_dep(&mut doc.children, visitor)
}
#[allow(deprecated)]
fn visit_blocks_vec_dep<V: AstVisitor + ?Sized>(children: &mut Vec<Block>, visitor: &mut V) {
let mut i = 0usize;
while i < children.len() {
let mut node = children.remove(i);
let ctrl = visitor.visit_block(&mut node);
let VisitControl {
insert_before,
insert_after,
replace_with,
remove,
recurse,
} = ctrl;
if !insert_before.is_empty() {
let before_len = insert_before.len();
children.splice(i..i, insert_before);
i += before_len;
}
if remove {
if !insert_after.is_empty() {
let after_len = insert_after.len();
children.splice(i..i, insert_after);
i += after_len;
}
continue;
}
if let Some(reps) = replace_with {
let replacement_len = reps.len();
children.splice(i..i, reps);
i += replacement_len;
continue;
}
if recurse {
match &mut node {
Block::Heading {
children: inline_children,
..
}
| Block::Paragraph {
children: inline_children,
..
} => {
visit_inlines_vec_dep(inline_children, visitor);
}
Block::BlockQuote {
children: block_children,
..
} => {
visit_blocks_vec_dep(block_children, visitor);
}
Block::List { items, .. } => {
for item in items.iter_mut() {
visit_blocks_vec_dep(&mut item.children, visitor);
}
}
Block::Table { table, .. } => {
visit_table_dep(table, visitor);
}
_ => {}
}
}
children.insert(i, node);
i += 1;
if !insert_after.is_empty() {
let after_len = insert_after.len();
children.splice(i..i, insert_after);
i += after_len;
}
}
}
#[allow(deprecated)]
fn visit_table_dep<V: AstVisitor + ?Sized>(
table: &mut crate::markdown::ast::tokens::token::Table,
visitor: &mut V,
) {
for cell in table.header.cells.iter_mut() {
visit_inlines_vec_dep(&mut cell.children, visitor);
}
for row in table.rows.iter_mut() {
for cell in row.cells.iter_mut() {
visit_inlines_vec_dep(&mut cell.children, visitor);
}
}
}
#[allow(deprecated)]
fn visit_inlines_vec_dep<V: AstVisitor + ?Sized>(inlines: &mut Vec<Inline>, visitor: &mut V) {
let mut i = 0usize;
while i < inlines.len() {
let mut node = inlines.remove(i);
let ctrl = visitor.visit_inline(&mut node);
let InlineVisitControl {
insert_before,
insert_after,
replace_with,
remove,
recurse,
} = ctrl;
if !insert_before.is_empty() {
let before_len = insert_before.len();
inlines.splice(i..i, insert_before);
i += before_len;
}
if remove {
if !insert_after.is_empty() {
let after_len = insert_after.len();
inlines.splice(i..i, insert_after);
i += after_len;
}
continue;
}
#[allow(unused_mut)]
if let Some(reps) = replace_with {
inlines.splice(i..i, reps);
continue;
}
if recurse {
match &mut node {
Inline::Emphasis { children, .. } | Inline::Strikethrough { children, .. } => {
visit_inlines_vec_dep(children, visitor);
}
Inline::Link { text, .. } | Inline::LinkReference { text, .. } => {
visit_inlines_vec_dep(text, visitor);
}
_ => {}
}
}
inlines.insert(i, node);
i += 1;
if !insert_after.is_empty() {
let after_len = insert_after.len();
inlines.splice(i..i, insert_after);
i += after_len;
}
}
}