use super::env::Env;
use crate::expanded::{Content, Node, Origin};
use crate::tree::{self, Moved, Point};
use std::collections::HashMap;
pub(super) fn of(node: &tree::Node, env: &Env) -> Option<Origin> {
env.origin.or_else(|| range(node))
}
pub(super) fn range(node: &tree::Node) -> Option<Origin> {
let position = node.position()?;
let end = match node {
tree::Node::ComponentContainer(container) => container
.close_position
.map_or_else(|| content_end(node), |close| close.end),
_ => position.end,
};
Some(Origin {
start: position.start,
end,
occurrence: 0,
})
}
fn content_end(node: &tree::Node) -> Point {
let own = node
.position()
.map(|position| position.end)
.unwrap_or_default();
let slots = match node {
tree::Node::ComponentContainer(container) => container.slots.as_slice(),
_ => &[],
};
let inner = slots
.iter()
.flat_map(|slot| {
slot.position
.map(|position| position.end)
.into_iter()
.chain(slot.children.iter().map(content_end))
})
.chain(node.children().iter().map(content_end));
inner.fold(own, |end, point| {
if point.offset > end.offset {
point
} else {
end
}
})
}
pub(super) fn finish(nodes: &mut [Node]) {
list(nodes, None, &mut HashMap::new());
}
fn list(nodes: &mut [Node], parent: Option<Origin>, seen: &mut HashMap<(Point, Point), usize>) {
for node in nodes {
node.origin = numbered(node.origin.or(parent), seen);
let inner = node.origin;
if let Content::ComponentContainer(container) = &mut node.content {
let mut slots = HashMap::new();
for slot in &mut container.slots {
slot.origin = numbered(slot.origin.or(inner), &mut slots);
list(&mut slot.children, slot.origin, &mut HashMap::new());
instances(slot.instances.as_deref_mut(), slot.origin);
}
instances(container.instances.as_deref_mut(), inner);
}
if let Some(children) = node.content.children_mut() {
list(children, inner, &mut HashMap::new());
}
}
}
fn instances(instances: Option<&mut [crate::expanded::Instance]>, parent: Option<Origin>) {
let mut seen = HashMap::new();
for instance in instances.into_iter().flatten() {
list(&mut instance.children, parent, &mut seen);
}
}
fn numbered(origin: Option<Origin>, seen: &mut HashMap<(Point, Point), usize>) -> Option<Origin> {
let mut origin = origin?;
let count = seen.entry((origin.start, origin.end)).or_insert(0);
origin.occurrence = *count;
*count += 1;
Some(origin)
}
pub(super) fn shift(nodes: &mut [Node], moved: Moved) {
let shift = |origin: &mut Option<Origin>| {
if let Some(origin) = origin {
moved.point(&mut origin.start);
moved.point(&mut origin.end);
}
};
for node in nodes {
shift(&mut node.origin);
if let Content::ComponentContainer(container) = &mut node.content {
for slot in &mut container.slots {
shift(&mut slot.origin);
}
}
for list in node.content.lists_mut() {
self::shift(list, moved);
}
}
}
pub(super) fn strip(nodes: &mut [Node]) {
for node in nodes {
node.origin = None;
if let Content::ComponentContainer(container) = &mut node.content {
for slot in &mut container.slots {
slot.origin = None;
}
}
for list in node.content.lists_mut() {
strip(list);
}
}
}