use super::Doc;
use super::node::{AttVal, Node, NodeId, NodeType};
use super::tables::{CM_INLINE, CM_OBJECT, TagId};
#[derive(Clone, Debug)]
pub struct IStack {
pub tag: usize,
pub element: String,
pub attributes: Vec<AttVal>,
}
impl Doc {
pub(crate) fn dup_attrs(&mut self, attrs: &[AttVal]) -> Vec<AttVal> {
let mut out = Vec::with_capacity(attrs.len());
for a in attrs {
let mut copy = a.clone();
copy.asp = a.asp.map(|n| self.clone_node(Some(n)));
copy.php = a.php.map(|n| self.clone_node(Some(n)));
copy.dict = copy
.attribute
.as_deref()
.and_then(|name| super::tables::tables().attr_index(name));
out.push(copy);
}
out
}
fn is_node_pushable(&self, node: NodeId) -> bool {
let Some(tag) = self.tree.node(node).tag else {
return false;
};
let model = self.tags[tag].model;
if (model & CM_INLINE) == 0 {
return false;
}
if (model & CM_OBJECT) != 0 {
return false;
}
if self.node_is(node, TagId::INS) || self.node_is(node, TagId::DEL) {
return false;
}
true
}
pub(crate) fn push_inline(&mut self, node: NodeId) {
if self.tree.node(node).implicit {
return;
}
if !self.is_node_pushable(node) {
return;
}
if !self.node_is(node, TagId::FONT) && self.is_pushed(node) {
return;
}
let n = self.tree.node(node);
let tag = n.tag.expect("pushable nodes have a tag");
let element = n.element.clone().unwrap_or_default();
let attributes = n.attributes.clone();
let attributes = self.dup_attrs(&attributes);
self.lexer.istack.push(IStack {
tag,
element,
attributes,
});
}
fn pop_istack(&mut self) {
self.lexer.istack.pop();
}
fn pop_istack_until(&mut self, id: TagId) {
while let Some(popped) = self.lexer.istack.pop() {
if self.tags[popped.tag].id == id {
break;
}
}
}
pub(crate) fn pop_inline(&mut self, node: Option<NodeId>) {
if let Some(node) = node {
if !self.is_node_pushable(node) {
return;
}
if self.node_is(node, TagId::A) {
self.pop_istack_until(TagId::A);
return;
}
}
if !self.lexer.istack.is_empty() {
self.pop_istack();
if let Some(insert) = self.lexer.insert
&& insert >= self.lexer.istack.len()
{
self.lexer.insert = None;
}
}
}
pub(crate) fn is_pushed(&self, node: NodeId) -> bool {
let tag = self.tree.node(node).tag;
self.lexer.istack.iter().rev().any(|e| Some(e.tag) == tag)
}
pub(crate) fn is_pushed_last(&self, element: Option<NodeId>, node: NodeId) -> bool {
if let Some(e) = element
&& !self.is_node_pushable(e)
{
return false;
}
match self.lexer.istack.last() {
Some(top) => Some(top.tag) == self.tree.node(node).tag,
None => false,
}
}
pub(crate) const fn inline_dup(&mut self, node: Option<NodeId>) -> usize {
let n = self
.lexer
.istack
.len()
.saturating_sub(self.lexer.istackbase);
if n > 0 {
self.lexer.insert = Some(self.lexer.istackbase);
self.lexer.inode = node;
}
n
}
pub(crate) const fn defer_dup(&mut self) {
self.lexer.insert = None;
self.lexer.inode = None;
}
pub(crate) fn inserted_token(&mut self) -> Option<NodeId> {
let Some(insert) = self.lexer.insert else {
let node = self.lexer.inode;
self.lexer.inode = None;
return node;
};
if self.lexer.inode.is_none() {
self.lexer.lines = self.stream.curline as u32;
self.lexer.columns = self.stream.curcol as u32;
}
let mut node = Node::new(self.lexer.lines, self.lexer.columns);
node.ntype = NodeType::StartTag;
node.implicit = true;
node.start = self.lexer.txtstart;
node.end = self.lexer.txtend;
let entry = self.lexer.istack[insert].clone();
node.element = Some(entry.element);
node.tag = Some(entry.tag);
let id = self.tree.alloc(node);
let attributes = self.dup_attrs(&entry.attributes);
self.tree.node_mut(id).attributes = attributes;
let next = insert + 1;
self.lexer.insert = if next < self.lexer.istack.len() {
Some(next)
} else {
None
};
Some(id)
}
pub(crate) fn switch_inline(&mut self, element: NodeId, node: NodeId) -> bool {
let (Some(etag), Some(ntag)) = (self.tree.node(element).tag, self.tree.node(node).tag)
else {
return false;
};
if !(self.is_pushed(element)
&& self.is_pushed(node)
&& self.lexer.istack.len() - self.lexer.istackbase >= 2)
{
return false;
}
let mut i = (self.lexer.istack.len() - self.lexer.istackbase) as isize - 1;
while i >= 0 {
let iu = i as usize;
if self.lexer.istack[iu].tag == etag {
let mut j = i - 1;
while j >= 0 {
let ju = j as usize;
if self.lexer.istack[ju].tag == ntag {
self.lexer.istack.swap(iu, ju);
return true;
}
j -= 1;
}
}
i -= 1;
}
false
}
pub(crate) fn inline_dup1(&mut self, node: Option<NodeId>, element: Option<NodeId>) -> bool {
let Some(element) = element else {
return false;
};
let Some(etag) = self.tree.node(element).tag else {
return false;
};
let n = self
.lexer
.istack
.len()
.saturating_sub(self.lexer.istackbase);
if n == 0 {
return false;
}
for i in (0..n).rev() {
if self.lexer.istack[i].tag == etag {
self.lexer.insert = Some(i);
self.lexer.inode = node;
return true;
}
}
false
}
}