oak_markdown/builder/
mod.rs

1use crate::{ast::*, language::MarkdownLanguage, parser::MarkdownParser};
2use oak_core::{Builder, BuilderCache, GreenNode, OakError, Parser, RedNode, RedTree, SourceText, TextEdit, source::Source};
3
4/// Markdown 语言的 AST 构建器
5#[derive(Clone)]
6pub struct MarkdownBuilder<'config> {
7    /// 语言配置
8    config: &'config MarkdownLanguage,
9}
10
11impl<'config> MarkdownBuilder<'config> {
12    /// 创建新的 Markdown 构建器
13    pub fn new(config: &'config MarkdownLanguage) -> Self {
14        Self { config }
15    }
16
17    /// 从语法树构建 AST 根节点
18    fn build_root(&self, green_tree: &GreenNode<MarkdownLanguage>, source: &SourceText) -> Result<MarkdownRoot, OakError> {
19        let red_root = RedNode::new(green_tree, 0);
20
21        let mut blocks = Vec::new();
22        for child in red_root.children() {
23            if let RedTree::Node(node) = child {
24                if let Some(block) = self.build_block(node, source) {
25                    blocks.push(block);
26                }
27            }
28        }
29
30        Ok(MarkdownRoot { blocks })
31    }
32
33    /// 构建块级元素
34    fn build_block(&self, node: RedNode<MarkdownLanguage>, source: &SourceText) -> Option<Block> {
35        use crate::kind::MarkdownSyntaxKind::*;
36
37        let kind = node.green.kind;
38        match kind {
39            Heading1 | Heading2 | Heading3 | Heading4 | Heading5 | Heading6 => {
40                let level = match kind {
41                    Heading1 => 1,
42                    Heading2 => 2,
43                    Heading3 => 3,
44                    Heading4 => 4,
45                    Heading5 => 5,
46                    Heading6 => 6,
47                    _ => unreachable!(),
48                };
49                Some(Block::Heading(crate::ast::Heading { level, content: source.get_text_in(node.span()).to_string(), span: node.span() }))
50            }
51            Paragraph => Some(Block::Paragraph(crate::ast::Paragraph { content: source.get_text_in(node.span()).to_string(), span: node.span() })),
52            CodeBlock => Some(Block::CodeBlock(crate::ast::CodeBlock {
53                language: None, // TODO: 提取语言标识符
54                content: source.get_text_in(node.span()).to_string(),
55                span: node.span(),
56            })),
57            UnorderedList | OrderedList => {
58                let mut items = Vec::new();
59                for child in node.children() {
60                    if let RedTree::Node(child_node) = child {
61                        if child_node.green.kind == ListItem {
62                            items.push(self.build_list_item(child_node, source));
63                        }
64                    }
65                }
66                Some(Block::List(crate::ast::List { is_ordered: kind == OrderedList, items, span: node.span() }))
67            }
68            Blockquote => Some(Block::Blockquote(crate::ast::Blockquote { content: node.children().filter_map(|child| if let RedTree::Node(child_node) = child { self.build_block(child_node, source) } else { None }).collect(), span: node.span() })),
69            HorizontalRule => Some(Block::HorizontalRule(crate::ast::HorizontalRule { span: node.span() })),
70            Table => {
71                // TODO: 实现表格构建
72                None
73            }
74            HtmlTag => {
75                // TODO: 实现 HTML 构建
76                None
77            }
78            _ => None,
79        }
80    }
81
82    fn build_list_item(&self, node: RedNode<MarkdownLanguage>, source: &SourceText) -> crate::ast::ListItem {
83        let mut content = Vec::new();
84        for child in node.children() {
85            if let RedTree::Node(child_node) = child {
86                if let Some(block) = self.build_block(child_node, source) {
87                    content.push(block);
88                }
89            }
90        }
91
92        crate::ast::ListItem {
93            content,
94            is_task: false,   // TODO: 检测任务列表
95            is_checked: None, // TODO: 检测勾选状态
96            span: node.span(),
97        }
98    }
99}
100
101impl<'config> Builder<MarkdownLanguage> for MarkdownBuilder<'config> {
102    fn build<'a, S: Source + ?Sized>(&self, source: &S, edits: &[TextEdit], _cache: &'a mut impl BuilderCache<MarkdownLanguage>) -> oak_core::builder::BuildOutput<MarkdownLanguage> {
103        let parser = MarkdownParser::new(self.config);
104        let mut parse_session = oak_core::parser::session::ParseSession::<MarkdownLanguage>::default();
105        let parse_result = parser.parse(source, edits, &mut parse_session);
106
107        match parse_result.result {
108            Ok(green_tree) => {
109                let source_text = SourceText::new(source.get_text_in((0..source.length()).into()));
110                match self.build_root(green_tree, &source_text) {
111                    Ok(ast_root) => oak_core::OakDiagnostics { result: Ok(ast_root), diagnostics: parse_result.diagnostics },
112                    Err(build_error) => {
113                        let mut diagnostics = parse_result.diagnostics;
114                        diagnostics.push(build_error.clone());
115                        oak_core::OakDiagnostics { result: Err(build_error), diagnostics }
116                    }
117                }
118            }
119            Err(parse_error) => oak_core::OakDiagnostics { result: Err(parse_error), diagnostics: parse_result.diagnostics },
120        }
121    }
122}