marco_core/intelligence/markdown/
inlines.rs1use super::ast::{is_inline_kind, Node, NodeKind};
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
8pub enum InlineCategory {
10 Text,
12 TaskCheckboxInline,
14 Emphasis,
16 Strong,
18 StrongEmphasis,
20 Strikethrough,
22 Mark,
24 Superscript,
26 Subscript,
28 Link,
30 LinkReference,
32 FootnoteReference,
34 Image,
36 CodeSpan,
38 InlineHtml,
40 HardBreak,
42 SoftBreak,
44 PlatformMention,
46 InlineMath,
48 DisplayMath,
50}
51
52pub fn classify_inline_kind(kind: &NodeKind) -> Option<InlineCategory> {
54 match kind {
55 NodeKind::Text(_) => Some(InlineCategory::Text),
56 NodeKind::TaskCheckboxInline { .. } => Some(InlineCategory::TaskCheckboxInline),
57 NodeKind::Emphasis => Some(InlineCategory::Emphasis),
58 NodeKind::Strong => Some(InlineCategory::Strong),
59 NodeKind::StrongEmphasis => Some(InlineCategory::StrongEmphasis),
60 NodeKind::Strikethrough => Some(InlineCategory::Strikethrough),
61 NodeKind::Mark => Some(InlineCategory::Mark),
62 NodeKind::Superscript => Some(InlineCategory::Superscript),
63 NodeKind::Subscript => Some(InlineCategory::Subscript),
64 NodeKind::Link { .. } => Some(InlineCategory::Link),
65 NodeKind::LinkReference { .. } => Some(InlineCategory::LinkReference),
66 NodeKind::FootnoteReference { .. } => Some(InlineCategory::FootnoteReference),
67 NodeKind::Image { .. } => Some(InlineCategory::Image),
68 NodeKind::CodeSpan(_) => Some(InlineCategory::CodeSpan),
69 NodeKind::InlineHtml(_) => Some(InlineCategory::InlineHtml),
70 NodeKind::HardBreak => Some(InlineCategory::HardBreak),
71 NodeKind::SoftBreak => Some(InlineCategory::SoftBreak),
72 NodeKind::PlatformMention { .. } => Some(InlineCategory::PlatformMention),
73 NodeKind::InlineMath { .. } => Some(InlineCategory::InlineMath),
74 NodeKind::DisplayMath { .. } => Some(InlineCategory::DisplayMath),
75 _ => None,
76 }
77}
78
79pub fn is_inline_node(node: &Node) -> bool {
81 is_inline_kind(&node.kind)
82}
83
84pub fn collect_inline_nodes<'a>(nodes: &'a [Node], out: &mut Vec<&'a Node>) {
86 for node in nodes {
87 if is_inline_node(node) {
88 out.push(node);
89 }
90 if !node.children.is_empty() {
91 collect_inline_nodes(&node.children, out);
92 }
93 }
94}
95
96#[cfg(test)]
97mod tests {
98 use super::*;
99
100 #[test]
101 fn smoke_test_classify_inline_kind_basic() {
102 assert_eq!(
103 classify_inline_kind(&NodeKind::Text("x".to_string())),
104 Some(InlineCategory::Text)
105 );
106 assert_eq!(classify_inline_kind(&NodeKind::Paragraph), None);
107 }
108
109 #[test]
110 fn smoke_test_collect_inline_nodes_recursive() {
111 let nodes = vec![Node {
112 kind: NodeKind::Paragraph,
113 span: None,
114 children: vec![Node {
115 kind: NodeKind::Link {
116 url: "https://example.com".to_string(),
117 title: None,
118 },
119 span: None,
120 children: vec![Node {
121 kind: NodeKind::Text("example".to_string()),
122 span: None,
123 children: vec![],
124 }],
125 }],
126 }];
127
128 let mut inlines = Vec::new();
129 collect_inline_nodes(&nodes, &mut inlines);
130
131 assert_eq!(inlines.len(), 2);
132 assert!(matches!(inlines[0].kind, NodeKind::Link { .. }));
133 assert!(matches!(inlines[1].kind, NodeKind::Text(_)));
134 }
135}