1use std::collections::{BTreeSet, HashMap, HashSet};
14use std::sync::Arc;
15
16use super::{Caller, Instance, Layout, Node, Record, Resolver, Scene, resolved};
17use crate::parse::{self, Child, Element, TextPart};
18use crate::proto;
19use crate::tree::NodeId;
20use crate::wire::{self, Applied, Edit, Uses};
21use crate::{Files, Problem};
22
23#[derive(Debug, Clone)]
26pub struct Resolution {
27 path: String,
28 scene: Scene,
29 uses: Uses,
30 record: Record,
31 pending: BTreeSet<String>,
34 pub(crate) wholes: usize,
37}
38
39#[derive(Debug)]
41pub enum Outcome {
42 Same,
44 Changed(proto::Patch, Applied),
47 Problems(Vec<Problem>),
49}
50
51impl Resolution {
52 pub fn new(files: &dyn Files, path: &str) -> Result<Resolution, Vec<Problem>> {
54 let (scene, record) = resolved(files, path)?;
55 Ok(Resolution {
56 path: path.to_string(),
57 uses: Uses::of(&scene.root),
58 scene,
59 record,
60 pending: BTreeSet::new(),
61 wholes: 0,
62 })
63 }
64
65 pub fn scene(&self) -> &Scene {
67 &self.scene
68 }
69
70 pub fn wholes(&self) -> usize {
74 self.wholes
75 }
76
77 pub fn uses(&self) -> &Uses {
79 &self.uses
80 }
81
82 pub fn update(&mut self, files: &dyn Files, changed: &[String]) -> Outcome {
89 self.pending.extend(changed.iter().cloned());
90 match self.again(files) {
91 Ok(outcome) => {
92 self.pending.clear();
93 outcome
94 }
95 Err(()) => match resolved(files, &self.path) {
96 Err(problems) => Outcome::Problems(problems),
97 Ok((scene, record)) => {
100 self.pending.clear();
101 self.whole(scene, record)
102 }
103 },
104 }
105 }
106
107 fn whole(&mut self, scene: Scene, record: Record) -> Outcome {
110 self.wholes += 1;
111 let was_themed = self.scene.themed;
112 let edits = wire::edits(&self.scene, &scene);
113 let patch = self.patch(&edits, &scene.images, &scene.fonts, scene.themed);
114 let images = scene.images.clone();
115 let fonts = scene.fonts.clone();
116 match wire::apply_edits(
117 &mut self.scene,
118 &mut self.uses,
119 edits,
120 images,
121 fonts,
122 scene.themed,
123 ) {
124 Ok(applied) => {
125 self.scene.files = scene.files;
126 self.record = record;
127 if patch.ops.is_empty() && was_themed == patch.themed {
128 Outcome::Same
129 } else {
130 Outcome::Changed(patch, applied)
131 }
132 }
133 Err(_) => {
134 self.uses = Uses::of(&scene.root);
137 self.scene = scene;
138 self.record = record;
139 let patch = proto::Patch {
140 ops: wire::encode_edits(&[Edit::Insert {
141 parent: None,
142 after: None,
143 node: self.scene.root.clone(),
144 }]),
145 images: self.scene.images.values().map(|f| wire::file(f)).collect(),
146 fonts: self
147 .scene
148 .fonts
149 .values()
150 .map(|f| wire::font_file(f))
151 .collect(),
152 themed: self.scene.themed,
153 };
154 let applied = Applied {
155 removed: vec![],
156 placed: vec![self.scene.root.id.clone()],
157 changed: vec![],
158 };
159 Outcome::Changed(patch, applied)
160 }
161 }
162 }
163
164 fn patch(
167 &self,
168 edits: &[Edit],
169 images: &super::ImageFiles,
170 fonts: &crate::fonts::FontFiles,
171 themed: bool,
172 ) -> proto::Patch {
173 proto::Patch {
174 ops: wire::encode_edits(edits),
175 images: images
176 .values()
177 .filter(|f| !self.scene.images.contains_key(&f.id))
178 .map(|f| wire::file(f))
179 .collect(),
180 fonts: fonts
181 .values()
182 .filter(|f| !self.scene.fonts.contains_key(&f.id))
183 .map(|f| wire::font_file(f))
184 .collect(),
185 themed,
186 }
187 }
188
189 fn again(&mut self, files: &dyn Files) -> Result<Outcome, ()> {
192 let pending: Vec<String> = self.pending.iter().cloned().collect();
193 let mut w = Work {
194 r: Resolver::new(files),
195 files,
196 res: self,
197 edits: Vec::new(),
198 forget: Vec::new(),
199 forget_instances: Vec::new(),
200 covered: HashSet::new(),
201 covered_instances: Vec::new(),
202 own: HashMap::new(),
203 parses: HashMap::new(),
204 indexes: HashMap::new(),
205 failed: false,
206 whole: false,
207 };
208 for key in &pending {
209 if w.res.record.parses.contains_key(key) {
210 w.file(key);
211 }
212 }
213 for key in &pending {
214 let readers: Vec<Vec<u32>> = w
215 .res
216 .record
217 .readers
218 .get(key)
219 .map(|r| r.iter().cloned().collect())
220 .unwrap_or_default();
221 for id in readers {
222 w.reader(key, &id);
223 }
224 }
225 let Work {
226 r,
227 edits,
228 forget,
229 forget_instances,
230 parses,
231 failed,
232 whole,
233 ..
234 } = w;
235 if failed || !r.problems.is_empty() {
236 return Err(());
237 }
238 if whole {
239 let (scene, record) = resolved(files, &self.path).map_err(|_| ())?;
240 return Ok(self.whole(scene, record));
241 }
242 let record = &mut self.record;
245 for prefix in &forget_instances {
246 record.forget_instances(prefix);
247 }
248 for id in &forget {
249 record.forget(&id.0);
250 record.themed.remove(id);
251 }
252 record.merge(r.record);
253 record.parses.extend(parses);
254 let themed = !record.themed.is_empty();
255 if edits.is_empty() && themed == self.scene.themed {
256 return Ok(Outcome::Same);
257 }
258 let patch = self.patch(&edits, &r.images, &r.fonts, themed);
259 let applied = wire::apply_edits(
260 &mut self.scene,
261 &mut self.uses,
262 edits,
263 r.images,
264 r.fonts,
265 themed,
266 )
267 .map_err(|_| ())?;
268 for file in r.read {
269 if !self.scene.files.contains(&file) {
270 self.scene.files.push(file);
271 }
272 }
273 Ok(Outcome::Changed(patch, applied))
275 }
276}
277
278impl Record {
279 fn forget_instances(&mut self, prefix: &[u32]) {
282 let gone: Vec<Vec<u32>> = self
283 .instances
284 .range(prefix.to_vec()..)
285 .take_while(|(k, _)| k.starts_with(prefix))
286 .map(|(k, _)| k.clone())
287 .collect();
288 for k in gone {
289 if let Some(i) = self.instances.remove(&k)
290 && let Some(set) = self.by_file.get_mut(&i.file)
291 {
292 set.remove(&k);
293 }
294 }
295 let read: Vec<Vec<u32>> = self
296 .reads
297 .range(prefix.to_vec()..)
298 .take_while(|(k, _)| k.starts_with(prefix))
299 .map(|(k, _)| k.clone())
300 .collect();
301 for k in read {
302 self.forget(&k);
303 }
304 let themed: Vec<NodeId> = self
305 .themed
306 .range(NodeId(prefix.to_vec())..)
307 .take_while(|k| k.0.starts_with(prefix))
308 .cloned()
309 .collect();
310 for k in themed {
311 self.themed.remove(&k);
312 }
313 }
314
315 fn merge(&mut self, other: Record) {
317 self.instances.extend(other.instances);
318 for (file, set) in other.by_file {
319 self.by_file.entry(file).or_default().extend(set);
320 }
321 for (key, set) in other.readers {
322 self.readers.entry(key).or_default().extend(set);
323 }
324 for (id, set) in other.reads {
325 self.reads.entry(id).or_default().extend(set);
326 }
327 self.themed.extend(other.themed);
328 self.parses.extend(other.parses);
329 }
330}
331
332struct Work<'a, 'r> {
335 r: Resolver<'a>,
336 files: &'a dyn Files,
337 res: &'r Resolution,
338 edits: Vec<Edit>,
339 forget: Vec<NodeId>,
341 forget_instances: Vec<Vec<u32>>,
343 covered: HashSet<NodeId>,
346 covered_instances: Vec<Vec<u32>>,
347 own: HashMap<NodeId, Node>,
349 parses: HashMap<String, Arc<parse::File>>,
351 indexes: HashMap<String, HashMap<u32, Vec<usize>>>,
353 failed: bool,
355 whole: bool,
357}
358
359enum Act<'p> {
362 Own(&'p Element, bool),
364 Whole(&'p Element, &'p Element),
366 Insert {
367 parent: &'p Element,
368 after: Option<&'p Element>,
369 el: &'p Element,
370 },
371 Remove(&'p Element),
372 Move {
373 parent: &'p Element,
374 after: Option<&'p Element>,
375 el: &'p Element,
376 },
377}
378
379impl Work<'_, '_> {
380 fn file(&mut self, path: &str) {
383 let old = self.res.record.parses[path].clone();
384 let Some(source) = self.files.read(path) else {
385 self.failed = true;
386 return;
387 };
388 let new = match self.files.parse(path, &source) {
389 Ok(new) => new,
390 Err(_) => {
391 self.failed = true;
392 return;
393 }
394 };
395 self.parses.insert(path.to_string(), new.clone());
396 self.indexes.remove(path);
397 let mut scope = parse::File {
399 imports: new.imports.clone(),
400 root: Element {
401 id: new.root.id,
402 name: new.root.name.clone(),
403 attrs: new.root.attrs.clone(),
404 children: Vec::new(),
405 pos: new.root.pos,
406 },
407 next_id: new.next_id,
408 };
409 self.r.instance = Vec::new();
410 self.r.current = None;
411 let saved = std::mem::take(&mut self.r.record);
413 self.r.bind_own(path, &mut scope.root);
414 let scoped = self.r.scope(path, &scope);
415 self.r.record = saved;
416 let Some((imports, _)) = scoped else {
417 self.failed = true;
418 return;
419 };
420 let old_imports = import_map(path, &old);
421 let props = |f: &parse::File| f.root.attrs.iter().find(|a| a.name == "props").cloned();
422 let props_same = match (props(&old), props(&new)) {
423 (Some(a), Some(b)) => crate::ids::same_value(&a.value, &b.value),
424 (None, None) => true,
425 _ => false,
426 };
427 let instances: Vec<Vec<u32>> = self
428 .res
429 .record
430 .by_file
431 .get(path)
432 .map(|s| s.iter().cloned().collect())
433 .unwrap_or_default();
434 if new.root.id != old.root.id || new.root.name != old.root.name || !props_same {
435 for i in instances {
437 self.whole_instance(&i);
438 }
439 return;
440 }
441 let retargeted: HashSet<&str> = imports
443 .keys()
444 .chain(old_imports.keys())
445 .filter(|n| imports.get(*n) != old_imports.get(*n))
446 .map(String::as_str)
447 .collect();
448 let is_instance = |el: &Element| imports.contains_key(&el.name);
449 let was_instance = |el: &Element| old_imports.contains_key(&el.name);
450 let mut acts = Vec::new();
451 let mut dup = false;
452 walk(
453 &old.root,
454 &new.root,
455 true,
456 &|el| is_instance(el),
457 &|el| was_instance(el),
458 &retargeted,
459 &mut acts,
460 &mut dup,
461 );
462 if dup {
463 self.failed = true;
464 return;
465 }
466 for i in instances {
467 if self.is_covered_instance(&i) {
468 continue;
469 }
470 let Some(inst) = self.res.record.instances.get(&i).cloned() else {
471 continue;
472 };
473 for act in &acts {
474 self.act(&i, &inst, path, act);
475 }
476 }
477 }
478
479 fn act(&mut self, i: &[u32], inst: &Instance, path: &str, act: &Act) {
481 match *act {
482 Act::Own(el, root) => {
483 let id = if root {
484 inst.root.clone()
485 } else {
486 node_id(i, el)
487 };
488 self.own_again(i, inst, path, el, &id, root);
489 }
490 Act::Whole(el, old) => {
491 let was = self.node_of(i, inst, old, true);
492 self.whole_again(i, inst, path, el, &was);
493 }
494 Act::Insert { parent, after, el } => {
495 let parent_id = self.node_of(i, inst, parent, false);
496 let after = after.map(|a| self.node_of(i, inst, a, false));
497 if self.covered.contains(&parent_id) {
498 return;
499 }
500 let layout = self.layout(&parent_id);
501 let Some(node) = self.subtree(i, inst, path, el, &parent_id, layout) else {
502 return;
503 };
504 self.edits.push(Edit::Insert {
505 parent: Some(parent_id),
506 after,
507 node,
508 });
509 }
510 Act::Remove(old) => {
511 let id = self.node_of(i, inst, old, true);
512 if self.covered.contains(&id) {
513 return;
514 }
515 self.drop_subtree(&id);
516 self.edits.push(Edit::Remove(id));
517 }
518 Act::Move { parent, after, el } => {
519 let id = self.node_of(i, inst, el, false);
520 let parent = self.node_of(i, inst, parent, false);
521 let after = after.map(|a| self.node_of(i, inst, a, false));
522 self.edits.push(Edit::Move { id, parent, after });
523 }
524 }
525 }
526
527 fn node_of(&self, i: &[u32], inst: &Instance, el: &Element, old: bool) -> NodeId {
531 if el.id == root_id(inst) {
532 return inst.root.clone();
533 }
534 let mut key = i.to_vec();
535 key.push(el.id);
536 let made = if old {
538 self.res.record.instances.get(&key)
539 } else {
540 self.r
541 .record
542 .instances
543 .get(&key)
544 .or_else(|| self.res.record.instances.get(&key))
545 };
546 match made {
547 Some(child) => child.root.clone(),
548 None => NodeId(key),
549 }
550 }
551
552 fn layout(&self, parent: &NodeId) -> Option<Layout> {
554 self.own
555 .get(parent)
556 .or_else(|| self.res.scene.node(parent))
557 .map(|n| n.layout)
558 }
559
560 fn reader(&mut self, key: &str, id: &[u32]) {
562 if self.is_covered_instance(id) {
563 return;
564 }
565 if let Some(inst) = self.res.record.instances.get(id).cloned() {
566 if key.ends_with(super::THEMES_FILE) {
567 self.whole_instance(id);
569 } else {
570 let Some(file) = self.parse_of(&inst.file) else {
572 self.failed = true;
573 return;
574 };
575 self.r.instance = id.to_vec();
576 let mut scope = (*file).clone();
577 scope.root.children.clear();
578 let saved = std::mem::take(&mut self.r.record);
579 if self.r.scope(&inst.file, &scope).is_none() {
580 self.failed = true;
581 }
582 self.r.record = saved;
583 }
584 return;
585 }
586 let node = NodeId(id.to_vec());
587 if self.covered.contains(&node) || self.own.contains_key(&node) {
588 return;
589 }
590 let (prefix, el_id) = id.split_at(id.len() - 1);
591 let Some(inst) = self.res.record.instances.get(prefix).cloned() else {
592 return;
593 };
594 let Some(file) = self.parse_of(&inst.file) else {
595 self.failed = true;
596 return;
597 };
598 let Some(el) = self.element(&inst.file, &file, el_id[0]) else {
599 return;
600 };
601 let root = node == inst.root;
602 self.own_again(prefix, &inst, &inst.file.clone(), &el, &node, root);
603 }
604
605 fn parse_of(&self, path: &str) -> Option<Arc<parse::File>> {
607 self.parses
608 .get(path)
609 .or_else(|| self.res.record.parses.get(path))
610 .cloned()
611 }
612
613 fn element(&mut self, path: &str, file: &parse::File, id: u32) -> Option<Element> {
615 let index = self
616 .indexes
617 .entry(path.to_string())
618 .or_insert_with(|| index(&file.root));
619 let at = index.get(&id)?;
620 let mut el = &file.root;
621 for &i in at {
622 match &el.children[i] {
623 Child::Element(e) => el = e,
624 Child::Text(..) => return None,
625 }
626 }
627 Some(shallow(el))
628 }
629
630 fn whole_instance(&mut self, i: &[u32]) {
633 if self.is_covered_instance(i) {
634 return;
635 }
636 let Some((caller, el_id)) = i.split_last().map(|(l, p)| (p, *l)) else {
637 self.whole = true;
638 return;
639 };
640 let (Some(inst), Some(at)) = (
641 self.res.record.instances.get(caller).cloned(),
642 self.res.record.instances.get(i).cloned(),
643 ) else {
644 self.whole = true;
645 return;
646 };
647 let Some(file) = self.parse_of(&inst.file) else {
648 self.failed = true;
649 return;
650 };
651 let Some(el) = self.element_whole(&inst.file, &file, el_id) else {
652 return;
654 };
655 self.whole_again(caller, &inst, &inst.file.clone(), &el, &at.root);
656 }
657
658 fn element_whole(&mut self, path: &str, file: &parse::File, id: u32) -> Option<Element> {
660 let index = self
661 .indexes
662 .entry(path.to_string())
663 .or_insert_with(|| index(&file.root));
664 let at = index.get(&id)?;
665 let mut el = &file.root;
666 for &i in at {
667 match &el.children[i] {
668 Child::Element(e) => el = e,
669 Child::Text(..) => return None,
670 }
671 }
672 Some(el.clone())
673 }
674
675 fn is_covered_instance(&self, i: &[u32]) -> bool {
676 self.covered_instances.iter().any(|p| i.starts_with(p))
677 }
678
679 fn enter(&mut self, i: &[u32], depth: usize) {
682 self.r.instance = i.to_vec();
683 self.r.stack = (0..=i.len())
684 .filter_map(|k| self.res.record.instances.get(&i[..k]))
685 .map(|inst| inst.file.clone())
686 .collect();
687 self.r.depth = depth;
688 self.r.current = None;
689 }
690
691 fn depth_of(&self, id: &NodeId) -> usize {
693 let order = self.res.scene.ids.get(id).map_or(0, |o| o.len());
694 order + id.0.len() - 1
695 }
696
697 fn own_again(
700 &mut self,
701 i: &[u32],
702 inst: &Instance,
703 path: &str,
704 el: &Element,
705 id: &NodeId,
706 root: bool,
707 ) {
708 if self.covered.contains(id) || self.own.contains_key(id) {
709 return;
710 }
711 let Some(was) = self.res.scene.node(id) else {
712 return;
713 };
714 let parent = if root {
715 inst.parent
716 } else {
717 let order = &self.res.scene.ids.get(id).expect("indexed")[..];
718 let parent = self.res.scene.at(&order[..order.len() - 1]);
719 parent
720 .and_then(|p| self.own.get(&p.id).or(Some(p)))
721 .map(|p| p.layout)
722 };
723 let depth = self.depth_of(id);
724 self.enter(i, depth);
725 let mut el = shallow(el);
726 self.r.bind_own(path, &mut el);
727 self.r.own_only = true;
728 let node = self
729 .r
730 .element(path, &el, &inst.props, &inst.imports, root, parent);
731 self.r.own_only = false;
732 let Some(mut node) = node else {
733 self.failed = true;
734 return;
735 };
736 if root && let Some(caller) = &inst.caller {
737 overrides(&mut self.r, caller, &mut node, inst.parent);
738 }
739 self.forget.push(id.clone());
740 if !node.same_own(was) {
741 self.edits.push(Edit::Change(node.own()));
742 }
743 self.own.insert(id.clone(), node);
744 }
745
746 fn whole_again(&mut self, i: &[u32], inst: &Instance, path: &str, el: &Element, was: &NodeId) {
749 if self.covered.contains(was) {
750 return;
751 }
752 let Some(order) = self.res.scene.ids.get(was).cloned() else {
753 return;
754 };
755 let parent_order = &order[..order.len().saturating_sub(1)];
756 let Some(parent) = (!order.is_empty())
757 .then(|| self.res.scene.at(parent_order))
758 .flatten()
759 else {
760 self.whole = true;
762 return;
763 };
764 let parent_id = parent.id.clone();
765 let after = parent
766 .children
767 .key_before(order.last().expect("not the root"))
768 .and_then(|k| parent.children.get(k))
769 .map(|n| n.id.clone());
770 let layout = self.layout(&parent_id);
771 let Some(node) = self.subtree(i, inst, path, el, &parent_id, layout) else {
772 return;
773 };
774 self.drop_subtree(was);
775 if node.id == *was {
776 self.edits
777 .extend(wire::subtree(&self.res.scene, &order, &node));
778 } else {
779 self.edits.push(Edit::Remove(was.clone()));
780 self.edits.push(Edit::Insert {
781 parent: Some(parent_id),
782 after,
783 node,
784 });
785 }
786 }
787
788 fn subtree(
791 &mut self,
792 i: &[u32],
793 inst: &Instance,
794 path: &str,
795 el: &Element,
796 parent: &NodeId,
797 layout: Option<Layout>,
798 ) -> Option<Node> {
799 let depth = self.depth_of(parent) + 1;
800 self.enter(i, depth);
801 let mut el = el.clone();
802 self.r.bind_element(path, &mut el);
803 let node = self
804 .r
805 .element(path, &el, &inst.props, &inst.imports, false, layout);
806 if node.is_none() {
807 self.failed = true;
808 }
809 node
810 }
811
812 fn drop_subtree(&mut self, id: &NodeId) {
815 let Some(node) = self.res.scene.node(id) else {
816 return;
817 };
818 let mut todo = vec![node];
819 while let Some(n) = todo.pop() {
820 self.covered.insert(n.id.clone());
821 self.forget.push(n.id.clone());
822 let (prefix, _) = n.id.0.split_at(n.id.0.len() - 1);
823 let instance_root = !prefix.is_empty()
824 && self
825 .res
826 .record
827 .instances
828 .get(prefix)
829 .is_some_and(|inst| inst.root == n.id)
830 && n.id != *id;
831 if instance_root || (n.id == *id && self.is_root_of_instance(&n.id)) {
832 self.forget_instances.push(prefix.to_vec());
833 self.covered_instances.push(prefix.to_vec());
834 for c in &n.children {
836 todo.push(c);
837 }
838 continue;
839 }
840 for c in &n.children {
841 todo.push(c);
842 }
843 }
844 }
845
846 fn is_root_of_instance(&self, id: &NodeId) -> bool {
847 let (prefix, _) = id.0.split_at(id.0.len() - 1);
848 !prefix.is_empty()
849 && self
850 .res
851 .record
852 .instances
853 .get(prefix)
854 .is_some_and(|inst| inst.root == *id)
855 }
856}
857
858fn overrides(r: &mut Resolver, caller: &Caller, root: &mut Node, parent: Option<Layout>) {
860 r.current = Some(root.id.clone());
861 for (attr, by) in &caller.overrides {
862 r.common(&caller.file, by, root, attr, attr.value.clone());
863 r.fill_needs_layout(&caller.file, attr, &attr.value, parent);
864 r.place_needs_no_layout(&caller.file, attr, parent);
865 }
866}
867
868fn root_id(inst: &Instance) -> u32 {
870 *inst.root.0.last().expect("an id")
871}
872
873fn node_id(i: &[u32], el: &Element) -> NodeId {
874 let mut id = i.to_vec();
875 id.push(el.id);
876 NodeId(id)
877}
878
879fn import_map(path: &str, file: &parse::File) -> HashMap<String, String> {
881 file.imports
882 .iter()
883 .filter_map(|i| Some((i.name.clone(), super::join(path, &i.path)?)))
884 .collect()
885}
886
887fn shallow(el: &Element) -> Element {
890 Element {
891 id: el.id,
892 name: el.name.clone(),
893 attrs: el.attrs.clone(),
894 children: el
895 .children
896 .iter()
897 .filter(|c| matches!(c, Child::Text(..)))
898 .cloned()
899 .collect(),
900 pos: el.pos,
901 }
902}
903
904fn index(root: &Element) -> HashMap<u32, Vec<usize>> {
906 let mut out = HashMap::new();
907 let mut todo = vec![(root, Vec::new())];
908 while let Some((el, at)) = todo.pop() {
909 for (i, c) in el.children.iter().enumerate() {
910 if let Child::Element(e) = c {
911 let mut here = at.clone();
912 here.push(i);
913 todo.push((e, here));
914 }
915 }
916 out.insert(el.id, at);
917 }
918 out
919}
920
921fn same_text(a: &Element, b: &Element) -> bool {
923 let texts = |e: &Element| {
924 e.children
925 .iter()
926 .filter_map(|c| match c {
927 Child::Text(parts, _) => Some(
928 parts
929 .iter()
930 .map(|p| match p {
931 TextPart::Literal(s) => (0, s.clone()),
932 TextPart::Prop(s) => (1, s.clone()),
933 TextPart::Theme(s, _) => (2, s.clone()),
934 })
935 .collect::<Vec<_>>(),
936 ),
937 Child::Element(_) => None,
938 })
939 .collect::<Vec<_>>()
940 };
941 texts(a) == texts(b)
942}
943
944fn moved_origin(a: &Element, b: &Element) -> bool {
947 let at = |e: &Element| {
948 e.attrs
949 .iter()
950 .filter(|x| x.name == "shadow" || x.name == "src")
951 .map(|x| (x.pos.line, x.pos.col))
952 .collect::<Vec<_>>()
953 };
954 at(a) != at(b)
955}
956
957fn children(el: &Element) -> Vec<&Element> {
958 el.children
959 .iter()
960 .filter_map(|c| match c {
961 Child::Element(e) => Some(e),
962 Child::Text(..) => None,
963 })
964 .collect()
965}
966
967#[allow(clippy::too_many_arguments)]
972fn walk<'p>(
973 old: &'p Element,
974 new: &'p Element,
975 root: bool,
976 is_instance: &dyn Fn(&Element) -> bool,
977 was_instance: &dyn Fn(&Element) -> bool,
978 retargeted: &HashSet<&str>,
979 out: &mut Vec<Act<'p>>,
980 dup: &mut bool,
981) {
982 let own_same = crate::ids::same_own(old, new) && same_text(old, new);
983 let instance = is_instance(new) || was_instance(old);
984 if !root
985 && (old.name != new.name
986 || is_instance(new) != was_instance(old)
987 || (instance && (!own_same || retargeted.contains(new.name.as_str()))))
988 {
989 out.push(Act::Whole(new, old));
990 return;
991 }
992 if !own_same || moved_origin(old, new) {
993 out.push(Act::Own(new, root));
994 }
995 let layout = |e: &Element| e.attrs.iter().find(|a| a.name == "layout").cloned();
996 let relayout = match (layout(old), layout(new)) {
997 (Some(a), Some(b)) => !crate::ids::same_value(&a.value, &b.value),
998 (None, None) => false,
999 _ => true,
1000 };
1001 let olds = children(old);
1002 let news = children(new);
1003 let mut keys = HashSet::new();
1005 for c in &news {
1006 if let Some(k) = crate::ids::key(c)
1007 && !keys.insert(k)
1008 {
1009 *dup = true;
1010 }
1011 }
1012 let rank: HashMap<u32, usize> = olds.iter().enumerate().map(|(i, e)| (e.id, i)).collect();
1013 let ranks: Vec<Option<usize>> = news.iter().map(|e| rank.get(&e.id).copied()).collect();
1014 let stay = longest_run(&ranks);
1015 let by_id: HashMap<u32, &Element> = olds.iter().map(|e| (e.id, *e)).collect();
1016 let mut prev: Option<&Element> = None;
1017 for (n, c) in news.iter().enumerate() {
1018 match by_id.get(&c.id) {
1019 Some(o) => {
1020 if !stay[n] {
1021 out.push(Act::Move {
1022 parent: new,
1023 after: prev,
1024 el: c,
1025 });
1026 }
1027 walk(o, c, false, is_instance, was_instance, retargeted, out, dup);
1028 if relayout {
1030 if is_instance(c) {
1031 out.push(Act::Whole(c, o));
1032 } else {
1033 out.push(Act::Own(c, false));
1034 }
1035 }
1036 }
1037 None => out.push(Act::Insert {
1038 parent: new,
1039 after: prev,
1040 el: c,
1041 }),
1042 }
1043 prev = Some(c);
1044 }
1045 let kept: HashSet<u32> = news.iter().map(|e| e.id).collect();
1046 for o in olds {
1047 if !kept.contains(&o.id) {
1048 out.push(Act::Remove(o));
1049 }
1050 }
1051}
1052
1053fn longest_run(ranks: &[Option<usize>]) -> Vec<bool> {
1056 let mut tails: Vec<usize> = Vec::new();
1057 let mut before: Vec<Option<usize>> = vec![None; ranks.len()];
1058 for (i, r) in ranks.iter().enumerate() {
1059 let Some(r) = r else { continue };
1060 let at = tails.partition_point(|&t| ranks[t].expect("ranked") < *r);
1061 before[i] = at.checked_sub(1).map(|a| tails[a]);
1062 if at == tails.len() {
1063 tails.push(i);
1064 } else {
1065 tails[at] = i;
1066 }
1067 }
1068 let mut stay = vec![false; ranks.len()];
1069 let mut at = tails.last().copied();
1070 while let Some(i) = at {
1071 stay[i] = true;
1072 at = before[i];
1073 }
1074 stay
1075}
1076
1077#[cfg(test)]
1078mod tests;