Skip to main content

nargo_parser/
oak_template_parser.rs

1use crate::{ParseState, TemplateParser};
2use nargo_ir::{AttributeIR, ElementIR, TemplateNodeIR, Trivia};
3use nargo_types::{Position, Result, Span};
4use oak_core::{parser::ParseSession, tree::RedTree, Parser, SourceText};
5use oak_vue::{VueElementType, VueLanguage, VueParser};
6
7/// 基于 oak-vue 的 Vue 模板解析器
8pub struct OakVueTemplateParser;
9
10impl TemplateParser for OakVueTemplateParser {
11    fn parse(&self, state: &mut ParseState, _lang: &str) -> Result<Vec<TemplateNodeIR>> {
12        let source = SourceText::new(state.cursor.source.to_string());
13        let language = VueLanguage::default();
14        let parser = VueParser::new(&language);
15        let mut session = ParseSession::default();
16        let parse_output = parser.parse(&source, &[], &mut session);
17
18        // 将 green 树转换为 red 树,获取包含位置信息的 AST
19        let green_node = parse_output.result.map_err(|e| nargo_types::Error::parse_error(format!("Vue parsing error: {:?}", e), Span::default()))?;
20        let red_tree = RedTree::new(green_node);
21
22        // 转换 oak-vue 的解析结果为项目的 IR 格式
23        let nodes = self.convert_to_ir(&red_tree, &source);
24        Ok(nodes)
25    }
26}
27
28impl OakVueTemplateParser {
29    /// 将 oak-vue 的解析结果转换为项目的 IR 格式
30    fn convert_to_ir(&self, tree: &RedTree<'_, VueLanguage>, source: &SourceText) -> Vec<TemplateNodeIR> {
31        let mut nodes = Vec::new();
32        self.convert_node(tree, source, &mut nodes);
33        nodes
34    }
35
36    /// 转换单个节点
37    fn convert_node(&self, tree: &RedTree<'_, VueLanguage>, source: &SourceText, result: &mut Vec<TemplateNodeIR>) {
38        match tree {
39            RedTree::Node(node) => {
40                match node.element_type() {
41                    VueElementType::Root => {
42                        for child in node.children() {
43                            self.convert_node(&child, source, result);
44                        }
45                    }
46                    VueElementType::Element => {
47                        let mut tag = String::new();
48                        let mut attributes = Vec::new();
49                        let mut children = Vec::new();
50                        let mut trivia = Trivia::default();
51
52                        for child in node.children() {
53                            match &child {
54                                RedTree::Node(child_node) => {
55                                    match child_node.element_type() {
56                                        VueElementType::Tag => {
57                                            // 解析标签名
58                                            tag = self.get_node_text(child_node, source);
59                                        }
60                                        VueElementType::Attribute | VueElementType::Directive => {
61                                            let attribute = self.convert_attribute(&child, source);
62                                            attributes.push(attribute);
63                                        }
64                                        VueElementType::Element => {
65                                            let mut child_nodes = Vec::new();
66                                            self.convert_node(&child, source, &mut child_nodes);
67                                            children.extend(child_nodes);
68                                        }
69                                        _ => {}
70                                    }
71                                }
72                                RedTree::Leaf(leaf) => {
73                                    // 处理文本节点和其他叶子节点
74                                    let text = leaf.text(source).to_string();
75                                    if !text.trim().is_empty() {
76                                        let span = Span { start: Position::new(1, 1, leaf.span().start as u32), end: Position::new(1, 1, leaf.span().end as u32) };
77                                        let text_node = TemplateNodeIR::Text(text, span, Trivia::default());
78                                        result.push(text_node);
79                                    }
80                                }
81                            }
82                        }
83
84                        let span = Span { start: Position::new(1, 1, node.span().start as u32), end: Position::new(1, 1, node.span().end as u32) };
85                        let element = TemplateNodeIR::Element(ElementIR { tag, attributes, children, is_static: false, span, trivia });
86                        result.push(element);
87                    }
88                    _ => {}
89                }
90            }
91            RedTree::Leaf(_) => {
92                // 叶子节点已经在父节点中处理
93            }
94        }
95    }
96
97    /// 转换属性
98    fn convert_attribute(&self, tree: &RedTree<'_, VueLanguage>, source: &SourceText) -> AttributeIR {
99        let mut name = String::new();
100        let mut value = None;
101        let mut is_directive = false;
102        let mut is_dynamic = false;
103        let mut trivia = Trivia::default();
104
105        if let RedTree::Node(node) = tree {
106            for child in node.children() {
107                if let RedTree::Node(child_node) = &child {
108                    match child_node.element_type() {
109                        VueElementType::AttributeName => {
110                            name = self.get_node_text(child_node, source);
111                        }
112                        VueElementType::AttributeValue => {
113                            let text = self.get_node_text(child_node, source);
114                            value = Some(text);
115                        }
116                        VueElementType::Directive => {
117                            is_directive = true;
118                        }
119                        _ => {}
120                    }
121                }
122            }
123        }
124
125        let span = if let RedTree::Node(node) = tree { Span { start: Position::new(1, 1, node.span().start as u32), end: Position::new(1, 1, node.span().end as u32) } } else { Span { start: Position::new(1, 1, 0), end: Position::new(1, 1, 0) } };
126
127        AttributeIR { name, value, value_ast: None, argument: None, modifiers: Vec::new(), is_directive, is_dynamic, span, trivia }
128    }
129
130    /// 获取节点的文本内容
131    fn get_node_text(&self, node: &oak_core::tree::RedNode<'_, VueLanguage>, source: &SourceText) -> String {
132        node.text(source).to_string()
133    }
134}