use crate::nodes::Node;
use crate::parsers::common::*;
use crate::parsers::tags::{tag_start_delimiter, tags};
use crate::plugins::PluginDispatch;
use crate::state::SharedState;
use chonk::prelude::*;
pub fn inline<'a>(state: SharedState) -> impl Parser<'a, Vec<Node>, Message<'a>> {
move |ctx| {
find(
1..,
find_all((
take_all((
optional(find_when_not(take(2.., is('\n')), is('\n'))),
indent(0..),
)),
tagged(
state.clone(),
find_any((
emphasis(state.clone()),
quoted(state.clone()),
preformatted(state.clone()),
span(state.clone()),
word(state.clone()),
)),
),
)),
)
.parse(ctx)
.map_result(|children| {
let mut items = Vec::new();
for child in children {
if child.0.len() > 0 {
items.push(Node::Whitespace {
space: child.0.into(),
});
}
items.push(child.1);
}
items
})
}
}
pub fn tagged<'a, C>(state: SharedState, mut callback: C) -> impl Parser<'a, Node, Message<'a>>
where
C: Parser<'a, Node, Message<'a>>,
{
move |ctx| match callback.parse(ctx)? {
(ctx, result) => match tag_start_delimiter().parse(ctx.clone()) {
Ok((_, _)) => match tags(state.clone()).parse(ctx)? {
(ctx, tags) => Ok((
ctx,
state
.borrow_mut()
.plugins
.on_node_tagged(result.with_tags(tags)),
)),
},
Err((ctx, _)) => Ok((ctx, result)),
},
}
}
pub fn emphasis<'a>(state: SharedState) -> impl Parser<'a, Node, Message<'a>> {
move |ctx| {
find_when(
is('*'),
find_any((
strong_emphasis(state.clone()),
normal_emphasis(state.clone()),
)),
)
.parse(ctx)
}
}
pub fn normal_emphasis<'a>(state: SharedState) -> impl Parser<'a, Node, Message<'a>> {
move |ctx| {
delimited(is('*'), inline(state.clone()), is('*'))
.parse(ctx)
.map_result(|words| {
state.borrow_mut().plugins.on_node_parsed(Node::Element {
name: "em".into(),
attributes: None,
tags: None,
children: Some(words),
})
})
}
}
pub fn strong_emphasis<'a>(state: SharedState) -> impl Parser<'a, Node, Message<'a>> {
move |ctx| {
delimited(is("**"), inline(state.clone()), is("**"))
.parse(ctx)
.map_result(|words| {
state.borrow_mut().plugins.on_node_parsed(Node::Element {
name: "strong".into(),
attributes: None,
tags: None,
children: Some(words),
})
})
}
}
#[test]
fn emphasis_parses() {
use crate::state::State;
let state = State::shareable();
tagged(state.clone(), emphasis(state.clone()))
.parse("**foo**[neat]")
.map_result(|result| {
assert_eq!(
result,
Node::Element {
name: "strong".into(),
attributes: None,
tags: Some(vec!["neat".into()]),
children: Some(vec![Node::Word { word: "foo".into() }])
}
)
})
.unwrap();
tagged(state.clone(), emphasis(state.clone()))
.parse("*foo*[neat]")
.map_result(|result| {
assert_eq!(
result,
Node::Element {
name: "em".into(),
attributes: None,
tags: Some(vec!["neat".into()]),
children: Some(vec![Node::Word { word: "foo".into() }])
}
)
})
.unwrap();
}
pub fn preformatted<'a>(state: SharedState) -> impl Parser<'a, Node, Message<'a>> {
move |ctx: ParserContext<'a>| match find_when(is('`'), take(1.., is('`'))).parse(ctx.clone())? {
(_, delimiter) => delimited(is(delimiter), take(1.., isnt(delimiter)), is(delimiter))
.parse(ctx)
.map_result(|preformatted| {
state.borrow_mut().plugins.on_node_parsed(Node::Element {
name: "code".into(),
attributes: None,
tags: None,
children: Some(vec![Node::Text {
text: preformatted.into(),
}]),
})
}),
}
}
#[test]
fn preformatted_parses() {
use crate::state::State;
let state = State::shareable();
tagged(state.clone(), preformatted(state.clone()))
.parse("`foo bar`[neat]")
.map_result(|result| {
assert_eq!(
result,
Node::Element {
name: "code".into(),
attributes: None,
tags: Some(vec!["neat".into()]),
children: Some(vec![Node::Text {
text: "foo bar".into(),
},])
}
)
})
.unwrap();
}
pub fn quoted<'a>(state: SharedState) -> impl Parser<'a, Node, Message<'a>> {
move |ctx| {
find_when(is('"'), delimited(is('"'), inline(state.clone()), is('"')))
.parse(ctx)
.map_result(|children| {
state.borrow_mut().plugins.on_node_parsed(Node::Element {
name: "q".into(),
attributes: None,
tags: None,
children: if children.is_empty() {
None
} else {
Some(children)
},
})
})
}
}
#[test]
fn quoted_parses() {
use crate::state::State;
let state = State::shareable();
tagged(state.clone(), quoted(state.clone()))
.parse("\"foo bar\"[neat]")
.map_result(|result| {
assert_eq!(
result,
Node::Element {
name: "q".into(),
attributes: None,
tags: Some(vec!["neat".into()]),
children: Some(vec![
Node::Word { word: "foo".into() },
Node::Whitespace { space: " ".into() },
Node::Word { word: "bar".into() },
]),
}
)
})
.unwrap();
}
pub fn span<'a>(state: SharedState) -> impl Parser<'a, Node, Message<'a>> {
move |ctx| {
find_when(is('['), delimited(is('['), inline(state.clone()), is(']')))
.parse(ctx)
.map(|(ctx, children)| {
(
ctx,
state.borrow_mut().plugins.on_node_parsed(Node::Span {
attributes: None,
tags: None,
children: if children.is_empty() {
None
} else {
Some(children)
},
}),
)
})
}
}
#[test]
fn span_parses() {
use crate::state::State;
let state = State::shareable();
tagged(state.clone(), span(state.clone()))
.parse("[foo bar][neat]")
.map_result(|result| {
assert_eq!(
result,
Node::Span {
attributes: None,
tags: Some(vec!["neat".into()]),
children: Some(vec![
Node::Word { word: "foo".into() },
Node::Whitespace { space: " ".into() },
Node::Word { word: "bar".into() },
])
}
)
})
.unwrap();
}
pub fn word_character(character: char) -> bool {
!whitespace(character)
&& character != '['
&& character != ']'
&& character != '*'
&& character != '`'
&& character != '"'
&& character != '#'
}
pub fn word<'a>(state: SharedState) -> impl Parser<'a, Node, Message<'a>> {
move |ctx| {
take(1.., is(word_character)).parse(ctx).map_result(|word| {
state
.borrow_mut()
.plugins
.on_node_parsed(Node::Word { word: word.into() })
})
}
}
#[test]
fn word_parses() {
use crate::state::State;
let state = State::shareable();
word(state.clone())
.parse("foo")
.map_result(|result| assert_eq!(result, Node::Word { word: "foo".into() }))
.unwrap();
tagged(state.clone(), word(state.clone()))
.parse("foo[neat]")
.map_result(|result| {
assert_eq!(
result,
Node::Span {
attributes: None,
tags: Some(vec!["neat".into()]),
children: Some(vec![Node::Word { word: "foo".into() }]),
}
)
})
.unwrap();
}