use super::shared::{opt_span_range, GrammarSpan};
use crate::grammar::inlines as grammar;
use crate::parser::ast::{Node, NodeKind};
use nom::IResult;
pub fn parse_emphasis(input: GrammarSpan) -> IResult<GrammarSpan, Node> {
let start = input;
let (rest, content) = grammar::emphasis(input)?;
let span = opt_span_range(start, rest);
let children = match crate::parser::inlines::parse_inlines_from_span(content) {
Ok(children) => children,
Err(e) => {
log::warn!("Failed to parse emphasis children: {}", e);
vec![]
}
};
let node = Node {
kind: NodeKind::Emphasis,
span,
children,
};
Ok((rest, node))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn smoke_test_parse_emphasis_asterisk() {
let input = GrammarSpan::new("*hello*");
let result = parse_emphasis(input);
assert!(result.is_ok(), "Failed to parse emphasis");
let (rest, node) = result.unwrap();
assert_eq!(rest.fragment(), &"");
assert!(matches!(node.kind, NodeKind::Emphasis));
assert_eq!(node.children.len(), 1); }
#[test]
fn smoke_test_parse_emphasis_underscore() {
let input = GrammarSpan::new("_hello_");
let result = parse_emphasis(input);
assert!(result.is_ok());
let (_, node) = result.unwrap();
assert!(matches!(node.kind, NodeKind::Emphasis));
assert!(!node.children.is_empty());
}
#[test]
fn smoke_test_parse_emphasis_with_nested_code() {
let input = GrammarSpan::new("*text with `code`*");
let result = parse_emphasis(input);
assert!(result.is_ok());
let (_, node) = result.unwrap();
assert!(matches!(node.kind, NodeKind::Emphasis));
assert!(node.children.len() >= 2);
}
#[test]
fn smoke_test_parse_emphasis_not_emphasis() {
let input = GrammarSpan::new("just text");
let result = parse_emphasis(input);
assert!(result.is_err(), "Should not parse non-emphasis as emphasis");
}
#[test]
fn smoke_test_parse_emphasis_unclosed() {
let input = GrammarSpan::new("*unclosed");
let result = parse_emphasis(input);
assert!(result.is_err(), "Should not parse unclosed emphasis");
}
#[test]
fn smoke_test_parse_emphasis_empty() {
let input = GrammarSpan::new("**");
let result = parse_emphasis(input);
let _ = result;
}
#[test]
fn test_parse_emphasis_utf8_and_emoji_positions() {
let input = GrammarSpan::new("*Tëst*");
let result = parse_emphasis(input);
assert!(result.is_ok());
let (rest, node) = result.unwrap();
assert_eq!(*rest.fragment(), "");
assert!(node.span.is_some());
let span = node.span.unwrap();
assert_eq!(span.start.line, 1);
assert_eq!(span.start.column, 1);
assert!(span.end.offset > span.start.offset);
assert!(span.end.column > span.start.column);
let input2 = GrammarSpan::new("*😊*");
let result2 = parse_emphasis(input2);
assert!(result2.is_ok());
let (_, node2) = result2.unwrap();
assert!(node2.span.is_some());
let span2 = node2.span.unwrap();
assert_eq!(span2.start.line, 1);
assert_eq!(span2.start.column, 1);
assert!(span2.end.offset > span2.start.offset);
assert!(span2.end.column > span2.start.column);
}
#[test]
fn smoke_test_parse_emphasis_position() {
let input = GrammarSpan::new("*hello* world");
let result = parse_emphasis(input);
assert!(result.is_ok());
let (rest, node) = result.unwrap();
assert_eq!(rest.fragment(), &" world");
assert!(node.span.is_some(), "Emphasis should have position info");
let span = node.span.unwrap();
assert_eq!(span.start.offset, 0);
assert!(span.end.offset > span.start.offset);
}
}