mod attr;
pub(crate) mod scope;
mod tag;
use crate::markup::Namespace;
use vize_l0::{Allocator, Box, Vec};
use crate::event::{Event, EventKind};
use crate::markup::surface::{
Element, ElementClose, Interpolation, OpenTag, SurfaceChild, SurfaceTree, Token,
};
#[derive(Clone, Copy)]
enum RawKind {
Comment,
Cdata,
Pi,
}
struct Frame<'a> {
open: OpenTag<'a>,
children: Vec<'a, SurfaceChild<'a>>,
ns: Namespace,
}
impl<'a> scope::Frame<'a> for Frame<'a> {
fn tag(&self) -> &'a str {
let text = self.open.lt_name.text;
text.get(1..).unwrap_or(text)
}
fn namespace(&self) -> Namespace {
self.ns
}
}
pub(crate) fn build<'a, const RAW: bool, const LINT: bool>(
allocator: &'a Allocator,
src: &'a str,
events: &[Event],
repair_interactive: bool,
) -> (SurfaceTree<'a>, bool) {
let mut b = Builder::<RAW, LINT> {
src,
allocator,
events,
i: 0,
cursor: 0,
root: Vec::new_in(&allocator),
stack: Vec::new_in(&allocator),
recovery: scope::Recovery::new(allocator, repair_interactive),
repaired: false,
};
b.run();
b.flush_gap(src.len());
while let Some(frame) = b.stack.pop() {
let element = Element {
open: frame.open,
children: frame.children,
close: ElementClose::Missing,
};
b.attach(element);
}
(
SurfaceTree {
source: src,
children: b.root,
},
b.repaired,
)
}
struct Builder<'a, 'e, const RAW: bool, const LINT: bool> {
src: &'a str,
allocator: &'a Allocator,
events: &'e [Event],
i: usize,
cursor: usize,
root: Vec<'a, SurfaceChild<'a>>,
stack: Vec<'a, Frame<'a>>,
recovery: scope::Recovery<'a>,
repaired: bool,
}
impl<'a, const RAW: bool, const LINT: bool> Builder<'a, '_, RAW, LINT> {
fn run(&mut self) {
while let Some(ev) = self.events.get(self.i).copied() {
match ev.kind {
EventKind::Text => self.text(ev),
EventKind::Interpolation => self.interpolation(ev),
EventKind::OpenTagName => self.element(ev),
EventKind::CloseTag => self.close_tag(ev),
EventKind::Comment => self.raw_node(ev, RawKind::Comment),
EventKind::Cdata => self.raw_node(ev, RawKind::Cdata),
EventKind::ProcessingInstruction => self.raw_node(ev, RawKind::Pi),
EventKind::OpenTagEnd
| EventKind::SelfClosingTag
| EventKind::AttrName
| EventKind::AttrNameEnd
| EventKind::AttrData
| EventKind::AttrEnd => self.i += 1,
}
}
}
fn token_at(&mut self, start: usize, end: usize) -> Token<'a> {
debug_assert!(self.cursor <= start && start <= end && end <= self.src.len());
let leading = crate::slice::range(self.src, self.cursor.min(start), start);
let text = crate::slice::range(self.src, start, end);
self.cursor = end;
Token::present(leading, text)
}
fn missing_to(&mut self, leading_end: usize) -> Token<'a> {
debug_assert!(self.cursor <= leading_end && leading_end <= self.src.len());
let leading = crate::slice::range(self.src, self.cursor.min(leading_end), leading_end);
self.cursor = self.cursor.max(leading_end);
Token::missing(
leading,
crate::slice::range(self.src, self.cursor, self.cursor),
)
}
fn flush_gap(&mut self, target: usize) {
debug_assert!(self.cursor <= target);
if self.cursor < target {
let text = crate::slice::range(self.src, self.cursor, target);
self.cursor = target;
self.push_child(SurfaceChild::Unexpected(Token::present("", text)));
}
}
fn find_byte(&self, byte: u8, from: usize, to: usize) -> Option<usize> {
let bytes = self.src.as_bytes();
bytes
.get(from..to.min(bytes.len()))?
.iter()
.position(|b| *b == byte)
.map(|p| from + p)
}
fn push_child(&mut self, child: SurfaceChild<'a>) {
match self.stack.last_mut() {
Some(frame) => frame.children.push(child),
None => self.root.push(child),
}
}
fn attach(&mut self, element: Element<'a>) {
let boxed = Box::new_in(element, &self.allocator);
self.push_child(SurfaceChild::Element(boxed));
}
fn text(&mut self, ev: Event) {
let start = ev.start as usize;
let mut end = ev.end as usize;
self.i += 1;
while let Some(next) = self.events.get(self.i) {
if next.kind == EventKind::Text && next.start as usize == end {
end = next.end as usize;
self.i += 1;
} else {
break;
}
}
self.flush_gap(start);
let token = self.token_at(start, end);
self.push_child(SurfaceChild::Text(token));
}
fn interpolation(&mut self, ev: Event) {
let (s, e) = (ev.start as usize, ev.end as usize);
self.i += 1;
let width = if RAW { ev.interpolation_width() } else { 2 };
let open_start = s.saturating_sub(width);
self.flush_gap(open_start);
let mut open = self.token_at(open_start, s);
if RAW && width == 3 {
open.mark_raw_interpolation();
}
let content = self.token_at(s, e);
let close = self.token_at(e, (e + width).min(self.src.len()));
let node = Interpolation {
open,
content,
close,
};
self.push_child(SurfaceChild::Interpolation(Box::new_in(
node,
&self.allocator,
)));
}
fn raw_node(&mut self, ev: Event, kind: RawKind) {
let e = ev.end as usize;
self.i += 1;
let tail = crate::slice::from(self.src, e.min(self.src.len()));
let closer = match kind {
RawKind::Comment => {
if tail.starts_with("-->") {
3
} else if tail.starts_with("--!>") {
4
} else if tail.starts_with('>') {
1
} else {
0
}
}
RawKind::Cdata => {
if tail.starts_with("]]>") {
3
} else {
0
}
}
RawKind::Pi => {
if tail.starts_with('>') {
1
} else {
0
}
}
};
let token = self.token_at(self.cursor, e + closer);
self.push_child(match kind {
RawKind::Comment => SurfaceChild::Comment(token),
RawKind::Cdata => SurfaceChild::Cdata(token),
RawKind::Pi => SurfaceChild::ProcessingInstruction(token),
});
}
}