use crate::nodes::Node;
use crate::parsers::common::*;
use crate::parsers::tags::tags;
use crate::plugins::PluginDispatch;
use crate::state::SharedState;
use glue::prelude::*;
use glue::combinators::structures::*;
pub fn inline<'a>(state: SharedState) -> impl Parser<'a, Vec<Node>> {
move |ctx| {
map_result(
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()),
)),
),
)),
),
|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
},
).parse(ctx)
}
}
pub fn tagged<'a, C>(state: SharedState, mut callback: C) -> impl Parser<'a, Node>
where
C: Parser<'a, Node>,
{
move |ctx| match callback.parse(ctx)? {
(ctx, result) => match is('[').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> {
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> {
move |ctx| {
map_result(
delimited(is('*'), inline(state.clone()), is('*')),
|words| {
state.borrow_mut().plugins.on_node_parsed(Node::Element {
name: "em".into(),
attributes: None,
tags: None,
children: Some(words),
})
},
).parse(ctx)
}
}
pub fn strong_emphasis<'a>(state: SharedState) -> impl Parser<'a, Node> {
move |ctx| {
map_result(
delimited(is("**"), inline(state.clone()), is("**")),
|words| {
state.borrow_mut().plugins.on_node_parsed(Node::Element {
name: "strong".into(),
attributes: None,
tags: None,
children: Some(words),
})
},
).parse(ctx)
}
}
#[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> {
move |ctx: ParserContext<'a>| match find_when(is('`'), take(1.., is('`'))).parse(ctx.clone())? {
(_, delimiter) => map_result(
delimited(is(delimiter), take(1.., isnt(delimiter)), is(delimiter)),
|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(),
},
]),
})
},
).parse(ctx),
}
}
#[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> {
move |ctx| {
map_result(
find_when(is('"'), delimited(is('"'), inline(state.clone()), is('"'))),
|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)
},
})
},
).parse(ctx)
}
}
#[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> {
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> {
move |ctx| {
map_result(take(1.., is(word_character)), |word| {
state
.borrow_mut()
.plugins
.on_node_parsed(Node::Word { word: word.into() })
}).parse(ctx)
}
}
#[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();
}