use vize_carton::{directive::parse_vize_directive, Box};
use vize_relief::{
ast::*,
errors::{CompilerError, ErrorCode},
};
use super::{CurrentElement, Parser, ParserStackEntry};
impl<'a> Parser<'a> {
pub(super) fn on_text_impl(&mut self, start: usize, end: usize) {
if start >= end {
return;
}
let content = self.get_source(start, end);
let loc = self.create_loc(start, end);
let text_node = TextNode::new(content, loc);
let boxed = Box::new_in(text_node, self.allocator);
self.add_child(TemplateChildNode::Text(boxed));
}
pub(super) fn on_interpolation_impl(&mut self, start: usize, end: usize) {
let raw_content = self.get_source(start, end);
let content = raw_content.trim();
let leading_ws = raw_content.len() - raw_content.trim_start().len();
let trimmed_start = start + leading_ws;
let trimmed_end = trimmed_start + content.len();
let delim_len = self.options.delimiters.0.len();
let full_start = start - delim_len;
let full_end = end + self.options.delimiters.1.len();
let loc = self.create_loc(full_start, full_end);
let inner_loc = self.create_loc(trimmed_start, trimmed_end);
let expr = SimpleExpressionNode::new(content, false, inner_loc);
let expr_boxed = Box::new_in(expr, self.allocator);
let interp = InterpolationNode {
content: ExpressionNode::Simple(expr_boxed),
loc,
};
let boxed = Box::new_in(interp, self.allocator);
self.add_child(TemplateChildNode::Interpolation(boxed));
}
pub(super) fn on_open_tag_name_impl(&mut self, start: usize, end: usize) {
let tag = self.get_source(start, end);
let ns =
(self.options.get_namespace)(tag, self.stack.last().map(|e| e.element.tag.as_str()));
self.current_element = Some(CurrentElement {
tag: tag.into(),
tag_start: start,
tag_end: end,
ns,
is_self_closing: false,
props: vize_carton::Vec::new_in(self.allocator),
});
}
pub(super) fn on_open_tag_end_impl(&mut self, end: usize) {
if let Some(current) = self.current_element.take() {
let tag_start = current.tag_start;
let loc = self.create_loc(tag_start - 1, end + 1);
let mut element = ElementNode::new(self.allocator, current.tag.clone(), loc);
element.ns = current.ns;
element.is_self_closing = current.is_self_closing;
element.props = current.props;
element.tag_type = self.determine_element_type(&element);
let is_pre = (self.options.is_pre_tag)(element.tag.as_str());
let has_v_pre = element
.props
.iter()
.any(|p| matches!(p, PropNode::Directive(d) if d.name == "pre"));
if current.is_self_closing || (self.options.is_void_tag)(element.tag.as_str()) {
let boxed = Box::new_in(element, self.allocator);
self.add_child(TemplateChildNode::Element(boxed));
} else {
self.stack.push(ParserStackEntry {
element,
in_pre: self.in_pre,
in_v_pre: self.in_v_pre,
});
self.in_pre = is_pre || self.in_pre;
self.in_v_pre = has_v_pre || self.in_v_pre;
}
}
}
pub(super) fn on_self_closing_tag_impl(&mut self, _end: usize) {
if let Some(ref mut current) = self.current_element {
current.is_self_closing = true;
}
}
pub(super) fn on_close_tag_impl(&mut self, start: usize, end: usize) {
let tag = self.get_source(start, end);
let mut found = false;
for i in (0..self.stack.len()).rev() {
if self.stack[i].element.tag.eq_ignore_ascii_case(tag) {
found = true;
let mut elements: vize_carton::Vec<'a, ParserStackEntry<'a>> =
vize_carton::Vec::new_in(self.allocator);
while self.stack.len() > i {
elements.push(self.stack.pop().unwrap());
}
for entry in elements.iter().skip(1) {
let loc = entry.element.loc.clone();
self.errors
.push(CompilerError::new(ErrorCode::MissingEndTag, Some(loc)));
}
for entry in elements.into_iter().rev() {
let in_pre = entry.in_pre;
let in_v_pre = entry.in_v_pre;
let boxed = Box::new_in(entry.element, self.allocator);
self.add_child(TemplateChildNode::Element(boxed));
self.in_pre = in_pre;
self.in_v_pre = in_v_pre;
}
break;
}
}
if !found {
let loc = self.create_loc(start - 2, end + 1); self.errors
.push(CompilerError::new(ErrorCode::InvalidEndTag, Some(loc)));
}
}
pub(super) fn determine_element_type(&self, element: &ElementNode<'a>) -> ElementType {
let tag = element.tag.as_str();
if tag == "slot" {
return ElementType::Slot;
}
if tag == "template" {
let has_structural_directive = element.props.iter().any(|p| {
matches!(p, PropNode::Directive(d) if matches!(d.name.as_str(), "if" | "else-if" | "else" | "for" | "slot"))
});
if has_structural_directive {
return ElementType::Template;
}
}
if self.is_component(tag) {
return ElementType::Component;
}
ElementType::Element
}
pub(super) fn is_component(&self, tag: &str) -> bool {
if matches!(
tag,
"Teleport"
| "Suspense"
| "KeepAlive"
| "BaseTransition"
| "Transition"
| "TransitionGroup"
) {
return true;
}
if let Some(is_custom) = self.options.is_custom_element {
if is_custom(tag) {
return false;
}
}
if let Some(is_native) = self.options.is_native_tag {
if !is_native(tag) {
return true;
}
} else {
if tag.chars().next().is_some_and(|c| c.is_uppercase()) {
return true;
}
}
false
}
pub(super) fn on_comment_impl(&mut self, start: usize, end: usize) {
let content = self.get_source(start, end);
let loc = self.create_loc(start - 4, end + 3);
let directive = parse_vize_directive(content, loc.start.line, loc.start.offset);
if directive.is_none() && !self.options.comments {
return;
}
let mut comment = CommentNode::new(content, loc);
comment.directive = directive.map(|d| d.kind);
let boxed = Box::new_in(comment, self.allocator);
self.add_child(TemplateChildNode::Comment(boxed));
}
pub(super) fn on_error_impl(&mut self, code: ErrorCode, index: usize) {
let loc = self.create_loc(index, index + 1);
self.errors.push(CompilerError::new(code, Some(loc)));
}
}